@blamejs/core 0.8.77 → 0.8.78
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 +1 -0
- package/lib/config.js +39 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,7 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.8.x
|
|
10
10
|
|
|
11
|
+
- v0.8.78 (2026-05-10) — save-triggered reload for `b.config.loadDbBacked`. Admin save handlers / settings-management UIs that write a row in `_blamejs_config_overrides` now call `await cfg.refresh()` immediately after the write, so the new value is active without waiting for the poll's `intervalMs` tick. The poll stays in place as a safety-net for drift (e.g., direct DB writes outside the admin path). `refresh()` returns a `Promise<void>` of identical shape to `cfg.hydrated`: resolves after the tick settles (success OR audit-on-failure), NEVER rejects so save handlers don't deadlock on a flaky DB. The existing `cfg.subscribe(fn)` continues to fire synchronously inside every successful reload — operators reach for it to invalidate caches / recompute derived state / hot-rebuild middleware that closed over the previous config. Three-tier precedence is documented explicitly in the `@primitive` block: DB-row overlay > `opts.env` baseline > schema `default(...)`.
|
|
11
12
|
- v0.8.77 (2026-05-10) — substantive additive release closing 10 audit clusters surfaced by the 8-agent compliance audit. **OAuth resource-server completeness**: `b.auth.oauth.introspectToken` (RFC 7662), `registerClient` (RFC 7591 — refuses empty redirect_uris), `deviceAuthorization` + `pollDeviceCode` (RFC 8628 with slow_down/authorization_pending handling), `exchangeToken` (RFC 8693 subject+actor delegation), new `b.middleware.protectedResourceMetadata` serving `.well-known/oauth-protected-resource` (draft-ietf-oauth-resource-metadata). **Vendored-deps SBOM**: new `scripts/build-vendored-sbom.js` emits `sbom.vendored.cdx.json` (CycloneDX 1.6) covering every `lib/vendor/*` bundle with per-file SHA-256 + purl + license metadata; wired into `npm-publish.yml` so OSV-Scanner now scans it alongside the primary `sbom.cdx.json` — closes the gap where downstream scanners couldn't see what was actually shipping. **MCP endpoint coverage**: `b.mcp.assertProtocolVersion` (MCP 2025-11-25 §4.1 header), `b.mcp.sampling.guard({ maxRequestsPerSession, maxMessagesPerRequest, maxTokensPerRequest, allowedModelHints })` (HIGH-RISK endpoint — confused-deputy class), `b.mcp.elicitation.guard` (prompt-injection scan + schema-type allowlist + size cap). **ACME completeness**: `revokeCert` (RFC 8555 §7.6), `accountKeyRollover` (§7.3.5), `deactivateAccount` (§7.3.6), `tlsAlpn01KeyAuthorization` (RFC 8737), External Account Binding opt on `newAccount` (§7.3.4 — required by ZeroSSL/Buypass/Google CA) — closes 47-day CA/B forum surface before Mar 2026 effective date. **Permissions-Policy denylist** expanded with `identity-credentials-get`, `attribution-reporting-cross-site`, `publickey-credentials-create`, `join-ad-interest-group`, `run-ad-auction`, `shared-storage`, `shared-storage-select-url`, `smartcard`, `all-screens-capture`, `deferred-fetch` (10 directives — single-file fix). **NIST control crosswalk**: new `b.nistCrosswalk` catalog mapping `800-53r5` (~50 controls), `csf-2.0` (~22 functions), `800-171r3` (~25 requirements), `800-218` (SSDF tasks) to framework primitives — used by operators producing SSPs, POAMs, ATO packages, CMMC self-assessments. **SCIM 2.0 server**: new `b.middleware.scimServer` implementing RFC 7642/7643/7644 — Users + Groups + ServiceProviderConfig + ResourceTypes + Schemas + filter parser (eq/ne/co/sw/ew/pr/gt/ge/lt/le) + GET/POST/PUT/PATCH/DELETE dispatch + bearer-auth callback hook + 1 MiB body cap; the most operator-visible federation gap before this — Okta/Entra/etc. couldn't push users without an external adapter. **CRA + EU AI Act forward-deadline templates**: `b.cra.conformityAssessment` Annex VIII technical dossier scaffold (CE marking, Module routing, vuln-handling auto-fill), `b.complianceAiAct.fundamentalRightsImpactAssessment` (Article 27 FRIA template — mandatory for Annex III §5-8 deployers), `b.complianceAiAct.gpai.trainingDataSummary` (Article 53(1)(d) AI Office template — mandatory 2026-08-02). **C2PA COSE_Sign1 wrap**: new `b.contentCredentials.signCose` produces RFC 9052 COSE_Sign1 CBOR envelope with x5chain header + ML-DSA-87 / ed25519 / es256/384/512 / SLH-DSA-SHAKE-256f algorithms — interops with c2patool / JPEG Trust / Adobe verifiers (current `sign()` ships a blamejs-internal envelope; the new `signCose()` ships the canonical wire format). **US state-law backlog**: 22 new compliance postures (`vcdpa`, `co-cpa`, `ctdpa`, `ucpa`, `tdpsa`, `or-cpa`, `mt-cdpa`, `ia-icdpa`, `in-indpa`, `de-dpdpa`, `nh-nhpa`, `nj-njdpa`, `ky-kcdpa`, `tn-tipa`, `mn-mncdpa`, `ri-ricpa`, `ne-dpa`, `nv-sb370`, `ca-aadc`, `ct-sb3`, `tx-cubi`, plus existing `modpa` + `quebec-25`) registered in `b.compliance` + per-state DSR rules via `b.dsr.stateRules(state)` / `b.dsr.listStateRules()` returning `{ responseDays, extensionDays, cureDays, profilingOptOut, minorOptIn, notes }`. **Operator hook**: `b.middleware.rateLimit` instance gains `.resetAll()` for clean-slate flushing during incident-response (in-memory backends only; cluster backend no-ops per multi-replica race-safety). Cluster backend correctly refuses lest one replica's flush race another's in-flight `take()`. **`b.config.loadDbBacked` gains `transformValue: (row) => string | Promise<string>`** — per-row transform applied between `fetchRows` and schema validation; common shape is unsealing a `b.vault`-sealed ciphertext column so canonical secrets live encrypted-at-rest in `_blamejs_config_overrides`. Per-row failures (transform throws OR returns non-string) emit `config.reload.failed` and skip the row so a single bad row can't crash the poller. **`b.cryptoField` gains `sealDoc` / `unsealDoc` doc-shaped aliases** of the existing `sealRow` / `unsealRow` — same identity, lets downstream tests reach for the document-naming convention when preparing seed objects via raw `INSERT`. **Bug fix — `b.config` reactive `value`**: `cfg.value.X` now reflects the latest validated state after every `reload()` (and every `loadDbBacked` poll). Before this fix, `cfg.value` was a captured property pinned to the create-time object, so `cfg.value.FEATURE_X` stayed stale forever and only `cfg.get("FEATURE_X")` saw updates — the published example in `@primitive b.config.loadDbBacked` was wrong against the implementation. Now backed by a `Object.defineProperty` getter; `cfg.get()` / `cfg.has()` semantics unchanged. **Bug fix — `b.config.loadDbBacked` startup hydration window**: `loadDbBacked` returned a config handle that stayed at env-only defaults for the first `intervalMs` because `safeAsync.repeating` is `setInterval`-shaped (no t=0 fire). The handle now kicks off one immediate hydration `_tick()` on construction and exposes `cfg.hydrated` — a Promise that resolves after the first tick settles. Callers awaiting it before serving traffic get a fully-hydrated config; the Promise NEVER rejects (per-tick failures route through audit, last-good value stays). **`b.middleware._modules.rateLimit.instances()` + module-level `.resetAll()`** — module now keeps a registry of every rate-limit middleware created in the process. Incident-response scripts can enumerate every limiter and flush state across the whole process without threading references through the app code. `create()` registers; `middleware.close()` deregisters. Top-level `resetAll()` returns the count of instances it walked.
|
|
12
13
|
- v0.8.76 (2026-05-10) — CI green-up for v0.8.75. The OSV-Scanner v2 binary refuses to parse SBOMs whose filename doesn't match the CycloneDX recognized-pattern spec — `sbom.cyclonedx.json` is NOT recognized; only `bom.json` / `*.cdx.json` / `*.spdx.json` etc. are. v0.8.75's npm-publish workflow failed with `Failed to parse SBOM "sbom.cyclonedx.json": Invalid SBOM filename`. Renamed the artifact to `sbom.cdx.json` everywhere (workflow generation step, post-process script, OSV scan target, cosign sign target, GH release asset upload, `package.json` `files` array, `scripts/check-pack-against-gitignore.js` allowlist, `.gitignore` allowlist). No primitive surface change versus v0.8.75; published-tarball asset filename changes from `sbom.cyclonedx.json` to `sbom.cdx.json` (consumers reading the SBOM out of the install tree should update the path).
|
|
13
14
|
- v0.8.75 (2026-05-10) — CI green-up for v0.8.73 + v0.8.74. The OSV-Scanner action's v2.3.5 binary removed the `--fail-on-vuln=<severity>` flag; passing it now errors with `flag provided but not defined: -fail-on-vuln` and the entire npm-publish workflow exits 1 before `npm publish` ever runs. v0.8.73 + v0.8.74's npm-publish workflows both failed for this reason (Dependabot bumped osv-scanner-action 2.0.2 → 2.3.5 in PR #8 alongside the v2 flag removal; the workflow was never re-tested under the new binary). v2's default behavior is exit-1-on-ANY-finding — stricter than the v1 `--fail-on-vuln=HIGH` floor, and appropriate for a zero-npm-runtime-dep framework where any surfaced vuln means a vendor refresh is overdue. The framework currently has no findings, so the stricter floor is a no-op at HEAD. No primitive surface change versus v0.8.74.
|
package/lib/config.js
CHANGED
|
@@ -232,8 +232,18 @@ function create(opts) {
|
|
|
232
232
|
*
|
|
233
233
|
* The returned handle is the same shape as `create()` plus:
|
|
234
234
|
* - `.hydrated` — Promise<void> for the first tick
|
|
235
|
+
* - `.refresh()`— run one tick on demand (save-triggered reload);
|
|
236
|
+
* returns Promise<void> that never rejects
|
|
235
237
|
* - `.stop()` — halts the poller
|
|
236
238
|
*
|
|
239
|
+
* Three tiers of precedence (highest wins): the DB-row overlay
|
|
240
|
+
* resolved at each `_tick` > the `opts.env` baseline > defaults
|
|
241
|
+
* declared on the schema (`s.string().default(...)` and friends).
|
|
242
|
+
* The `.subscribe(fn)` callback registered through `create()` fires
|
|
243
|
+
* synchronously inside every successful reload — operators reach for
|
|
244
|
+
* it to invalidate caches, recompute derived state, or hot-rebuild
|
|
245
|
+
* middleware that closed over the previous config value.
|
|
246
|
+
*
|
|
237
247
|
* @opts
|
|
238
248
|
* schema: b.safeSchema instance (required),
|
|
239
249
|
* env: object (env baseline; default process.env),
|
|
@@ -277,6 +287,25 @@ function create(opts) {
|
|
|
277
287
|
* },
|
|
278
288
|
* intervalMs: 30 * 1000,
|
|
279
289
|
* });
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* // Save-triggered reload — admin UI writes a row, fires refresh()
|
|
293
|
+
* // so the new value is active immediately without waiting for
|
|
294
|
+
* // intervalMs. cfg.subscribe(...) sees the change inline.
|
|
295
|
+
* var cfg = b.config.loadDbBacked({
|
|
296
|
+
* schema: s.object({ FEATURE_X: b.config.coerce.boolean().default(false) }),
|
|
297
|
+
* fetchRows: async function () { return await db.all("SELECT key, value FROM _config"); },
|
|
298
|
+
* intervalMs: 5 * 60 * 1000, // safety-net interval
|
|
299
|
+
* });
|
|
300
|
+
* await cfg.hydrated; // boot path waits
|
|
301
|
+
* cfg.subscribe(function (next) { cache.invalidate(); });
|
|
302
|
+
*
|
|
303
|
+
* adminApp.post("/settings", async function (req, res) {
|
|
304
|
+
* await db.run("INSERT OR REPLACE INTO _config(key,value) VALUES (?,?)",
|
|
305
|
+
* req.body.key, req.body.value);
|
|
306
|
+
* await cfg.refresh(); // active immediately
|
|
307
|
+
* res.json({ ok: true });
|
|
308
|
+
* });
|
|
280
309
|
*/
|
|
281
310
|
function loadDbBacked(opts) {
|
|
282
311
|
opts = opts || {};
|
|
@@ -357,6 +386,16 @@ function loadDbBacked(opts) {
|
|
|
357
386
|
// the established "last-good config stays in place" contract.
|
|
358
387
|
cfg.hydrated = _tick();
|
|
359
388
|
var handle = safeAsync.repeating(_tick, opts.intervalMs, { name: "config-db-reload" });
|
|
389
|
+
// Save-triggered reload — admin save handlers / settings-management
|
|
390
|
+
// UIs invoke cfg.refresh() right after writing a row to drop the
|
|
391
|
+
// intervalMs-worth of staleness latency between save and active.
|
|
392
|
+
// Returns the same Promise<void> shape as cfg.hydrated: resolves
|
|
393
|
+
// after the tick settles (success OR audit-on-failure), never
|
|
394
|
+
// rejects so the save handler never deadlocks on a flaky DB.
|
|
395
|
+
// Subscribers fire synchronously inside cfg.reload() within the
|
|
396
|
+
// tick, matching the save-then-invalidate-cache pattern operators
|
|
397
|
+
// expect when an admin flips a feature flag.
|
|
398
|
+
cfg.refresh = function () { return _tick(); };
|
|
360
399
|
cfg.stop = function () { stopped = true; if (handle) { handle.stop(); handle = null; } };
|
|
361
400
|
return cfg;
|
|
362
401
|
}
|
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:77cdedbf-1ccf-45c3-afec-2d0261ed59ce",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-11T03:18
|
|
8
|
+
"timestamp": "2026-05-11T03:48:18.757Z",
|
|
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.8.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.8.78",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.8.
|
|
25
|
+
"version": "0.8.78",
|
|
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.8.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.8.78",
|
|
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.8.
|
|
57
|
+
"ref": "@blamejs/core@0.8.78",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|