@blamejs/core 0.10.6 → 0.10.8
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/CHANGELOG.md +2 -0
- package/index.js +8 -1
- package/lib/ai-content-detect.js +268 -0
- package/lib/ai-input.js +58 -8
- package/lib/ai-model-manifest.js +363 -0
- package/lib/atomic-file.js +83 -0
- package/lib/audit.js +3 -0
- package/lib/content-credentials.js +140 -0
- package/lib/crypto.js +30 -0
- package/lib/external-db.js +2 -2
- package/lib/guard-dsn.js +8 -5
- package/lib/guard-list-id.js +14 -10
- package/lib/guard-list-unsubscribe.js +67 -1
- package/lib/guard-message-id.js +26 -0
- package/lib/mail-arc-sign.js +21 -1
- package/lib/mail-auth.js +2 -1
- package/lib/mail-dkim.js +50 -9
- package/lib/mail-server-imap.js +104 -10
- package/lib/mail-server-mx.js +94 -14
- package/lib/mail-server-submission.js +135 -1
- package/lib/mail-store.js +6 -1
- package/lib/network-dns-resolver.js +1 -2
- package/lib/network-dns.js +4 -4
- package/lib/promise-pool.js +162 -0
- package/lib/safe-mime.js +51 -4
- package/lib/sd-notify.js +269 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/safe-mime.js
CHANGED
|
@@ -53,7 +53,15 @@ var DEFAULT_MAX_PARTS = 64; // allow:raw-byte-l
|
|
|
53
53
|
var DEFAULT_MAX_NESTING_DEPTH = 16;
|
|
54
54
|
var DEFAULT_MAX_BOUNDARY = 70; // RFC 2046 §5.1.1
|
|
55
55
|
var DEFAULT_MAX_HEADER_BYTES = C.BYTES.kib(64);
|
|
56
|
-
|
|
56
|
+
// RFC 5322 §2.1.1 line cap. The spec defines TWO limits: a SHOULD of
|
|
57
|
+
// 78 bytes (the readability target) and a MUST of 998 bytes (the
|
|
58
|
+
// hard ceiling). The 78-byte SHOULD is intentionally NOT enforced
|
|
59
|
+
// here — modern senders routinely emit header lines longer than 78
|
|
60
|
+
// bytes (long URLs in List-Unsubscribe, EAI display names) and a
|
|
61
|
+
// strict 78-byte refusal would reject legitimate mail. We enforce
|
|
62
|
+
// only the 998-byte MUST. Future drift attempting to "fix" this to
|
|
63
|
+
// 78 would be a regression and should fail the audit gate.
|
|
64
|
+
var DEFAULT_MAX_HEADER_LINE = 998; // allow:raw-byte-literal — RFC 5322 §2.1.1 MUST (998); the SHOULD (78) is by design not enforced
|
|
57
65
|
// Per-message header-count cap. RFC 5322 places no upper bound on
|
|
58
66
|
// the number of headers in a message; without one, a sender can pack
|
|
59
67
|
// tens of thousands of one-byte headers into the maxHeaderBytes budget
|
|
@@ -77,8 +85,15 @@ var DEFAULT_CHARSETS = Object.freeze([
|
|
|
77
85
|
"euc-kr", "euc-jp",
|
|
78
86
|
]);
|
|
79
87
|
|
|
88
|
+
// RFC 3030 §3 — `binary` CTE on receive REQUIRES the receiving MTA
|
|
89
|
+
// to have advertised BINARYMIME during ESMTP negotiation. Inbound
|
|
90
|
+
// flows without explicit BINARYMIME wiring must refuse `binary`
|
|
91
|
+
// because consumers downstream (DKIM canonicalization, message
|
|
92
|
+
// rewriting) assume CRLF line structure that `binary` doesn't
|
|
93
|
+
// guarantee. Operators that wire BINARYMIME end-to-end opt back in
|
|
94
|
+
// via `transferEncodingAllowlist: ["7bit", ..., "binary"]`.
|
|
80
95
|
var DEFAULT_TRANSFER_ENCODINGS = Object.freeze([
|
|
81
|
-
"7bit", "8bit", "
|
|
96
|
+
"7bit", "8bit", "quoted-printable", "base64",
|
|
82
97
|
]);
|
|
83
98
|
|
|
84
99
|
/**
|
|
@@ -453,12 +468,18 @@ function _parseHeaders(buf, ctx) {
|
|
|
453
468
|
// Refuse NUL, CR, LF, and other C0 control chars in header values.
|
|
454
469
|
// Tab (0x09) is allowed (header folding). C1 control range
|
|
455
470
|
// (0x80-0x9F) NOT refused — legitimate non-ASCII via EAI/RFC 2047
|
|
456
|
-
// decoded-words can produce bytes in that range.
|
|
471
|
+
// decoded-words can produce bytes in that range. Error metadata
|
|
472
|
+
// surfaces the BYTE offset (via `Buffer.byteLength` on the JS
|
|
473
|
+
// string prefix) rather than the UTF-16 code-unit index, so the
|
|
474
|
+
// operator audit log lines up with the wire-level byte stream
|
|
475
|
+
// they're inspecting.
|
|
457
476
|
for (var hci = 0; hci < value.length; hci += 1) {
|
|
458
477
|
var hcc = value.charCodeAt(hci);
|
|
459
478
|
if ((hcc < 0x20 && hcc !== 0x09) || hcc === 0x7F) { // allow:raw-byte-literal — C0 control char + DEL refusal
|
|
479
|
+
var byteOffset = Buffer.byteLength(value.slice(0, hci), "utf8");
|
|
460
480
|
throw new SafeMimeError("safe-mime/control-char-in-header",
|
|
461
|
-
"safeMime.parse: header '" + name + "' contains control char 0x" +
|
|
481
|
+
"safeMime.parse: header '" + name + "' contains control char 0x" +
|
|
482
|
+
hcc.toString(16) + " at byte offset " + byteOffset); // allow:raw-byte-literal — toString radix 16 hex, not bytes
|
|
462
483
|
}
|
|
463
484
|
}
|
|
464
485
|
value = _decodeRfc2047Words(value);
|
|
@@ -673,9 +694,35 @@ function _decodeBufferAs(buf, charset) {
|
|
|
673
694
|
if (c === "us-ascii" || c === "ascii") return buf.toString("ascii");
|
|
674
695
|
if (c === "iso-8859-1" || c === "latin1") return buf.toString("latin1");
|
|
675
696
|
if (c === "utf-16le") return buf.toString("utf16le");
|
|
697
|
+
if (c === "utf-16be") return _decodeUtf16BE(buf);
|
|
698
|
+
if (c === "utf-16") {
|
|
699
|
+
// RFC 2781 §3.3 — `utf-16` with a leading BOM (FE FF = BE, FF FE
|
|
700
|
+
// = LE). When no BOM is present the spec defaults to BE; Node
|
|
701
|
+
// doesn't speak BE natively so we transcode either way.
|
|
702
|
+
if (buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe) {
|
|
703
|
+
return buf.subarray(2).toString("utf16le");
|
|
704
|
+
}
|
|
705
|
+
if (buf.length >= 2 && buf[0] === 0xfe && buf[1] === 0xff) {
|
|
706
|
+
return _decodeUtf16BE(buf.subarray(2));
|
|
707
|
+
}
|
|
708
|
+
return _decodeUtf16BE(buf); // RFC 2781 §3.3 BE default with no BOM
|
|
709
|
+
}
|
|
676
710
|
return buf.toString("utf8");
|
|
677
711
|
}
|
|
678
712
|
|
|
713
|
+
// utf-16be → utf-16le swap (Node has no direct utf-16be decoder).
|
|
714
|
+
// Byte-pair endian flip into a temporary buffer, then decode as
|
|
715
|
+
// utf-16le. Allocates a single buffer (no per-character churn).
|
|
716
|
+
function _decodeUtf16BE(buf) {
|
|
717
|
+
var n = buf.length & ~1; // allow:raw-byte-literal — pair alignment mask
|
|
718
|
+
var swapped = Buffer.alloc(n);
|
|
719
|
+
for (var i = 0; i < n; i += 2) {
|
|
720
|
+
swapped[i] = buf[i + 1];
|
|
721
|
+
swapped[i + 1] = buf[i];
|
|
722
|
+
}
|
|
723
|
+
return swapped.toString("utf16le");
|
|
724
|
+
}
|
|
725
|
+
|
|
679
726
|
function _materializeText(part) {
|
|
680
727
|
return {
|
|
681
728
|
contentType: part.leaf.contentType,
|
package/lib/sd-notify.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.sdNotify
|
|
4
|
+
* @nav Process
|
|
5
|
+
* @title systemd Notify
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* `sd_notify` protocol surface for daemons running under
|
|
9
|
+
* `Type=notify` systemd units. Composes the standard lifecycle
|
|
10
|
+
* messages — `READY=1` on boot, `WATCHDOG=1` on heartbeat,
|
|
11
|
+
* `STOPPING=1` on shutdown, `RELOADING=1` on hot-reload — against
|
|
12
|
+
* the `$NOTIFY_SOCKET` env var systemd populates for the child
|
|
13
|
+
* process.
|
|
14
|
+
*
|
|
15
|
+
* Transport: Node has no unix-DGRAM socket support in its `dgram`
|
|
16
|
+
* module, so the v1 path shells out to `systemd-notify(1)` via
|
|
17
|
+
* `execFile` (NOT `exec` — no shell-string parsing on the
|
|
18
|
+
* message bytes). Operators running under systemd already have
|
|
19
|
+
* `systemd-tools` installed by definition, so the dependency is
|
|
20
|
+
* no expansion of the trust surface.
|
|
21
|
+
*
|
|
22
|
+
* Compose with `b.appShutdown` for the STOPPING signal: register a
|
|
23
|
+
* priority-0 phase that calls `b.sdNotify.stopping()` so systemd
|
|
24
|
+
* sees the shutdown intent before the framework tears anything
|
|
25
|
+
* down. Compose with a periodic `WATCHDOG=1` against the unit's
|
|
26
|
+
* `WatchdogSec=` interval so systemd auto-restarts the daemon if
|
|
27
|
+
* the event loop wedges.
|
|
28
|
+
*
|
|
29
|
+
* When `$NOTIFY_SOCKET` is unset (process running outside systemd
|
|
30
|
+
* — bare invocation, foreground dev, container without
|
|
31
|
+
* `--notify-ready`), every call is a no-op that surfaces a single
|
|
32
|
+
* boot-time audit entry. Operators get observability of the
|
|
33
|
+
* degraded state without per-call log noise.
|
|
34
|
+
*
|
|
35
|
+
* @card
|
|
36
|
+
* sd_notify protocol for systemd Type=notify daemons — READY / WATCHDOG / STOPPING / RELOADING. Composes b.appShutdown for shutdown signaling.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
var { execFile } = require("node:child_process");
|
|
40
|
+
var C = require("./constants");
|
|
41
|
+
var safeEnv = require("./parsers/safe-env");
|
|
42
|
+
var audit = require("./audit");
|
|
43
|
+
var { defineClass } = require("./framework-error");
|
|
44
|
+
|
|
45
|
+
var SdNotifyError = defineClass("SdNotifyError", { alwaysPermanent: true });
|
|
46
|
+
|
|
47
|
+
// Whitelist of sd_notify state= values we ship as named helpers. The
|
|
48
|
+
// underlying `send({ state })` accepts any string but the helpers are
|
|
49
|
+
// the operator-facing surface — `READY=1` etc. — and the audit log
|
|
50
|
+
// records the named state, not arbitrary payload bytes.
|
|
51
|
+
var KNOWN_STATES = Object.freeze({
|
|
52
|
+
ready: "READY=1",
|
|
53
|
+
stopping: "STOPPING=1",
|
|
54
|
+
reloading: "RELOADING=1",
|
|
55
|
+
watchdog: "WATCHDOG=1",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function _notifySocketPath() {
|
|
59
|
+
var p = safeEnv.readVar("NOTIFY_SOCKET");
|
|
60
|
+
if (typeof p !== "string" || p.length === 0) return null;
|
|
61
|
+
// Abstract namespace socket (Linux-only) prefixed with `@` —
|
|
62
|
+
// systemd-notify(1) accepts the same form, so we don't normalize.
|
|
63
|
+
return p;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function _runNotify(payload) {
|
|
67
|
+
return new Promise(function (resolve, reject) {
|
|
68
|
+
var args = [];
|
|
69
|
+
var lines = String(payload).split("\n");
|
|
70
|
+
for (var i = 0; i < lines.length; i += 1) {
|
|
71
|
+
if (lines[i].length > 0) args.push(lines[i]);
|
|
72
|
+
}
|
|
73
|
+
if (args.length === 0) { resolve(); return; }
|
|
74
|
+
// execFile (not exec) — no shell evaluation; the message bytes
|
|
75
|
+
// pass through argv exactly. systemd-notify accepts one or more
|
|
76
|
+
// KEY=VALUE arguments. The `--no-block` flag returns immediately
|
|
77
|
+
// without waiting for the notification to be processed.
|
|
78
|
+
execFile("systemd-notify", ["--no-block"].concat(args),
|
|
79
|
+
{ timeout: C.TIME.seconds(5), windowsHide: true },
|
|
80
|
+
function (err) {
|
|
81
|
+
if (err) reject(err);
|
|
82
|
+
else resolve();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @primitive b.sdNotify.send
|
|
89
|
+
* @signature b.sdNotify.send(opts)
|
|
90
|
+
* @since 0.10.8
|
|
91
|
+
* @status stable
|
|
92
|
+
* @related b.sdNotify.ready, b.sdNotify.stopping, b.appShutdown.create
|
|
93
|
+
*
|
|
94
|
+
* Generic sd_notify dispatch. Sends one or more `KEY=VALUE` payload
|
|
95
|
+
* lines to systemd via `systemd-notify(1)`. No-op when
|
|
96
|
+
* `$NOTIFY_SOCKET` is unset (foreground / container without
|
|
97
|
+
* `--notify-ready` / non-systemd init). Returns a Promise resolving
|
|
98
|
+
* on dispatch success.
|
|
99
|
+
*
|
|
100
|
+
* @opts
|
|
101
|
+
* state: string, // e.g. "READY=1" / "STOPPING=1"
|
|
102
|
+
* status: string, // free-form status text → `STATUS=`
|
|
103
|
+
* mainpid: number, // PID override → `MAINPID=`
|
|
104
|
+
* audit: boolean, // default true
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* await b.sdNotify.send({ state: "READY=1", status: "Listening on :8080" });
|
|
108
|
+
*/
|
|
109
|
+
function send(opts) {
|
|
110
|
+
opts = opts || {};
|
|
111
|
+
var lines = [];
|
|
112
|
+
if (typeof opts.state === "string" && opts.state.length > 0) lines.push(opts.state);
|
|
113
|
+
if (typeof opts.status === "string" && opts.status.length > 0) {
|
|
114
|
+
// STATUS= permits arbitrary UTF-8 except newline — refuse newline
|
|
115
|
+
// so a hostile status string can't smuggle a second key.
|
|
116
|
+
if (opts.status.indexOf("\n") !== -1 || opts.status.indexOf("\r") !== -1) {
|
|
117
|
+
throw new SdNotifyError("sd-notify/control-char-in-status",
|
|
118
|
+
"send: status field must not contain CR/LF (sd_notify framing)");
|
|
119
|
+
}
|
|
120
|
+
lines.push("STATUS=" + opts.status);
|
|
121
|
+
}
|
|
122
|
+
if (opts.mainpid !== undefined) {
|
|
123
|
+
if (typeof opts.mainpid !== "number" || !isFinite(opts.mainpid) ||
|
|
124
|
+
Math.floor(opts.mainpid) !== opts.mainpid || opts.mainpid < 1) {
|
|
125
|
+
throw new SdNotifyError("sd-notify/bad-mainpid",
|
|
126
|
+
"send: mainpid must be a positive integer");
|
|
127
|
+
}
|
|
128
|
+
lines.push("MAINPID=" + opts.mainpid);
|
|
129
|
+
}
|
|
130
|
+
if (lines.length === 0) return Promise.resolve();
|
|
131
|
+
|
|
132
|
+
var socketPath = _notifySocketPath();
|
|
133
|
+
if (socketPath === null) {
|
|
134
|
+
if (opts.audit !== false) {
|
|
135
|
+
try {
|
|
136
|
+
audit.safeEmit({
|
|
137
|
+
action: "sdnotify.send.skipped",
|
|
138
|
+
outcome: "denied",
|
|
139
|
+
metadata: { reason: "no-notify-socket", state: opts.state || null },
|
|
140
|
+
});
|
|
141
|
+
} catch (_e) { /* drop-silent */ }
|
|
142
|
+
}
|
|
143
|
+
return Promise.resolve();
|
|
144
|
+
}
|
|
145
|
+
var auditOn = opts.audit !== false;
|
|
146
|
+
return _runNotify(lines.join("\n")).then(function () {
|
|
147
|
+
if (auditOn) {
|
|
148
|
+
try {
|
|
149
|
+
audit.safeEmit({
|
|
150
|
+
action: "sdnotify.send",
|
|
151
|
+
outcome: "success",
|
|
152
|
+
metadata: { state: opts.state || null, status: opts.status || null },
|
|
153
|
+
});
|
|
154
|
+
} catch (_e) { /* drop-silent */ }
|
|
155
|
+
}
|
|
156
|
+
}).catch(function (err) {
|
|
157
|
+
if (auditOn) {
|
|
158
|
+
try {
|
|
159
|
+
audit.safeEmit({
|
|
160
|
+
action: "sdnotify.send",
|
|
161
|
+
outcome: "failure",
|
|
162
|
+
metadata: { state: opts.state || null, error: (err && err.message) || String(err) },
|
|
163
|
+
});
|
|
164
|
+
} catch (_e2) { /* drop-silent */ }
|
|
165
|
+
}
|
|
166
|
+
throw new SdNotifyError("sd-notify/dispatch-failed",
|
|
167
|
+
"send: systemd-notify dispatch failed: " + ((err && err.message) || String(err)));
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @primitive b.sdNotify.ready
|
|
173
|
+
* @signature b.sdNotify.ready(opts?)
|
|
174
|
+
* @since 0.10.8
|
|
175
|
+
* @status stable
|
|
176
|
+
* @related b.sdNotify.send, b.sdNotify.stopping
|
|
177
|
+
*
|
|
178
|
+
* Send `READY=1` to systemd, signaling boot complete. Use once the
|
|
179
|
+
* listener is bound and the daemon is accepting work.
|
|
180
|
+
*
|
|
181
|
+
* @opts
|
|
182
|
+
* status: string, // free-form status text → STATUS=
|
|
183
|
+
*
|
|
184
|
+
* @opts
|
|
185
|
+
* status: string, // free-form status text → STATUS=
|
|
186
|
+
* audit: boolean, // default true
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* await b.sdNotify.ready({ status: "Listening on :8080" });
|
|
190
|
+
*/
|
|
191
|
+
function ready(opts) {
|
|
192
|
+
return send(Object.assign({}, opts || {}, { state: KNOWN_STATES.ready }));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* @primitive b.sdNotify.stopping
|
|
197
|
+
* @signature b.sdNotify.stopping(opts?)
|
|
198
|
+
* @since 0.10.8
|
|
199
|
+
* @status stable
|
|
200
|
+
* @related b.sdNotify.send, b.appShutdown.create
|
|
201
|
+
*
|
|
202
|
+
* Send `STOPPING=1`. Operators wire this into `b.appShutdown` as the
|
|
203
|
+
* earliest shutdown phase (priority 0) so systemd sees the shutdown
|
|
204
|
+
* intent before any teardown begins.
|
|
205
|
+
*
|
|
206
|
+
* @opts
|
|
207
|
+
* status: string, // free-form status text → STATUS=
|
|
208
|
+
* audit: boolean, // default true
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* b.appShutdown.create({ name: "sd-notify-stopping", priority: 0,
|
|
212
|
+
* run: function () { return b.sdNotify.stopping(); } });
|
|
213
|
+
*/
|
|
214
|
+
function stopping(opts) {
|
|
215
|
+
return send(Object.assign({}, opts || {}, { state: KNOWN_STATES.stopping }));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @primitive b.sdNotify.reloading
|
|
220
|
+
* @signature b.sdNotify.reloading(opts?)
|
|
221
|
+
* @since 0.10.8
|
|
222
|
+
* @status stable
|
|
223
|
+
*
|
|
224
|
+
* Send `RELOADING=1` then (after the reload completes) `READY=1`.
|
|
225
|
+
* Use during hot-config-reload paths; systemd treats the unit as
|
|
226
|
+
* "reloading" until the next `READY=1`.
|
|
227
|
+
*
|
|
228
|
+
* @opts
|
|
229
|
+
* status: string, // free-form status text → STATUS=
|
|
230
|
+
* audit: boolean, // default true
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* await b.sdNotify.reloading();
|
|
234
|
+
* await reloadConfig();
|
|
235
|
+
* await b.sdNotify.ready();
|
|
236
|
+
*/
|
|
237
|
+
function reloading(opts) {
|
|
238
|
+
return send(Object.assign({}, opts || {}, { state: KNOWN_STATES.reloading }));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* @primitive b.sdNotify.watchdog
|
|
243
|
+
* @signature b.sdNotify.watchdog(opts?)
|
|
244
|
+
* @since 0.10.8
|
|
245
|
+
* @status stable
|
|
246
|
+
*
|
|
247
|
+
* Send `WATCHDOG=1`. Operators with `WatchdogSec=` configured on
|
|
248
|
+
* their unit call this periodically (e.g. every `WatchdogSec/2`)
|
|
249
|
+
* so systemd auto-restarts the daemon when the event loop wedges.
|
|
250
|
+
*
|
|
251
|
+
* @opts
|
|
252
|
+
* audit: boolean, // default true
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* setInterval(function () { b.sdNotify.watchdog(); }, 15000);
|
|
256
|
+
*/
|
|
257
|
+
function watchdog(opts) {
|
|
258
|
+
return send(Object.assign({}, opts || {}, { state: KNOWN_STATES.watchdog }));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = {
|
|
262
|
+
send: send,
|
|
263
|
+
ready: ready,
|
|
264
|
+
stopping: stopping,
|
|
265
|
+
reloading: reloading,
|
|
266
|
+
watchdog: watchdog,
|
|
267
|
+
isAvailable: function () { return _notifySocketPath() !== null; },
|
|
268
|
+
SdNotifyError: SdNotifyError,
|
|
269
|
+
};
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.6",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:2db3bd59-b835-4672-aac5-7c874f2f9276",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-
|
|
8
|
+
"timestamp": "2026-05-18T00:17:19.942Z",
|
|
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.10.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.10.8",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.10.
|
|
25
|
+
"version": "0.10.8",
|
|
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.10.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.10.8",
|
|
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.10.
|
|
57
|
+
"ref": "@blamejs/core@0.10.8",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|