fulmine.js 5.19.2 → 5.19.3
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 +2 -1
- package/src/adopt.js +20 -26
- package/src/application.js +46 -45
- package/src/cli.js +28 -34
- package/src/cluster.js +16 -25
- package/src/compression.js +39 -51
- package/src/declarative.js +586 -538
- package/src/hot-settings.js +80 -0
- package/src/index.js +11 -17
- package/src/lazy-readable.js +129 -0
- package/src/lazy-writable.js +97 -0
- package/src/middlewares.js +61 -77
- package/src/nest.js +19 -34
- package/src/node-shim.js +11 -13
- package/src/optimizer.js +598 -0
- package/src/parse-query.js +3 -3
- package/src/request-utils.js +306 -0
- package/src/request.js +101 -513
- package/src/response-utils.js +88 -0
- package/src/response.js +100 -443
- package/src/route.js +4 -5
- package/src/router-utils.js +950 -0
- package/src/router.js +126 -2148
- package/src/server-shape.js +26 -41
- package/src/server-timing.js +16 -29
- package/src/socket.js +208 -0
- package/src/testing.js +39 -42
- package/src/usage.js +16 -21
- package/src/utils.js +49 -59
- package/src/verify.js +18 -28
- package/src/view.js +5 -7
- package/src/walk.js +580 -0
- package/src/websocket.js +19 -20
- package/src/work.js +21 -27
package/src/usage.js
CHANGED
|
@@ -23,10 +23,9 @@ const acorn = require("acorn");
|
|
|
23
23
|
// GET alone (and a GET that declares a body falls back to the full header copy).
|
|
24
24
|
const kGetSafe = Symbol("fulmine.getSafe");
|
|
25
25
|
|
|
26
|
-
// What a handler may do with `req` and still let the header copy be skipped: members whose
|
|
27
|
-
//
|
|
28
|
-
// req.
|
|
29
|
-
// the parameter, so anything that can reach another object could reach headers through it.
|
|
26
|
+
// What a handler may do with `req` and still let the header copy be skipped: members whose reads
|
|
27
|
+
// never reach a header. Anything else, computed access included, keeps the copy. req.res and
|
|
28
|
+
// req.app are absent because they could reach headers through another object.
|
|
30
29
|
const REQ_OK = new Set(["query", "params", "body", "method", "path", "url", "baseUrl", "originalUrl", "route"]);
|
|
31
30
|
|
|
32
31
|
// Reading any of these needs the query string fetched: req.url and req.originalUrl carry it
|
|
@@ -70,9 +69,8 @@ const QUERY = 8; // reads req.query, req.url or req.originalUrl
|
|
|
70
69
|
const verdicts = new WeakMap();
|
|
71
70
|
|
|
72
71
|
/**
|
|
73
|
-
* What one callback provably does, as a mask of the facts above. The default is UNKNOWN: any
|
|
74
|
-
*
|
|
75
|
-
* That inversion is what makes source analysis sound to act on.
|
|
72
|
+
* What one callback provably does, as a mask of the facts above. The default is UNKNOWN: any shape
|
|
73
|
+
* this walk does not understand, and any alias of req, res or next, could do anything.
|
|
76
74
|
*
|
|
77
75
|
* @param {Function} fn
|
|
78
76
|
* @returns {number}
|
|
@@ -135,9 +133,8 @@ function analyze(fn) {
|
|
|
135
133
|
const resName = params[1] ? params[1].name : null;
|
|
136
134
|
const nextName = params[2] ? params[2].name : null;
|
|
137
135
|
|
|
138
|
-
// Every appearance of the three names
|
|
139
|
-
//
|
|
140
|
-
// looser, so scope tracking is not needed for soundness.
|
|
136
|
+
// Every appearance of the three names is judged, nested functions included. An inner binding
|
|
137
|
+
// that shadows one only makes this stricter, so no scope tracking is needed.
|
|
141
138
|
let mask = 0;
|
|
142
139
|
walk(root.body, null, (node, parent) => {
|
|
143
140
|
if (mask & UNKNOWN) {
|
|
@@ -192,11 +189,10 @@ function analyze(fn) {
|
|
|
192
189
|
return;
|
|
193
190
|
}
|
|
194
191
|
if (name === nextName) {
|
|
195
|
-
// calling next is how a chain advances, and past its end
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
// routes nobody analyzed, so only shapes that cannot be a string pass.
|
|
192
|
+
// calling next is how a chain advances, and past its end the request lands in the
|
|
193
|
+
// framework's own final answer, which the constructor's accept pre-read covers.
|
|
194
|
+
// Anything but a direct call aliases the continuation, and an argument that could be
|
|
195
|
+
// the string "route" would leave the chain, so only non-string shapes pass.
|
|
200
196
|
if (!parent || parent.type !== "CallExpression" || parent.callee !== node) {
|
|
201
197
|
mask |= UNKNOWN;
|
|
202
198
|
return;
|
|
@@ -235,8 +231,8 @@ function analyze(fn) {
|
|
|
235
231
|
* Walks every node, handing each its parent. Arrays and nested objects are entered, nothing
|
|
236
232
|
* is interpreted: the judging happens in the visitor.
|
|
237
233
|
*
|
|
238
|
-
* @param {any} node
|
|
239
|
-
* @param {any} parent
|
|
234
|
+
* @param {any} node an acorn AST node. acorn ships no useful node types, and every shape here is checked by hand
|
|
235
|
+
* @param {any} parent its parent node, or null at the root
|
|
240
236
|
* @param {(node: any, parent: any) => void} visit
|
|
241
237
|
*/
|
|
242
238
|
function walk(node, parent, visit) {
|
|
@@ -265,10 +261,9 @@ function walk(node, parent, visit) {
|
|
|
265
261
|
* What a native route's whole chain provably never does, so the request constructor may leave
|
|
266
262
|
* that work undone: skipHeaders spares the header copy, skipQuery the query fetch.
|
|
267
263
|
*
|
|
268
|
-
* A
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* fall-through itself needs neither headers nor query.
|
|
264
|
+
* A bare next() passes anywhere but in the terminal route, where it falls out of the chain: there
|
|
265
|
+
* it passes only when no later route could catch the fall-through. The framework's own 404 answers
|
|
266
|
+
* from the path alone.
|
|
272
267
|
*
|
|
273
268
|
* @param {any[]} chain the routes the native handler runs, in order, this route last
|
|
274
269
|
* @param {boolean} allowTerminalNext whether a fall-through past the chain lands only in the
|
package/src/utils.js
CHANGED
|
@@ -28,6 +28,9 @@ const ms = require("ms");
|
|
|
28
28
|
const fs = require("fs");
|
|
29
29
|
const { Stats } = require("fs");
|
|
30
30
|
|
|
31
|
+
/** @typedef {import("./request.js")} Request */
|
|
32
|
+
/** @typedef {import("./response.js")} Response */
|
|
33
|
+
|
|
31
34
|
const EMPTY_REGEX = new RegExp(``);
|
|
32
35
|
|
|
33
36
|
// what express hands qs for a query string. allowPrototypes keeps a key named "constructor" or
|
|
@@ -231,11 +234,9 @@ function patternToRegex(pattern, isPrefix = false, caseSensitive = true, strict
|
|
|
231
234
|
let lastWildcardEnd = -1;
|
|
232
235
|
// What path-to-regexp calls the wildcard backtrack: the literal text written since the last
|
|
233
236
|
// wildcard. Once a wildcard has eaten slashes, a later one in the same path is held to a single
|
|
234
|
-
// segment, or the two would divide the path
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
// the text written since the last capture of any kind, and the text written since the last
|
|
238
|
-
// wildcard, which are the two path-to-regexp weighs
|
|
237
|
+
// segment, or the two would divide the path in more than one way and the regex would have to
|
|
238
|
+
// backtrack. /*a/*b against /x/y/ shows it: express refuses it under strict routing.
|
|
239
|
+
// Two counters: text since the last capture of any kind, and text since the last wildcard
|
|
239
240
|
let backtrack = "";
|
|
240
241
|
let wildcardBacktrack = "";
|
|
241
242
|
let lastCaptureWasWildcard = false;
|
|
@@ -405,16 +406,13 @@ function patternToRegex(pattern, isPrefix = false, caseSensitive = true, strict
|
|
|
405
406
|
i++;
|
|
406
407
|
|
|
407
408
|
// When a :parameter precedes this group, that parameter is the one that gives ground
|
|
408
|
-
// while backtracking, so this one must not swallow the separator
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
// The whole
|
|
413
|
-
// foo=123 and bar=abc on express, and reading
|
|
414
|
-
// match its own text
|
|
415
|
-
// than one character cannot go in a class, so it is written as a lookahead, and either
|
|
416
|
-
// way the parameter is still allowed to be exactly the separator, as path-to-regexp
|
|
417
|
-
// writes it.
|
|
409
|
+
// while backtracking, so this one must not swallow the separator too. Express splits
|
|
410
|
+
// /a.b.c against /:file{.:ext} as file=a.b, ext=c, which only works if ext cannot
|
|
411
|
+
// contain a dot. After static text nothing gives ground, so the parameter takes
|
|
412
|
+
// everything: /file{.:ext} against /file.tar.gz gives ext=tar.gz.
|
|
413
|
+
// The whole separator, not its first character: /:foo{abc:bar} against /123abcabc
|
|
414
|
+
// splits as foo=123 and bar=abc on express, and reading it as "a" left bar unable to
|
|
415
|
+
// match its own text. More than one character cannot go in a class, so it is a lookahead
|
|
418
416
|
const colon = groupContent.indexOf(":");
|
|
419
417
|
const separator = lastTokenWasParam && colon > 0 ? groupContent.slice(0, colon) : "";
|
|
420
418
|
const escapedSeparator = separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -445,13 +443,11 @@ function patternToRegex(pattern, isPrefix = false, caseSensitive = true, strict
|
|
|
445
443
|
}
|
|
446
444
|
}
|
|
447
445
|
if (lastWildcard && lastWildcardEnd === regexPattern.length) {
|
|
448
|
-
// A wildcard immediately before the group. `(?<w>[^]+)(?:group)?` can never let
|
|
449
|
-
//
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
// wildcard greedy in both, so that is what goes here. The second branch captures
|
|
454
|
-
// the same parameter under a name of its own, which is what uniqueGroupName is for.
|
|
446
|
+
// A wildcard immediately before the group. `(?<w>[^]+)(?:group)?` can never let the
|
|
447
|
+
// group match, because the wildcard is greedy and the group may be empty, and a
|
|
448
|
+
// lazy wildcard is not the same thing either: it gives the trailing slash away, and
|
|
449
|
+
// /*path{.:ext} against /a/b/ loses the empty last segment. path-to-regexp writes
|
|
450
|
+
// the two branches out instead, group first and the wildcard greedy in both
|
|
455
451
|
const second = uniqueGroupName(lastWildcard.name);
|
|
456
452
|
wildcardNames.push(second);
|
|
457
453
|
const withWildcard = regexPattern.slice(lastWildcard.start);
|
|
@@ -619,13 +615,13 @@ const NOT_A_LITERAL = /[:*{}\\]/;
|
|
|
619
615
|
*
|
|
620
616
|
* The answer is structural: no position where two different literals meet, and, when neither path
|
|
621
617
|
* can change length, the same number of segments. `/orders/:id` and `/invoices/:id` cannot both
|
|
622
|
-
* match, `/users/:id` and `/users/me` can. The caller reads "do not know" as yes
|
|
623
|
-
*
|
|
624
|
-
*
|
|
618
|
+
* match, `/users/:id` and `/users/me` can. The caller reads "do not know" as yes: saying two paths
|
|
619
|
+
* overlap only costs a native registration, while missing one lets uWS answer a request that
|
|
620
|
+
* belonged to an earlier route.
|
|
625
621
|
*
|
|
626
622
|
* A parameter is not the only shape that matches more than itself. A wildcard and an optional group
|
|
627
|
-
* do too, and reading `{:opt}` or `*splat` as
|
|
628
|
-
*
|
|
623
|
+
* do too, and reading `{:opt}` or `*splat` as literal text reported "cannot overlap" for a route
|
|
624
|
+
* that plainly could.
|
|
629
625
|
*
|
|
630
626
|
* @param {string} a
|
|
631
627
|
* @param {string} b
|
|
@@ -759,11 +755,8 @@ function acceptParams(str) {
|
|
|
759
755
|
//
|
|
760
756
|
// The keys are media types, so an application uses a handful and the ceiling is never approached.
|
|
761
757
|
// It is here because application code is free to hand res.type() something a client sent, and an
|
|
762
|
-
// unbounded map keyed on that is a leak the client controls.
|
|
763
|
-
//
|
|
764
|
-
// Clearing beats evicting one entry at a time: the few types an application really uses are back
|
|
765
|
-
// within a few requests, whereas refusing new entries once full would let a flood of invented
|
|
766
|
-
// values lock the real ones out for the life of the process.
|
|
758
|
+
// unbounded map keyed on that is a leak the client controls. Clearing beats evicting one at a
|
|
759
|
+
// time: refusing new entries once full would let a flood of invented values lock the real ones out.
|
|
767
760
|
const MEMO_LIMIT = 512;
|
|
768
761
|
|
|
769
762
|
/**
|
|
@@ -818,7 +811,7 @@ function normalizeType(type) {
|
|
|
818
811
|
* unicode escapes, so a string in the body cannot close a script tag in an HTML page that embeds
|
|
819
812
|
* the response.
|
|
820
813
|
*
|
|
821
|
-
* @param {any} value
|
|
814
|
+
* @param {any} value whatever the handler passed to res.json
|
|
822
815
|
* @param {any} [replacer] the "json replacer" setting
|
|
823
816
|
* @param {string|number} [spaces] the "json spaces" setting
|
|
824
817
|
* @param {boolean} [escape] the "json escape" setting
|
|
@@ -855,14 +848,12 @@ const ENCODING_ANY = ENCODING_BR | ENCODING_GZIP | ENCODING_DEFLATE | ENCODING_Z
|
|
|
855
848
|
/**
|
|
856
849
|
* The encoding to answer with, read straight off Accept-Encoding rather than through negotiator:
|
|
857
850
|
* the header is a short list of names with an optional q, and building a Negotiator per response
|
|
858
|
-
*
|
|
859
|
-
*
|
|
860
|
-
* The tie-break is negotiator's, for the list the compression module hands it: brotli first, then
|
|
861
|
-
* gzip, then deflate, and identity last.
|
|
851
|
+
* costs more than the scan does. The tie-break is negotiator's, for the list the compression module
|
|
852
|
+
* hands it: brotli first, then gzip, then deflate, and identity last.
|
|
862
853
|
*
|
|
863
|
-
* Only the encodings named in `allowed` are on offer, since the caller may not
|
|
864
|
-
*
|
|
865
|
-
*
|
|
854
|
+
* Only the encodings named in `allowed` are on offer, since the caller may not produce all three:
|
|
855
|
+
* express.static offers the two it can have lying on disk. An uncompressed answer is always on
|
|
856
|
+
* offer, and is what an empty header chooses.
|
|
866
857
|
*
|
|
867
858
|
* @param {string} accept the header, or "" when the request carried none
|
|
868
859
|
* @param {number} allowed ENCODING_BR, ENCODING_ZSTD, ENCODING_GZIP and ENCODING_DEFLATE, or'd
|
|
@@ -989,12 +980,10 @@ const defaultSettings = {
|
|
|
989
980
|
// The native µWS router matches bytes, so the compiler in _compileOptimizedRoutes only hands
|
|
990
981
|
// it routes whose earlier siblings it can prove agree under either case rule.
|
|
991
982
|
"declarative responses": true,
|
|
992
|
-
// on. Off hands every request to the ordinary chain instead of letting
|
|
983
|
+
// on. Off hands every request to the ordinary chain instead of letting uWS match what it can,
|
|
993
984
|
// which is slower and answers the same. Not a tuning knob: it exists so one application can be
|
|
994
|
-
// served both ways and the
|
|
995
|
-
//
|
|
996
|
-
// `npm run fuzz -- --self`. A compiled response needs a native registration to hang on, so this
|
|
997
|
-
// takes "declarative responses" with it.
|
|
985
|
+
// served both ways and the answers compared, see `npm run fuzz -- --self`. A compiled response
|
|
986
|
+
// needs a native registration to hang on, so this takes "declarative responses" with it
|
|
998
987
|
"native routes": true,
|
|
999
988
|
// off: with a window set, the size and mtime of a file served by sendFile are remembered for
|
|
1000
989
|
// it, which is one syscall less per request and a file that can be served as it was a moment
|
|
@@ -1053,7 +1042,7 @@ function cachedStat(file, ttl) {
|
|
|
1053
1042
|
/**
|
|
1054
1043
|
* A duration setting as milliseconds: false is off, a string is read by ms, a number is itself.
|
|
1055
1044
|
*
|
|
1056
|
-
* @param {any} value
|
|
1045
|
+
* @param {any} value the setting as the application wrote it
|
|
1057
1046
|
* @param {string} name for the error, which names the setting the application wrote
|
|
1058
1047
|
* @returns {number}
|
|
1059
1048
|
*/
|
|
@@ -1274,8 +1263,8 @@ function parseHttpDate(date) {
|
|
|
1274
1263
|
* no longer the current one, which is a 412 rather than a 304: the client asked to be stopped if
|
|
1275
1264
|
* anything had changed.
|
|
1276
1265
|
*
|
|
1277
|
-
* @param {
|
|
1278
|
-
* @param {
|
|
1266
|
+
* @param {Request} req
|
|
1267
|
+
* @param {Response} res
|
|
1279
1268
|
* @returns {boolean}
|
|
1280
1269
|
*/
|
|
1281
1270
|
function isPreconditionFailure(req, res) {
|
|
@@ -1296,7 +1285,8 @@ function isPreconditionFailure(req, res) {
|
|
|
1296
1285
|
// if-unmodified-since
|
|
1297
1286
|
const unmodifiedSince = parseHttpDate(req.headers["if-unmodified-since"]);
|
|
1298
1287
|
if (!isNaN(unmodifiedSince)) {
|
|
1299
|
-
|
|
1288
|
+
// cast because res.get answers an array for set-cookie, and never for this one
|
|
1289
|
+
const lastModified = parseHttpDate(/** @type {string|undefined} */ (res.get("Last-Modified")));
|
|
1300
1290
|
return isNaN(lastModified) || lastModified > unmodifiedSince;
|
|
1301
1291
|
}
|
|
1302
1292
|
|
|
@@ -1367,8 +1357,8 @@ function createETagGenerator(options) {
|
|
|
1367
1357
|
* and sending the whole file. It may carry either an ETag or a date, and a date only counts when
|
|
1368
1358
|
* it matches Last-Modified exactly.
|
|
1369
1359
|
*
|
|
1370
|
-
* @param {
|
|
1371
|
-
* @param {
|
|
1360
|
+
* @param {Request} req
|
|
1361
|
+
* @param {Response} res
|
|
1372
1362
|
* @returns {boolean}
|
|
1373
1363
|
*/
|
|
1374
1364
|
function isRangeFresh(req, res) {
|
|
@@ -1384,7 +1374,8 @@ function isRangeFresh(req, res) {
|
|
|
1384
1374
|
}
|
|
1385
1375
|
|
|
1386
1376
|
// if-range as modified date
|
|
1387
|
-
|
|
1377
|
+
// cast because res.get answers an array for set-cookie, and never for this one
|
|
1378
|
+
const lastModified = /** @type {string|undefined} */ (res.get("Last-Modified"));
|
|
1388
1379
|
return parseHttpDate(lastModified) <= parseHttpDate(ifRange);
|
|
1389
1380
|
}
|
|
1390
1381
|
|
|
@@ -1489,10 +1480,9 @@ const HEADER_VALUE = /[^\t\x20-\x7e\x80-\xff]/;
|
|
|
1489
1480
|
* One of node's header errors, built the way node builds it.
|
|
1490
1481
|
*
|
|
1491
1482
|
* Assigning the code is not the whole of it. Node also puts the code in the first line of the
|
|
1492
|
-
* stack, by naming the error "TypeError [THE_CODE]" while V8 formats that line and then taking
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
* "TypeError [ERR_INVALID_CHAR]:" behind Express. Found by fuzzing against express.
|
|
1483
|
+
* stack, by naming the error "TypeError [THE_CODE]" while V8 formats that line and then taking the
|
|
1484
|
+
* name back off. The default error page prints exactly that: without this, the same refusal reads
|
|
1485
|
+
* "TypeError:" here and "TypeError [ERR_INVALID_CHAR]:" behind Express. Found by fuzzing.
|
|
1496
1486
|
*
|
|
1497
1487
|
* @param {string} message
|
|
1498
1488
|
* @param {string} code
|
|
@@ -1515,7 +1505,7 @@ function headerError(message, code) {
|
|
|
1515
1505
|
* Refuses a header name that is not an HTTP token, the way node's setHeader does and with its
|
|
1516
1506
|
* error, so an application catching ERR_INVALID_HTTP_TOKEN behind Express catches it here.
|
|
1517
1507
|
*
|
|
1518
|
-
* @param {any} name
|
|
1508
|
+
* @param {any} name whatever a caller passed as a header name, which is what is being checked
|
|
1519
1509
|
* @returns {void}
|
|
1520
1510
|
* @throws {TypeError} if the name is not a token, which includes not being a string
|
|
1521
1511
|
*/
|
|
@@ -1567,7 +1557,7 @@ function validateHeaderValue(name, value) {
|
|
|
1567
1557
|
* again is what turns one bad header into a dead process.
|
|
1568
1558
|
*
|
|
1569
1559
|
* @param {string} name
|
|
1570
|
-
* @param {any} value
|
|
1560
|
+
* @param {any} value whatever a caller passed as a header value
|
|
1571
1561
|
* @returns {boolean}
|
|
1572
1562
|
*/
|
|
1573
1563
|
function headerIsWritable(name, value) {
|
|
@@ -1612,7 +1602,7 @@ function httpError(status) {
|
|
|
1612
1602
|
*
|
|
1613
1603
|
* The error itself is returned rather than a new one, so its errno, code, syscall and path survive.
|
|
1614
1604
|
*
|
|
1615
|
-
* @param {any} err
|
|
1605
|
+
* @param {any} err the fs error, which carries its errno and path
|
|
1616
1606
|
* @returns {any} the same error
|
|
1617
1607
|
*/
|
|
1618
1608
|
function asStatError(err) {
|
package/src/verify.js
CHANGED
|
@@ -16,16 +16,12 @@ limitations under the License.
|
|
|
16
16
|
|
|
17
17
|
// npx fulmine verify
|
|
18
18
|
//
|
|
19
|
-
// Whether this machine, and the image it will be deployed in, can run the thing at all. Not
|
|
20
|
-
//
|
|
21
|
-
// for: this is the question that comes before it, and it is the one that costs an hour when the
|
|
22
|
-
// answer is no and nobody asked.
|
|
19
|
+
// Whether this machine, and the image it will be deployed in, can run the thing at all. Not whether
|
|
20
|
+
// the application behaves the same, which is the test suite's job.
|
|
23
21
|
//
|
|
24
|
-
// There is a
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// Dockerfile written before any of this: each one fails at require time, in a container, in CI,
|
|
28
|
-
// with a message about a missing module that says nothing about what to do.
|
|
22
|
+
// There is a uWebSockets.js binary underneath, built per platform, per architecture and per node
|
|
23
|
+
// ABI, and linked against glibc. An Alpine image, a node version with no binary for it, a musl
|
|
24
|
+
// base: each one fails at require time with a message about a missing module.
|
|
29
25
|
//
|
|
30
26
|
// Thirty seconds here instead.
|
|
31
27
|
|
|
@@ -34,7 +30,7 @@ limitations under the License.
|
|
|
34
30
|
const fs = require("fs");
|
|
35
31
|
const path = require("path");
|
|
36
32
|
|
|
37
|
-
// The oldest glibc the pinned
|
|
33
|
+
// The oldest glibc the pinned uWS binaries are built against. A runtime older than this loads the
|
|
38
34
|
// file and then fails on a symbol, which is a worse error than not finding it at all.
|
|
39
35
|
const MIN_GLIBC = "2.38";
|
|
40
36
|
|
|
@@ -44,8 +40,8 @@ const MIN_NODE = require("../package.json").engines.node.replace(/[^0-9.]/g, "")
|
|
|
44
40
|
const MIN_NODE_MAJOR = MIN_NODE.split(".")[0];
|
|
45
41
|
const SWAP_IMAGE = `node:${MIN_NODE_MAJOR}-trixie-slim`;
|
|
46
42
|
|
|
47
|
-
// What a project may carry that needs a different API here
|
|
48
|
-
//
|
|
43
|
+
// What a project may carry that needs a different API here. Everything that just works is
|
|
44
|
+
// `npx fulmine migrate`'s business.
|
|
49
45
|
const NEEDS_A_LOOK = {
|
|
50
46
|
"socket.io": "attach it with io.attachApp(app.uwsApp), not io.attach(server): there is no node socket to take over",
|
|
51
47
|
ws: "the websocket server is µWS's own, through app.ws(path, behavior)",
|
|
@@ -55,9 +51,8 @@ const NEEDS_A_LOOK = {
|
|
|
55
51
|
};
|
|
56
52
|
|
|
57
53
|
/**
|
|
58
|
-
* One line of the report.
|
|
59
|
-
*
|
|
60
|
-
* something to read, not something to fail a pipeline over.
|
|
54
|
+
* One line of the report. Only "no" is a failure: an image that cannot load the binary stops the
|
|
55
|
+
* deployment, a dependency that wants a different call does not.
|
|
61
56
|
*
|
|
62
57
|
* @param {"ok"|"note"|"no"} level
|
|
63
58
|
* @param {string} what
|
|
@@ -92,9 +87,8 @@ function atLeast(version, minimum) {
|
|
|
92
87
|
/**
|
|
93
88
|
* The node this is running on, against what the package asks for.
|
|
94
89
|
*
|
|
95
|
-
* The version
|
|
96
|
-
*
|
|
97
|
-
* takes what it judges for the same reason.
|
|
90
|
+
* The version is an argument rather than read here, so a node this machine is not running is still
|
|
91
|
+
* testable. Every check below takes what it judges for the same reason.
|
|
98
92
|
*
|
|
99
93
|
* @param {string} [running] defaults to the node running this
|
|
100
94
|
* @param {string} [required] defaults to what package.json asks for
|
|
@@ -121,9 +115,8 @@ function currentGlibc() {
|
|
|
121
115
|
/**
|
|
122
116
|
* Whether the C library is the one the binaries are linked against. Only linux has two of them.
|
|
123
117
|
*
|
|
124
|
-
* Both arguments are required
|
|
125
|
-
*
|
|
126
|
-
* case into whatever this machine happens to run. Reading the machine is the caller's job.
|
|
118
|
+
* Both arguments are required on purpose: undefined means musl, and a default parameter fires on
|
|
119
|
+
* an explicit undefined, so a default would turn the musl case into whatever this machine runs.
|
|
127
120
|
*
|
|
128
121
|
* @param {string} platform
|
|
129
122
|
* @param {string|undefined} glibc the runtime glibc, absent on musl
|
|
@@ -152,9 +145,8 @@ function checkLibc(platform, glibc) {
|
|
|
152
145
|
}
|
|
153
146
|
|
|
154
147
|
/**
|
|
155
|
-
* Whether there is a
|
|
156
|
-
*
|
|
157
|
-
* loaded first, so the answer says which of the three does not line up.
|
|
148
|
+
* Whether there is a uWebSockets.js binary for this platform, architecture and node ABI. The file
|
|
149
|
+
* is named rather than loaded, so the answer says which of the three does not line up.
|
|
158
150
|
*
|
|
159
151
|
* @param {string} [platform]
|
|
160
152
|
* @param {string} [arch]
|
|
@@ -218,8 +210,7 @@ function abiToNode(abi) {
|
|
|
218
210
|
}
|
|
219
211
|
|
|
220
212
|
/**
|
|
221
|
-
* The base images a Dockerfile names, which is where the musl question is usually answered
|
|
222
|
-
* anybody meaning to.
|
|
213
|
+
* The base images a Dockerfile names, which is where the musl question is usually answered.
|
|
223
214
|
*
|
|
224
215
|
* @param {string} dir the project being verified
|
|
225
216
|
* @returns {ReturnType<typeof result>[]}
|
|
@@ -309,8 +300,7 @@ function verify(argv) {
|
|
|
309
300
|
console.log(` ${detail}`);
|
|
310
301
|
}
|
|
311
302
|
}
|
|
312
|
-
// only a blocked start is a failure
|
|
313
|
-
// reading and is not worth failing a pipeline over
|
|
303
|
+
// only a blocked start is a failure, a dependency that wants a different call is not
|
|
314
304
|
const blocking = results.filter((entry) => entry.level === "no").length;
|
|
315
305
|
const notes = results.filter((entry) => entry.level === "note").length;
|
|
316
306
|
console.log(
|
package/src/view.js
CHANGED
|
@@ -73,9 +73,8 @@ module.exports = class View {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* The first
|
|
77
|
-
*
|
|
78
|
-
* so an application can put its own templates in front of a package's.
|
|
76
|
+
* The first configured root that holds this template, or undefined. `views` may be one
|
|
77
|
+
* directory or a list, and the list is searched in order.
|
|
79
78
|
*
|
|
80
79
|
* @param {string} name template file name, relative to a root
|
|
81
80
|
* @returns {string|undefined} absolute path to the file that exists
|
|
@@ -100,10 +99,9 @@ module.exports = class View {
|
|
|
100
99
|
/**
|
|
101
100
|
* Renders the template through its engine.
|
|
102
101
|
*
|
|
103
|
-
* The callback is always delivered asynchronously, even when the engine answers on the spot
|
|
104
|
-
* `sync` is
|
|
105
|
-
*
|
|
106
|
-
* orders. Express normalises it the same way.
|
|
102
|
+
* The callback is always delivered asynchronously, even when the engine answers on the spot:
|
|
103
|
+
* `sync` is true only if the engine called back before this returned, and then the callback
|
|
104
|
+
* goes to the next tick. Express does the same.
|
|
107
105
|
*
|
|
108
106
|
* @param {Record<string, any>} options locals and engine options, passed through untouched
|
|
109
107
|
* @param {Function} callback called with whatever the engine passed, which is normally
|