@blamejs/core 0.7.64 → 0.7.73

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.
@@ -27,14 +27,17 @@ var cors = require("./cors");
27
27
  var cspNonce = require("./csp-nonce");
28
28
  var csrfProtect = require("./csrf-protect");
29
29
  var dbRoleFor = require("./db-role-for");
30
+ var dpop = require("./dpop");
30
31
  var errorHandler = require("./error-handler");
31
32
  var fetchMetadata = require("./fetch-metadata");
33
+ var gpc = require("./gpc");
32
34
  var headers = require("./headers");
33
35
  var health = require("./health");
34
36
  var networkAllowlist = require("./network-allowlist");
35
37
  var rateLimit = require("./rate-limit");
36
38
  var requestId = require("./request-id");
37
39
  var requestLog = require("./request-log");
40
+ var requireAal = require("./require-aal");
38
41
  var requireAuth = require("./require-auth");
39
42
  var securityHeaders = require("./security-headers");
40
43
  var sse = require("./sse");
@@ -48,9 +51,11 @@ module.exports = {
48
51
  rateLimit: rateLimit.create,
49
52
  attachUser: attachUser.create,
50
53
  bearerAuth: bearerAuth.create,
54
+ requireAal: requireAal.create,
51
55
  requireAuth: requireAuth.create,
52
56
  csrfProtect: csrfProtect.create,
53
57
  fetchMetadata: fetchMetadata.create,
58
+ gpc: gpc.create,
54
59
  headers: headers.create,
55
60
  bodyParser: bodyParser.create,
56
61
  health: health.create,
@@ -61,6 +66,7 @@ module.exports = {
61
66
  requestLog: requestLog.create,
62
67
  apiEncrypt: apiEncrypt,
63
68
  dbRoleFor: dbRoleFor.create,
69
+ dpop: dpop.create,
64
70
  networkAllowlist: networkAllowlist.create,
65
71
 
66
72
  // Module exports for advanced use (constants, raw factory access)
@@ -73,6 +79,7 @@ module.exports = {
73
79
  rateLimit: rateLimit,
74
80
  attachUser: attachUser,
75
81
  bearerAuth: bearerAuth,
82
+ requireAal: requireAal,
76
83
  requireAuth: requireAuth,
77
84
  csrfProtect: csrfProtect,
78
85
  fetchMetadata: fetchMetadata,
@@ -84,6 +91,7 @@ module.exports = {
84
91
  requestLog: requestLog,
85
92
  apiEncrypt: apiEncrypt,
86
93
  dbRoleFor: dbRoleFor,
94
+ dpop: dpop,
87
95
  networkAllowlist: networkAllowlist,
88
96
  },
89
97
  };
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * require-aal middleware — gate routes by NIST SP 800-63-4 AAL band.
4
+ *
5
+ * var stepUp = b.middleware.requireAal({ minimum: "AAL2" });
6
+ * router.use("/admin", stepUp);
7
+ *
8
+ * Reads the AAL band from `req.user.aal` by default. Operators with a
9
+ * different shape pass `getAal(req)` returning the band string.
10
+ *
11
+ * On failure the middleware writes 401 with
12
+ * `WWW-Authenticate: AAL-StepUp realm="<X>", required="<minimum>"`
13
+ * — the bespoke scheme name signals to the operator's frontend that a
14
+ * step-up flow should be triggered (re-prompt for TOTP / passkey).
15
+ *
16
+ * Audit:
17
+ * auth.aal.granted — request passed (carries the actual band)
18
+ * auth.aal.denied — request below the required minimum
19
+ */
20
+
21
+ var lazyRequire = require("../lazy-require");
22
+ var requestHelpers = require("../request-helpers");
23
+ var validateOpts = require("../validate-opts");
24
+ var { AuthError } = require("../framework-error");
25
+
26
+ var aal = lazyRequire(function () { return require("../auth/aal"); });
27
+ var audit = lazyRequire(function () { return require("../audit"); });
28
+
29
+ function _writeUnauthorized(res, requiredBand, actualBand, realm) {
30
+ if (res.headersSent) return;
31
+ var body = JSON.stringify({
32
+ error: "step_up_required",
33
+ error_description: "AAL " + requiredBand + " is required for this resource",
34
+ required_aal: requiredBand,
35
+ actual_aal: actualBand || null,
36
+ });
37
+ var realmStr = realm ? ' realm="' + realm + '"' : "";
38
+ var challenge = "AAL-StepUp" + realmStr + ', required="' + requiredBand + '"';
39
+ res.writeHead(401, { // allow:raw-byte-literal — HTTP 401 status
40
+ "Content-Type": "application/json; charset=utf-8",
41
+ "Content-Length": Buffer.byteLength(body),
42
+ "WWW-Authenticate": challenge,
43
+ });
44
+ res.end(body);
45
+ }
46
+
47
+ function create(opts) {
48
+ opts = opts || {};
49
+ validateOpts(opts, [
50
+ "minimum", "getAal", "audit", "realm",
51
+ ], "middleware.requireAal");
52
+
53
+ var minimum = opts.minimum;
54
+ if (!aal().isValidBand(minimum)) {
55
+ throw new AuthError("auth-aal/bad-minimum",
56
+ "middleware.requireAal: opts.minimum must be one of " +
57
+ aal().BANDS.join(", ") + " (got " + JSON.stringify(minimum) + ")");
58
+ }
59
+ validateOpts.optionalFunction(opts.getAal,
60
+ "middleware.requireAal: getAal", AuthError, "auth-aal/bad-opt");
61
+
62
+ var auditOn = opts.audit !== false;
63
+ var realm = (typeof opts.realm === "string" && opts.realm.length > 0) ? opts.realm : null;
64
+
65
+ return function requireAalMiddleware(req, res, next) {
66
+ var actual = null;
67
+ if (typeof opts.getAal === "function") {
68
+ try { actual = opts.getAal(req); } catch (_e) { actual = null; }
69
+ } else if (req.user && typeof req.user.aal === "string") {
70
+ actual = req.user.aal;
71
+ }
72
+
73
+ if (!aal().meets(actual, minimum)) {
74
+ if (auditOn) {
75
+ try {
76
+ audit().safeEmit({
77
+ action: "auth.aal.denied",
78
+ actor: { clientIp: requestHelpers.clientIp(req), userId: req.user && req.user.id },
79
+ outcome: "fail",
80
+ metadata: {
81
+ required: minimum,
82
+ actual: actual || null,
83
+ route: req.url,
84
+ },
85
+ });
86
+ } catch (_ignored) { /* drop-silent */ }
87
+ }
88
+ return _writeUnauthorized(res, minimum, actual, realm);
89
+ }
90
+
91
+ if (auditOn) {
92
+ try {
93
+ audit().safeEmit({
94
+ action: "auth.aal.granted",
95
+ actor: { clientIp: requestHelpers.clientIp(req), userId: req.user && req.user.id },
96
+ outcome: "ok",
97
+ metadata: { aal: actual, required: minimum, route: req.url },
98
+ });
99
+ } catch (_ignored) { /* drop-silent */ }
100
+ }
101
+ return next();
102
+ };
103
+ }
104
+
105
+ module.exports = {
106
+ create: create,
107
+ };
@@ -75,6 +75,7 @@ function create(opts) {
75
75
  "hsts", "contentTypeOptions", "frameOptions", "referrerPolicy",
76
76
  "permissionsPolicy", "coop", "coep", "corp",
77
77
  "originAgentCluster", "dnsPrefetchControl", "csp", "trustProxy",
78
+ "reportingEndpoints",
78
79
  ], "middleware.securityHeaders");
79
80
  var trustProxy = opts.trustProxy === true || typeof opts.trustProxy === "number"
80
81
  ? opts.trustProxy : false;
@@ -89,6 +90,32 @@ function create(opts) {
89
90
  var oac = opts.originAgentCluster === undefined ? "?1" : opts.originAgentCluster;
90
91
  var dpc = opts.dnsPrefetchControl === undefined ? "off" : opts.dnsPrefetchControl;
91
92
  var csp = opts.csp === undefined ? DEFAULT_CSP : opts.csp;
93
+ // Reporting-Endpoints (W3C Reporting API) — when operator passes a
94
+ // map of endpoint-name → URL, we emit `Reporting-Endpoints: name="url",
95
+ // name2="url2", ...` and (when default CSP is in force) append
96
+ // `report-to default` to the CSP so violations route to the named
97
+ // endpoint. Operators using a custom CSP add `report-to` to it
98
+ // themselves.
99
+ var reportingEndpoints = null;
100
+ if (opts.reportingEndpoints && typeof opts.reportingEndpoints === "object") {
101
+ var pairs = [];
102
+ var keys = Object.keys(opts.reportingEndpoints);
103
+ for (var i = 0; i < keys.length; i += 1) {
104
+ var k = keys[i];
105
+ var v = opts.reportingEndpoints[k];
106
+ if (typeof v !== "string" || v.length === 0) continue;
107
+ // Defensive — refuse CR/LF/NUL in either side (header injection).
108
+ if (/[\r\n\0]/.test(k) || /[\r\n\0]/.test(v)) continue; // allow:duplicate-regex — CR/LF/NUL header-injection rejection appears in cookies / mail / security-headers; each is the boundary primitive — extracting forces a shared module that hides the boundary check from each domain
109
+ pairs.push(k + '="' + v + '"');
110
+ }
111
+ if (pairs.length > 0) reportingEndpoints = pairs.join(", ");
112
+ }
113
+ // Auto-append `report-to default` to the default CSP when operator
114
+ // wires a `default` reporting endpoint and didn't override `csp`.
115
+ if (csp === DEFAULT_CSP && reportingEndpoints &&
116
+ opts.reportingEndpoints && opts.reportingEndpoints["default"]) {
117
+ csp = csp.replace(/;\s*$/, "") + "; report-to default;";
118
+ }
92
119
 
93
120
  return function securityHeaders(req, res, next) {
94
121
  if (typeof res.setHeader !== "function") return next();
@@ -109,7 +136,8 @@ function create(opts) {
109
136
  if (corp) res.setHeader("Cross-Origin-Resource-Policy", corp);
110
137
  if (oac) res.setHeader("Origin-Agent-Cluster", oac);
111
138
  if (dpc) res.setHeader("X-DNS-Prefetch-Control", dpc);
112
- if (csp) res.setHeader("Content-Security-Policy", csp);
139
+ if (csp) res.setHeader("Content-Security-Policy", csp);
140
+ if (reportingEndpoints) res.setHeader("Reporting-Endpoints", reportingEndpoints);
113
141
  next();
114
142
  };
115
143
  }
@@ -291,28 +291,44 @@ function _encodeDnsQuery(host, qtype) {
291
291
  return { buf: buf, id: id };
292
292
  }
293
293
 
294
+ // Walk a DNS-message name in-place and advance `state.off`. RFC 1035
295
+ // §3.1 names terminate either with a single 0x00 byte OR with a
296
+ // 2-byte compression pointer (high two bits 11). The pre-0.7.68
297
+ // parser unconditionally executed `if (buf[off] === 0) off++`
298
+ // after the loop, which consumed the high byte of the next field
299
+ // when the loop had exited via the compression pointer — silently
300
+ // breaking every DNS response that used name compression in the
301
+ // answer section (which is most of them).
302
+ function _skipDnsName(buf, state) {
303
+ var endedViaPointer = false;
304
+ while (state.off < buf.length && buf[state.off] !== 0) {
305
+ if ((buf[state.off] & 0xc0) === 0xc0) { // allow:raw-byte-literal — RFC 1035 name-compression pointer mask
306
+ state.off += 2;
307
+ endedViaPointer = true;
308
+ break;
309
+ }
310
+ state.off += buf[state.off] + 1;
311
+ }
312
+ if (!endedViaPointer && state.off < buf.length && buf[state.off] === 0) {
313
+ state.off += 1;
314
+ }
315
+ }
316
+
294
317
  function _decodeDnsAnswer(buf, qtype) {
295
318
  if (!Buffer.isBuffer(buf) || buf.length < 12) throw new DnsError("dns/bad-reply", "dns reply truncated");
296
319
  var rcode = buf.readUInt8(3) & 0x0f;
297
320
  if (rcode !== 0) throw new DnsError("dns/no-result", "dns reply rcode " + rcode);
298
321
  var qdcount = buf.readUInt16BE(4);
299
322
  var ancount = buf.readUInt16BE(6);
300
- var off = 12;
323
+ var state = { off: 12 };
301
324
  for (var q = 0; q < qdcount; q++) {
302
- while (off < buf.length && buf[off] !== 0) {
303
- if ((buf[off] & 0xc0) === 0xc0) { off += 2; break; }
304
- off += buf[off] + 1;
305
- }
306
- if (buf[off] === 0) off++;
307
- off += 4;
325
+ _skipDnsName(buf, state);
326
+ state.off += 4;
308
327
  }
309
328
  var addrs = [];
310
329
  for (var a = 0; a < ancount; a++) {
311
- while (off < buf.length && buf[off] !== 0) {
312
- if ((buf[off] & 0xc0) === 0xc0) { off += 2; break; }
313
- off += buf[off] + 1;
314
- }
315
- if (buf[off] === 0) off++;
330
+ _skipDnsName(buf, state);
331
+ var off = state.off;
316
332
  var rtype = buf.readUInt16BE(off); off += 2;
317
333
  off += 2;
318
334
  off += 4;
@@ -327,10 +343,20 @@ function _decodeDnsAnswer(buf, qtype) {
327
343
  addrs.push(groups.join(":"));
328
344
  }
329
345
  off += rdlen;
346
+ state.off = off;
330
347
  }
331
348
  return addrs;
332
349
  }
333
350
 
351
+ // Read the AD bit (Authenticated Data, RFC 4035) from a DNS reply
352
+ // header. Byte 3 holds RA, Z, AD, CD, and rcode bits; AD is bit 5
353
+ // (mask 0x20). Set when the upstream recursive resolver has validated
354
+ // the chain.
355
+ function _readAdBit(buf) {
356
+ if (!Buffer.isBuffer(buf) || buf.length < 12) return false;
357
+ return (buf.readUInt8(3) & 0x20) !== 0; // allow:raw-byte-literal — RFC 4035 AD-bit mask
358
+ }
359
+
334
360
  // DoH GET URL length cap. RFC 8484 §4.1 says clients MAY use POST when
335
361
  // the GET URL would exceed implementation limits. We pick 2048 bytes
336
362
  // (a conservative ceiling well below RFC 7230's recommended 8 KB) so
@@ -404,6 +430,98 @@ async function _dohLookup(host, family) {
404
430
  });
405
431
  }
406
432
 
433
+ // _dohLookupSecure — DNSSEC-aware DoH lookup. Returns `{ rrs, ad }`
434
+ // where `ad` is the AD bit (RFC 4035) set by the upstream resolver
435
+ // after chain validation. Internal — operators reach for
436
+ // `resolveSecure` instead.
437
+ async function _dohLookupSecure(host, family) {
438
+ var qtype = family === 6 ? 28 : 1; // allow:raw-byte-literal — DNS QTYPE values for A / AAAA
439
+ var enc = _encodeDnsQuery(host, qtype);
440
+ var b64 = enc.buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
441
+ var getUrl = STATE.doh.url + (STATE.doh.url.indexOf("?") === -1 ? "?" : "&") + "dns=" + b64;
442
+ var forcedMethod = STATE.doh.method;
443
+ var usePost = forcedMethod === "POST" || (!forcedMethod && getUrl.length > DOH_GET_URL_MAX_BYTES);
444
+ var u = safeUrl.parse(STATE.doh.url, { allowedProtocols: safeUrl.ALLOW_HTTP_TLS });
445
+ return new Promise(function (resolve, reject) {
446
+ var reqOpts = {
447
+ hostname: u.hostname,
448
+ port: u.port || 443, // allow:raw-byte-literal — HTTPS default port
449
+ path: u.pathname + u.search,
450
+ method: usePost ? "POST" : "GET",
451
+ headers: { "accept": "application/dns-message" },
452
+ minVersion: "TLSv1.3",
453
+ ecdhCurve: C.TLS_GROUP_CURVE_STR,
454
+ };
455
+ if (STATE.doh.ca) reqOpts.ca = STATE.doh.ca;
456
+ if (usePost) {
457
+ reqOpts.headers["content-type"] = "application/dns-message";
458
+ reqOpts.headers["content-length"] = enc.buf.length;
459
+ } else {
460
+ var parsedGet = safeUrl.parse(getUrl, { allowedProtocols: safeUrl.ALLOW_HTTP_TLS });
461
+ reqOpts.path = parsedGet.pathname + parsedGet.search;
462
+ }
463
+ var req = https.request(reqOpts, function (res) {
464
+ var collector = safeBuffer.boundedChunkCollector({
465
+ maxBytes: C.BYTES.kib(256),
466
+ errorClass: DnsError,
467
+ sizeCode: "dns/doh-too-large",
468
+ sizeMessage: "DoH response exceeds 256 KiB",
469
+ });
470
+ var pushFailed = null;
471
+ res.on("data", function (c) {
472
+ if (pushFailed) return;
473
+ try { collector.push(c); }
474
+ catch (e) { pushFailed = e; }
475
+ });
476
+ res.on("end", function () {
477
+ try {
478
+ if (pushFailed) { reject(pushFailed); return; }
479
+ var body = collector.result();
480
+ if (res.statusCode !== 200) { // allow:raw-byte-literal — HTTP 200 OK
481
+ reject(new DnsError("dns/doh-http", "DoH HTTP " + res.statusCode + " for " + host));
482
+ return;
483
+ }
484
+ resolve({ rrs: _decodeDnsAnswer(body, qtype), ad: _readAdBit(body) });
485
+ } catch (e) { reject(e); }
486
+ });
487
+ });
488
+ req.on("error", function (e) { reject(new DnsError("dns/doh-failed", "DoH request failed: " + e.message)); });
489
+ if (usePost) req.write(enc.buf);
490
+ req.end();
491
+ });
492
+ }
493
+
494
+ // resolveSecure — DNSSEC-aware resolution. Returns `{ rrs, ad }`
495
+ // where `ad` is the AD bit (RFC 4035) set by the upstream DoH
496
+ // resolver after chain validation, and `rrs` is the answer-record
497
+ // list (IPv4 / IPv6 string addresses for A / AAAA queries).
498
+ //
499
+ // Operators wiring DANE / TLSA validation (RFC 7672 SMTP DANE)
500
+ // require `ad === true` to honor the DANE security claim per RFC
501
+ // 7672 §1.3 — without DNSSEC validation the TLSA records can't be
502
+ // authenticated and the chain check is meaningless.
503
+ //
504
+ // Only available over DoH transport. The system resolver and DoT
505
+ // transports don't surface the AD bit through Node's API today.
506
+ async function resolveSecure(host, type) {
507
+ type = type || "A";
508
+ if (!STATE.doh) {
509
+ throw new DnsError("dns/secure-requires-doh",
510
+ "resolveSecure requires DoH transport (call useDnsOverHttps " +
511
+ "or rely on the default-on DoH posture)");
512
+ }
513
+ if (typeof host !== "string" || host.length === 0 || host.length > 253) { // allow:raw-byte-literal — RFC 1035 hostname octet ceiling
514
+ throw new DnsError("dns/bad-host",
515
+ "resolveSecure host is malformed");
516
+ }
517
+ var family;
518
+ if (type === "A") family = 4;
519
+ else if (type === "AAAA") family = 6;
520
+ else throw new DnsError("dns/secure-unsupported-type",
521
+ "resolveSecure currently supports A and AAAA; got " + type);
522
+ return _dohLookupSecure(host, family);
523
+ }
524
+
407
525
  // DoT connection pool. Per-(host:port) cached TLS socket so successive
408
526
  // lookups amortize the handshake. Sockets idle past the timeout are
409
527
  // closed and removed; first lookup after expiry rebuilds. Each socket
@@ -695,6 +813,7 @@ module.exports = {
695
813
  resolve4: resolve4,
696
814
  resolve6: resolve6,
697
815
  resolveAaaa: resolveAaaa,
816
+ resolveSecure: resolveSecure,
698
817
  nodeLookup: nodeLookup,
699
818
  clearCache: _clearCache,
700
819
  DnsError: DnsError,
@@ -42,9 +42,13 @@
42
42
  * - Full DANE certificate-chain verification per RFC 6698 (needs
43
43
  * ASN.1 cert parsing). Operators today verify policy presence +
44
44
  * match the leaf SHA-256 themselves.
45
- * - DNSSEC-validated DANE lookups (node:dns doesn't expose
46
- * DNSSEC ad-bit; operators pin to a DNSSEC-validating resolver
47
- * externally).
45
+ * - DNSSEC-validated DANE lookups: the framework now exposes the
46
+ * AD bit via `b.network.dns.resolveSecure(name, type) { rrs,
47
+ * ad }`. Operators wiring strict RFC 7672 §1.3 compliance pass
48
+ * a DNSSEC-aware resolver via opts and refuse the chain when
49
+ * ad === false. The default `tlsa()` lookup path stays on
50
+ * `node:dns` for compatibility with operators on system
51
+ * resolvers.
48
52
  */
49
53
 
50
54
  var dns = require("node:dns");
@@ -13,7 +13,10 @@
13
13
  "server": "lib/vendor/noble-ciphers.cjs"
14
14
  },
15
15
  "bundler": "esbuild --format=cjs --minify --platform=node",
16
- "bundledAt": "2026-04-25"
16
+ "bundledAt": "2026-04-25",
17
+ "hashes": {
18
+ "server": "sha256:5d539dfc9ef47121d4c09bd7256d76448a1f5ac47ee09ac44c78ff6a062af9ab"
19
+ }
17
20
  },
18
21
  "argon2": {
19
22
  "version": "0.44.0",
@@ -39,7 +42,11 @@
39
42
  "win32-x64"
40
43
  ],
41
44
  "bundler": "esbuild --format=cjs --platform=node (inlines @phc/format + node-gyp-build)",
42
- "bundledAt": "2026-04-25"
45
+ "bundledAt": "2026-04-25",
46
+ "hashes": {
47
+ "server": "sha256:93b8d2fb7f24dc1b3304dc9420844d5e1afc199c41ab1f9a90c8de48cc7c2359",
48
+ "prebuilds": "sha256-tree:65921b7cf331e0a9430a1b52440da8f26cdf9d215a4cd490edbc4804dd713df3"
49
+ }
43
50
  },
44
51
  "@simplewebauthn/server": {
45
52
  "version": "13.3.0",
@@ -57,7 +64,10 @@
57
64
  "server": "lib/vendor/simplewebauthn-server.cjs"
58
65
  },
59
66
  "bundler": "esbuild --format=cjs --minify --platform=node --external:crypto --external:node:crypto",
60
- "bundledAt": "2026-04-26"
67
+ "bundledAt": "2026-04-26",
68
+ "hashes": {
69
+ "server": "sha256:a9777dca582095d67f17ca24e19a0791de29928555b6b779c2233429175eb3f0"
70
+ }
61
71
  },
62
72
  "SecLists-common-passwords-top-10000": {
63
73
  "version": "10k-most-common (master)",
@@ -69,7 +79,10 @@
69
79
  "server": "lib/vendor/common-passwords-top-10000.txt"
70
80
  },
71
81
  "bundler": "curl https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10k-most-common.txt",
72
- "bundledAt": "2026-05-02"
82
+ "bundledAt": "2026-05-02",
83
+ "hashes": {
84
+ "server": "sha256:4adb3f0afb4a10cf19ebe48d8c69a46f934bbc8d77c694c210564f9583e7f4ba"
85
+ }
73
86
  },
74
87
  "peculiar-pki": {
75
88
  "version": "2.0.0+pkijs-3.4.0",
@@ -90,7 +103,10 @@
90
103
  "server": "lib/vendor/pki.cjs"
91
104
  },
92
105
  "bundler": "esbuild --format=cjs --minify --platform=node --external:crypto --external:node:crypto",
93
- "bundledAt": "2026-04-29"
106
+ "bundledAt": "2026-04-29",
107
+ "hashes": {
108
+ "server": "sha256:9bbc191afaaa2b1e5757f00480457c08134cdc2c55d541df18d9155bba9cbf77"
109
+ }
94
110
  }
95
111
  }
96
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.7.64",
3
+ "version": "0.7.73",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",
@@ -2,10 +2,10 @@
2
2
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
3
3
  "bomFormat": "CycloneDX",
4
4
  "specVersion": "1.5",
5
- "serialNumber": "urn:uuid:186ac1cf-f222-4ea2-81b9-ad8a89f1849e",
5
+ "serialNumber": "urn:uuid:55a0114a-f249-481f-b122-6997247485a1",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-06T00:58:07.750Z",
8
+ "timestamp": "2026-05-06T03:31:31.641Z",
9
9
  "lifecycles": [
10
10
  {
11
11
  "phase": "build"
@@ -19,14 +19,14 @@
19
19
  }
20
20
  ],
21
21
  "component": {
22
- "bom-ref": "@blamejs/core@0.7.64",
22
+ "bom-ref": "@blamejs/core@0.7.73",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.7.64",
25
+ "version": "0.7.73",
26
26
  "scope": "required",
27
27
  "author": "blamejs contributors",
28
28
  "description": "The Node framework that owns its stack.",
29
- "purl": "pkg:npm/%40blamejs/core@0.7.64",
29
+ "purl": "pkg:npm/%40blamejs/core@0.7.73",
30
30
  "properties": [],
31
31
  "externalReferences": [
32
32
  {
@@ -54,7 +54,7 @@
54
54
  "components": [],
55
55
  "dependencies": [
56
56
  {
57
- "ref": "@blamejs/core@0.7.64",
57
+ "ref": "@blamejs/core@0.7.73",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]