@blamejs/core 0.14.14 → 0.14.16
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/lib/app.js +8 -0
- package/lib/mail.js +1 -0
- package/lib/network-dns.js +1 -0
- package/lib/network-nts.js +4 -0
- package/lib/ntp-check.js +8 -0
- package/lib/redis-client.js +8 -0
- package/lib/validate-opts.js +23 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.14.x
|
|
10
10
|
|
|
11
|
+
- v0.14.16 (2026-05-31) — **Connection entry-point ports are validated at config time.** Six connection entry points previously read opts.port with a bare `|| <default>` fallback, silently coercing a string, negative, NaN, or out-of-range port instead of catching the operator's typo. A new b.validateOpts.optionalPort enforces the RFC 6335 §6 wire-valid range and is wired into b.mail.smtpTransport, b.ntpCheck.querySingle, b.networkDns.useDnsOverTls, b.networkNts (KE handshake / query / facade), b.redisClient.create, and createApp().listen — each now throws at construction with a clear message naming the bad value. The app.listen / createApp bind site opts into allowZero so port 0 (the legitimate ephemeral-bind sentinel) still works; the five outbound-connect sites require [1,65535]. **Added:** *`b.validateOpts.optionalPort`* — A config-time port validator: `optionalPort(value, label, errorClass, code, opts?)` returns an omitted (`undefined` / `null`) port unchanged, and otherwise requires an integer in the RFC 6335 §6 wire-valid range [1,65535] — rejecting a string, negative, NaN, Infinity, fractional, or out-of-range value. Pass `{ allowZero: true }` for a listen-bind site where port 0 is the OS ephemeral-bind sentinel. The thrown message reports the offending shape (so `Infinity` / `"443"` stay visible), and routes a caller-supplied typed framework error (or a plain Error when none is given), matching the existing `optionalPositiveFinite` family. **Changed:** *Connection entry points reject a malformed port at construction* — `b.mail.smtpTransport`, `b.ntpCheck.querySingle`, `b.networkDns.useDnsOverTls`, `b.networkNts.performKeHandshake` / `query` / `querySingle`, `b.redisClient.create`, and `createApp().listen` (plus the `createApp` constructor's default port) now validate `opts.port` and throw synchronously on a non-integer / out-of-range value rather than coercing it through `||` to a default. This is a behavior change for a caller that was passing a non-canonical port (e.g. the string `"587"` or a NaN) and relying on the silent fallback — pass an integer in [1,65535] instead (or `0` for an ephemeral `createApp().listen` bind). `b.ntpCheck` gains a typed `NtpCheckError` for this (it had no error class before). **Detectors:** *Connection entry points must compose the port validator* — A new check flags a lib connection entry point that reads `opts.port` / `opts.kePort` / `opts.ntpPort` with a `|| <default>` fallback without composing `b.validateOpts.optionalPort` (or the equivalent `numericBounds.isPositiveFiniteInt` + 65535 cap), so an unvalidated port read can't slip back in. **Migration:** *Pass an integer port to connection primitives* — If you were passing a non-integer or out-of-range `opts.port` to a mail / NTP / NTS / DNS-over-TLS / Redis transport or to `createApp().listen` and relying on the silent `|| default` fallback, that now throws at construction. Pass an integer in [1,65535]; for an ephemeral `createApp().listen` bind, pass `0` (still accepted).
|
|
12
|
+
|
|
11
13
|
- v0.14.14 (2026-05-31) — **Recognized consent purposes with lawful-basis gating, and a new b.privacy namespace for annual EdTech vendor-review attestations.** Closes the student-data gap where an educational-only consent purpose and an annual third-party vendor-review report were described but never implemented. b.consent gains a recognized-purpose vocabulary: a purpose value matching a recognized key carries lawful-basis constraints that grant() enforces, and the named educational-only purpose (FERPA's school-official exception and California's SOPIPA) refuses a legitimate_interests lawful basis. The new b.privacy namespace ships vendorReview(), a builder for the dated, clause-by-clause annual EdTech third-party / processor review FERPA and SOPIPA expect a school or district to keep — it computes whether every required clause (no targeted advertising, no commercial profiling, no sale of student data, deletion on request, school-official designation, and so on) is attested, names the gaps, and stamps a 365-day re-review clock. Free-form consent purposes keep working unchanged, so the vocabulary is opt-in and additive. **Added:** *`b.consent` recognized-purpose vocabulary + lawful-basis gating* — `b.consent.recognizedPurpose(name)` looks up a recognized purpose and `b.consent.listPurposes()` enumerates them. When a `grant({ purpose })` value matches a recognized key, `grant()` enforces that purpose's lawful-basis constraints; the `educational-only` purpose forbids a `legitimate_interests` basis (FERPA 34 CFR 99.31(a)(1) school-official exception; California SOPIPA Cal. B&P 22584; FTC school-authorized COPPA consent 16 CFR 312.5(c)(10)) and marks the data commercial-use-prohibited. The commercial-use prohibition is an operator trust-boundary obligation — `isGranted()` does not re-derive it. Any purpose value NOT in the vocabulary stays free-form and unconstrained, so existing callers are unaffected; the hash-chain column set is unchanged, so `b.consent.verify()` over existing rows is unaffected. · *`b.privacy.vendorReview` — annual EdTech vendor-review attestation* — A new `b.privacy` namespace whose `vendorReview(opts)` builds the dated third-party / processor review a FERPA school-official arrangement and California SOPIPA expect for every vendor that touches student data. The operator supplies a boolean attestation per clause (educational-purpose-only, no-targeted-advertising, no-commercial-profiling, no-sale-of-student-data, security-safeguards, deletion-on-request, sub-processor-currency, breach-notification, school-official-designation, directory-information-handling); `vendorReview` validates the shape, computes whether every required clause is attested (`attested`) and which are not (`gaps`), and stamps `reviewedAt` plus a 365-day `nextReviewDueAt` re-review clock. `b.privacy.listVendorReviewClauses()` returns the clause set with citations. Operator-feeds-metadata: the frozen report is not framework-persisted — compose it into your retention / audit / export sink. A best-effort `privacy.vendor_review.recorded` audit event fires when an audit sink is wired. **Detectors:** *A gated consent purpose must go through `b.consent`* — A new check flags any lib code that mints a consent row with a hardcoded `educational-only` purpose literal without composing the recognized-purpose vocabulary — which would record the value while never enforcing its FERPA / SOPIPA lawful-basis constraint.
|
|
12
14
|
|
|
13
15
|
- v0.14.13 (2026-05-31) — **Close advertised-but-missing surface: SRS1 chained forwarding, DCQL array-wildcard claim paths, and in-memory safe-archive extraction.** Three primitives advertised a capability in their documentation or card but refused or omitted it at runtime; this release implements each. b.mail.srs gains srs1Rewrite for the SRS1 double-forward (and multi-hop) case — previously the @intro described SRS1 and create() threw, pointing at a function that was never exported. b.safeArchive gains extractToMemory, the in-memory counterpart to extract for read-only / serverless filesystems — previously the card advertised in-memory extraction but the orchestrator required a destination directory. b.auth.oid4vp.matchDcql now honours a null claims-path segment as the array wildcard the OpenID4VP DCQL spec defines, rather than refusing it as unsupported while the card advertised DCQL. A stale version-pinned wording in a safe-archive error message is corrected. Every change is additive or message-only — no existing caller changes behaviour. **Added:** *`b.mail.srs` SRS1 chained forwarding — `srs1Rewrite`* — `b.mail.srs.create(...)` now returns `srs1Rewrite` alongside `rewrite` / `reverse`. `srs1Rewrite(srsAddress)` chains an already-SRS0 (or SRS1) envelope-from for a further forwarding hop: it keeps the original SRS0 body verbatim, prepends the SRS0 originator's domain, and binds the pair with this forwarder's own HMAC-SHA-256 tag — no new timestamp, no repeated original local-part — emitting `SRS1=tag=originator==<SRS0-body>@thisForwarder`. `reverse()` now detects an SRS1 address, verifies this hop's tag and forwarder-domain binding, and unwraps exactly one hop back to the originator's SRS0 so a multi-hop bounce routes straight to the forwarder that can recover the original sender. Typed failure modes: `srs/not-srs0` (input not SRS-encoded), `srs/malformed` (missing the `==` separator), `srs/bad-tag` (tampered), `srs/too-long` (chain exceeds the RFC 5321 256-octet path limit). Implements the Sender Rewriting Scheme SRS1 wire format; the second-hop SPF rationale is RFC 7208 §2.4. · *`b.safeArchive.extractToMemory` — in-memory safe extraction* — An async generator counterpart to `b.safeArchive.extract` for read-only / serverless filesystems: it resolves the source, sniffs the format, auto-unwraps recipient (`BAWRP`) / passphrase (`BAWPP`) envelopes, and dispatches to the zip / tar / tar.gz reader's in-memory `extractEntries()`, yielding `{ name, bytes, size }` per regular-file entry without ever writing to disk. It takes no `destination`. Every defense the disk path runs applies unchanged: the zip-bomb caps (entry-count / per-entry / total / expansion-ratio), the `b.guardArchive` metadata cascade (Zip-Slip / path-traversal / symlink-escape / encrypted-entry refusal, CVE-2025-3445 class), and the entry-type policy. The disk-only realpath-agreement check (CVE-2025-4517 PATH_MAX TOCTOU defense) is intentionally absent — there is no extraction root — so the archive-level name refusals carry containment. Trusted-stream sources are refused upfront (the adversarial-safe central-directory walk needs random access). gzip magic per RFC 1952 §2.3.1. **Fixed:** *OID4VP DCQL `null` claim-path segment now resolves the array wildcard* — `b.auth.oid4vp.matchDcql` previously threw `auth-oid4vp/null-path-segment-not-supported` for a `null` claims-path segment while the namespace card advertised DCQL — under-disclosing a legitimate presentation (CWE-863). Per OpenID4VP 1.0 §7.1.1 a `null` segment selects all elements of the array at that depth; the matcher now recurses over array elements with existence semantics (with DCQL value-matching applied to any selected leaf), composed to arbitrary depth. A `null` segment on a non-array node — like an integer index into a non-array, or a string key into an array — is a clean non-match, not a thrown error, because the matcher walks holder credential data rather than operator config. String and integer claim paths are byte-identical to before; only queries that previously threw now succeed or fail cleanly. · *safe-archive trusted-stream refusal message no longer cites a stale version* — The thrown `safe-archive/trusted-stream-unsupported` message and its comment claimed trusted-stream extraction was "deferred to v0.12.8 / when the v0.12.8 sequential extract path lands." That path shipped long ago — `b.archive.read.zip.fromTrustedStream` and the tar sequential mode exist — so the message now points at them as present capabilities and drops the version-pinned wording. The error code is unchanged. **Detectors:** *A primitive may not advertise a capability and then throw an unimplemented stub* — A new check flags a bare `not yet supported` / `operator demand TBD` / `not supported in v1` refusal in a lib throw string (comments excluded). A defer is only complete with a written re-open condition; the SRS1 and DCQL stubs that this release implements both carried this bare-defer shape, and the detector keeps it from re-entering. · *DCQL `null` path segments must recurse, never refuse* — A new check flags the `null path segment not supported` refusal shape in `lib/auth/oid4vp.js`, so the spec-mandated array wildcard cannot be re-stubbed. · *`extractToMemory` must stay disk-free* — A new check flags any `writeFileSync` / `renameSync` / `mkdirSync` / `createWriteStream` inside the `extractToMemory` generator body, so the read-only / serverless contract cannot regress into a disk write.
|
package/lib/app.js
CHANGED
|
@@ -93,6 +93,7 @@ var nodeFs = require("node:fs");
|
|
|
93
93
|
var nodePath = require("node:path");
|
|
94
94
|
var appShutdown = require("./app-shutdown");
|
|
95
95
|
var audit = require("./audit");
|
|
96
|
+
var validateOpts = require("./validate-opts");
|
|
96
97
|
var C = require("./constants");
|
|
97
98
|
var cluster = require("./cluster");
|
|
98
99
|
var db = require("./db");
|
|
@@ -139,6 +140,9 @@ async function createApp(opts) {
|
|
|
139
140
|
if (!opts.dataDir || typeof opts.dataDir !== "string") {
|
|
140
141
|
throw new Error("createApp: opts.dataDir is required");
|
|
141
142
|
}
|
|
143
|
+
// Constructor-time default port (used by listen() when listenOpts.port is
|
|
144
|
+
// omitted); allowZero for the ephemeral-bind sentinel.
|
|
145
|
+
validateOpts.optionalPort(opts.port, "createApp: opts.port", undefined, undefined, { allowZero: true });
|
|
142
146
|
var dataDir = nodePath.resolve(opts.dataDir);
|
|
143
147
|
if (!nodeFs.existsSync(dataDir)) {
|
|
144
148
|
nodeFs.mkdirSync(dataDir, { recursive: true });
|
|
@@ -279,6 +283,10 @@ async function createApp(opts) {
|
|
|
279
283
|
|
|
280
284
|
function listen(listenOpts) {
|
|
281
285
|
listenOpts = listenOpts || {};
|
|
286
|
+
// Port 0 is the legitimate ephemeral-bind sentinel for a listen socket
|
|
287
|
+
// (RFC 6335 §6 / POSIX bind), so allowZero — but a non-integer / NaN /
|
|
288
|
+
// out-of-range port is an operator typo that must fail at boot.
|
|
289
|
+
validateOpts.optionalPort(listenOpts.port, "createApp.listen: listenOpts.port", undefined, undefined, { allowZero: true });
|
|
282
290
|
var port = (listenOpts.port !== undefined) ? listenOpts.port
|
|
283
291
|
: (opts.port !== undefined) ? opts.port
|
|
284
292
|
: 0;
|
package/lib/mail.js
CHANGED
|
@@ -767,6 +767,7 @@ function smtpTransport(opts) {
|
|
|
767
767
|
"dkimSigner must be an object with a .sign(rfc822) method " +
|
|
768
768
|
"(see b.mail.dkim.create)", true);
|
|
769
769
|
}
|
|
770
|
+
validateOpts.optionalPort(opts.port, "smtp transport: opts.port", MailError, "mail/smtp-misconfigured");
|
|
770
771
|
var port = opts.port || 587;
|
|
771
772
|
var useImplicitTLS = port === 465 || opts.implicitTls === true;
|
|
772
773
|
var rejectUnauthorized = opts.rejectUnauthorized !== false;
|
package/lib/network-dns.js
CHANGED
|
@@ -253,6 +253,7 @@ function useDnsOverTls(opts) {
|
|
|
253
253
|
opts = opts || {};
|
|
254
254
|
validateOpts(opts, ["host", "port", "servername", "ca"], "dns.useDnsOverTls");
|
|
255
255
|
validateOpts.requireNonEmptyString(opts.host, "dns.useDnsOverTls: host", DnsError, "dns/bad-dot-host");
|
|
256
|
+
validateOpts.optionalPort(opts.port, "dns.useDnsOverTls: opts.port", DnsError, "dns/bad-dot-port");
|
|
256
257
|
if (opts.ca !== undefined && opts.ca !== null &&
|
|
257
258
|
!Buffer.isBuffer(opts.ca) && typeof opts.ca !== "string" && !Array.isArray(opts.ca)) {
|
|
258
259
|
throw new DnsError("dns/bad-dot-ca",
|
package/lib/network-nts.js
CHANGED
|
@@ -241,6 +241,7 @@ function performKeHandshake(opts) {
|
|
|
241
241
|
opts = opts || {};
|
|
242
242
|
validateOpts(opts, ["host", "port", "servername", "aead", "ca", "timeoutMs"], "nts.performKeHandshake");
|
|
243
243
|
validateOpts.requireNonEmptyString(opts.host, "nts.performKeHandshake: host", NtsError, "nts/bad-host");
|
|
244
|
+
validateOpts.optionalPort(opts.port, "nts.performKeHandshake: opts.port", NtsError, "nts/bad-ke-port");
|
|
244
245
|
var timeoutMs = opts.timeoutMs || C.TIME.seconds(10);
|
|
245
246
|
return new Promise(function (resolve, reject) {
|
|
246
247
|
var settled = false;
|
|
@@ -408,6 +409,7 @@ function _walkExtensions(msg, startOff) {
|
|
|
408
409
|
function querySingle(opts) {
|
|
409
410
|
opts = opts || {};
|
|
410
411
|
validateOpts(opts, ["host", "port", "aeadId", "c2sKey", "s2cKey", "cookies", "timeoutMs"], "nts.querySingle");
|
|
412
|
+
validateOpts.optionalPort(opts.port, "nts.querySingle: opts.port", NtsError, "nts/bad-ntp-port");
|
|
411
413
|
if (!Buffer.isBuffer(opts.c2sKey) || opts.c2sKey.length === 0) {
|
|
412
414
|
throw new NtsError("nts/no-c2s-key", "nts.querySingle: c2sKey required (Buffer)");
|
|
413
415
|
}
|
|
@@ -542,6 +544,8 @@ function querySingle(opts) {
|
|
|
542
544
|
async function query(opts) {
|
|
543
545
|
opts = opts || {};
|
|
544
546
|
validateOpts(opts, ["host", "kePort", "ntpPort", "aead", "ca", "timeoutMs", "servername"], "nts.query");
|
|
547
|
+
validateOpts.optionalPort(opts.kePort, "nts.query: opts.kePort", NtsError, "nts/bad-ke-port");
|
|
548
|
+
validateOpts.optionalPort(opts.ntpPort, "nts.query: opts.ntpPort", NtsError, "nts/bad-ntp-port");
|
|
545
549
|
var ke = await performKeHandshake({
|
|
546
550
|
host: opts.host,
|
|
547
551
|
port: opts.kePort,
|
package/lib/ntp-check.js
CHANGED
|
@@ -48,10 +48,16 @@ var dgram = require("node:dgram");
|
|
|
48
48
|
var C = require("./constants");
|
|
49
49
|
var lazyRequire = require("./lazy-require");
|
|
50
50
|
var safeAsync = require("./safe-async");
|
|
51
|
+
var validateOpts = require("./validate-opts");
|
|
52
|
+
var { defineClass } = require("./framework-error");
|
|
51
53
|
|
|
52
54
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
53
55
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
54
56
|
|
|
57
|
+
// Config-time misuse (a bad opts.port) throws a typed, permanent error so an
|
|
58
|
+
// operator catches the typo at boot rather than as a Promise rejection.
|
|
59
|
+
var NtpCheckError = defineClass("NtpCheckError", { alwaysPermanent: true });
|
|
60
|
+
|
|
55
61
|
// NTP epoch: 1900-01-01. Unix epoch: 1970-01-01. Offset: 70 years incl. 17
|
|
56
62
|
// leap days = 2,208,988,800 seconds.
|
|
57
63
|
var NTP_TO_UNIX_OFFSET_SECONDS = 2208988800;
|
|
@@ -166,6 +172,7 @@ function _resetThresholdsForTest() {
|
|
|
166
172
|
*/
|
|
167
173
|
function querySingle(server, opts) {
|
|
168
174
|
opts = opts || {};
|
|
175
|
+
validateOpts.optionalPort(opts.port, "ntpCheck.querySingle: opts.port", NtpCheckError, "ntp/bad-port");
|
|
169
176
|
var port = opts.port || DEFAULT_PORT;
|
|
170
177
|
var timeoutMs = opts.timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
171
178
|
|
|
@@ -445,6 +452,7 @@ function monitor(opts) {
|
|
|
445
452
|
|
|
446
453
|
module.exports = {
|
|
447
454
|
querySingle: querySingle,
|
|
455
|
+
NtpCheckError: NtpCheckError,
|
|
448
456
|
checkDrift: checkDrift,
|
|
449
457
|
bootCheck: bootCheck,
|
|
450
458
|
monitor: monitor,
|
package/lib/redis-client.js
CHANGED
|
@@ -155,9 +155,17 @@ function _frameToValue(frame) {
|
|
|
155
155
|
function create(opts) {
|
|
156
156
|
opts = opts || {};
|
|
157
157
|
validateOpts.requireNonEmptyString(opts.url, "redis.create: opts.url", RedisError, "BAD_OPTS");
|
|
158
|
+
// Validate an operator-supplied opts.port up front for a clear typo
|
|
159
|
+
// message (e.g. the string "6379" or a negative value).
|
|
160
|
+
validateOpts.optionalPort(opts.port, "redis.create: opts.port", RedisError, "BAD_OPTS");
|
|
158
161
|
var parsed = _parseRedisUrl(opts.url);
|
|
159
162
|
var host = opts.host || parsed.host;
|
|
160
163
|
var port = opts.port || parsed.port;
|
|
164
|
+
// Re-validate the RESOLVED port. A url-supplied port (redis://h:0,
|
|
165
|
+
// redis://h:99999) is not range-checked by _parseRedisUrl, so without
|
|
166
|
+
// this an outbound connect could inherit a zero / out-of-range port that
|
|
167
|
+
// the opts.port guard above never sees.
|
|
168
|
+
validateOpts.optionalPort(port, "redis.create: resolved port (opts.port or url)", RedisError, "BAD_OPTS");
|
|
161
169
|
var useTls = opts.tls !== undefined ? !!opts.tls : parsed.tls;
|
|
162
170
|
var password = opts.password !== undefined ? opts.password : parsed.password;
|
|
163
171
|
var username = opts.username !== undefined ? opts.username : parsed.username;
|
package/lib/validate-opts.js
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
* a typed error wrap the call.
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
+
var numericBounds = require("./numeric-bounds");
|
|
31
|
+
|
|
30
32
|
function _format(primitive, unknownKey, allowedKeys) {
|
|
31
33
|
return primitive + ": unknown option '" + unknownKey + "'. " +
|
|
32
34
|
"Allowed keys: " + allowedKeys.slice().sort().join(", ") + ".";
|
|
@@ -150,6 +152,26 @@ function optionalFunction(value, label, errorClass, code) {
|
|
|
150
152
|
return value;
|
|
151
153
|
}
|
|
152
154
|
|
|
155
|
+
// optionalPort — a TCP/UDP port number must be an integer in the wire-valid
|
|
156
|
+
// range (RFC 6335 §6). Outbound-connect sites require [1,65535]; pass
|
|
157
|
+
// { allowZero: true } for a listen-bind site where port 0 is the legitimate
|
|
158
|
+
// ephemeral-bind sentinel the OS replaces with a kernel-assigned port. Uses
|
|
159
|
+
// numericBounds.shape() in the message so Infinity / NaN / "443" stay visible.
|
|
160
|
+
function optionalPort(value, label, errorClass, code, opts) {
|
|
161
|
+
if (value === undefined || value === null) return value;
|
|
162
|
+
opts = opts || {};
|
|
163
|
+
var ok = opts.allowZero
|
|
164
|
+
? (numericBounds.isNonNegativeFiniteInt(value) && value <= 65535)
|
|
165
|
+
: (numericBounds.isPositiveFiniteInt(value) && value <= 65535);
|
|
166
|
+
if (!ok) {
|
|
167
|
+
_throw(errorClass, code, (label || "opt") + " must be " +
|
|
168
|
+
(opts.allowZero ? "0 (ephemeral) or " : "") +
|
|
169
|
+
"an integer in [" + (opts.allowZero ? 0 : 1) + ",65535], got " + numericBounds.shape(value),
|
|
170
|
+
"validate-opts/bad-port");
|
|
171
|
+
}
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
|
|
153
175
|
// applyDefaults — resolve every key in DEFAULTS against opts. For each
|
|
154
176
|
// key, the operator's value (if not undefined) wins; otherwise the
|
|
155
177
|
// default is used. Returns a new plain object — NOT a frozen one, so
|
|
@@ -391,6 +413,7 @@ module.exports.optionalBoolean = optionalBoolean;
|
|
|
391
413
|
module.exports.optionalPositiveInt = optionalPositiveInt;
|
|
392
414
|
module.exports.optionalFiniteNonNegative = optionalFiniteNonNegative;
|
|
393
415
|
module.exports.optionalPositiveFinite = optionalPositiveFinite;
|
|
416
|
+
module.exports.optionalPort = optionalPort;
|
|
394
417
|
module.exports.optionalFunction = optionalFunction;
|
|
395
418
|
module.exports.optionalNonEmptyString = optionalNonEmptyString;
|
|
396
419
|
module.exports.optionalNonEmptyStringArray = optionalNonEmptyStringArray;
|
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.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:44556dbf-5411-4644-ab81-5f0571d5e036",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-
|
|
8
|
+
"timestamp": "2026-06-01T02:09:53.597Z",
|
|
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.14.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.14.16",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.14.
|
|
25
|
+
"version": "0.14.16",
|
|
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.14.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.14.16",
|
|
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.14.
|
|
57
|
+
"ref": "@blamejs/core@0.14.16",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|