@blamejs/core 0.6.21 → 0.6.22

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 CHANGED
@@ -8,6 +8,7 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.6.x
10
10
 
11
+ - **0.6.22** (2026-05-02) — `b.queue.enqueue({ availableAt })` is now honoured. Previously the local-protocol enqueue() ignored opts.availableAt entirely, recomputing from `Date.now() + delaySeconds*1000`. The cron-repeat path passes both fields (the exact next-fire ms in availableAt + the floored seconds in delaySeconds), and the enqueue's recomputation lost sub-second precision plus drifted on the internal clock-vs-caller delta. Symptoms: cron-scheduled jobs landed up to 999ms off the intended boundary; the queue-flow-repeat smoke test was intermittently flaky on slow CI runners (caught by ubuntu-latest on the v0.6.21 commit). Fix: enqueue() honours opts.availableAt directly when finite; falls back to delaySeconds-based shorthand otherwise. Operators relying on `enqueue({ availableAt: T })` for non-cron scheduled jobs (e.g. "deliver this notification at exactly 09:00 tomorrow") now get the requested time instead of nowMs+0.
11
12
  - **0.6.21** (2026-05-02) — closes the medium / low audit findings flagged after v0.6.18. **Cluster cache `invalidateTag`** now actually works — the cluster backend gained a `_blamejs_cache_tags` junction table (`(cacheKey, tag)` PK + index on `tag`), tag-aware `set` / `del` / `clear` / `_sweep`, plus `getTags(key)`. The old NOT_SUPPORTED-on-cluster path is gone; multi-tag rotation, mid-flight tag replacement on update, and namespace-scoped sweeps all covered. **Multi-column cursor pagination** — `b.pagination.cursor({ orderBy: [{column,direction},...] })` accepts a string (single column), an array of strings (multi, all using `opts.direction`), or an array of `{column,direction}` objects (mixed directions). The keyset WHERE expands to the standard OR cascade so successive pages can't skip or repeat rows when ties on the leading columns are broken by trailing ones. `_id` is appended as a tiebreaker if not in the chain. The Query class also gained chained `orderBy(col, dir)` calls — second-and-later calls extend a multi-column ORDER BY in the SQL. Cursor format is bumped to encode `{ orderKey, vals, forward }` instead of the old `{ orderBy, dir, orderByVal, id, forward }` — pre-1.0 break, no compat shim. **DoH POST mode** (RFC 8484 §4.1) — `b.network.dns.useDnsOverHttps({ method })` accepts `"GET" | "POST" | undefined` (auto). Auto switches to POST when the GET URL would exceed 2048 bytes (long DNS names). **DoT connection pooling** — per-`(host:port)` cached TLS socket with a 2-minute idle timeout, serialized in-flight queries per socket. Eliminates the per-query handshake. **INI parser** shipped as `b.parsers.ini` — covers Windows .ini / .gitconfig / systemd-unit / php.ini / tox.ini shape: sections (incl. `[parent.child]` / `[parent "child"]` nesting), `;` and `#` comments (inline + leading), single + double quoting with `\n` `\t` `\\` `\"` `\'` escapes, boolean coercion (`true`/`false`/`yes`/`no`/`on`/`off`), decimal + hex integers + floats. Prototype-pollution defense (`__proto__` / `constructor` / `prototype` rejected). Duplicate-key policy throws by default; `onDuplicate: "first" | "last"` opts in to silent shadowing. Section / per-section key / value-bytes caps configurable. **Cookie-jar file persistence** — `b.httpClient.cookieJar.create({ persist: "file", file: "/abs/path", vault: b.vault })` loads at construct, debounce-flushes on every set/clear, plus `flush()` and `close()` for explicit lifecycle. With `vault`, on-disk bytes are sealed; without, plaintext JSON (operator chooses). **Comment cleanups per rule §4** — `vault/index.js` stale `// later` removed, `mail.js` "future patch" wording rewritten as scope, `bundler.js` "What it does NOT do today (deliberately deferred)" rewritten as "Out of scope", `archive.js` "v1 scope cuts (deferred)" rewritten as "Out of scope", `framework-schema.js` "next release" wording rewritten to describe what's actually shipped, `http-client.js` "out of reach today" / "we'll plumb it through when h3 lands" rewritten as scope statements. **README CLI section** updated to reflect the v0.6.17 + v0.6.18 + v0.6.19 + v0.6.20 additions (security / config-drift / file-type / password / erase / retention) — was missing 6 subcommands.
12
13
  - **0.6.20** (2026-05-02) — CI / packaging fix-up. The npm-publish workflow's "Attach SBOM as release asset" step started failing with HTTP 422 ("Cannot upload assets to an immutable release") because the operator's manual `gh release create` had already been published when the workflow ran. Two changes: (1) `sbom.cyclonedx.json` is now bundled into the npm tarball (`files` block in package.json), so `npm install @blamejs/core && cat node_modules/@blamejs/core/sbom.cyclonedx.json` is the canonical SBOM access path. The prepack guard's known-allowed list covers the just-in-time generation. (2) The workflow's GH-release-attach step is now non-fatal: it tries to upload, logs a warning if the release is immutable, and lets the publish proceed regardless. The npm tarball is the load-bearing artifact; the GitHub release attachment was only ever supplementary. `.gitignore` adds `sbom.cyclonedx.json` so a stray local `npm sbom` doesn't pollute the repo.
13
14
  - **0.6.19** (2026-05-02) — closes the critical + high gaps surfaced by the v0.6.18 audit. **Critical**: `b.network.ntp.nts.querySingle` now actually verifies the server reply with the s2cKey — extracts the AUTHENTICATOR_AND_ENC extension, AEAD-decrypts with AAD = bytes-before-authenticator, fails closed (`nts/auth-failed` / `nts/no-authenticator`) when verification fails. Server-supplied new cookies in the encrypted plaintext are appended to the cookie pool and the consumed cookie popped (real RFC 8915 cookie rotation). Previously the function returned `authenticated: true` while only checking the unique-identifier echo — any MITM that mirrored the request's 32-byte unique field could spoof timestamps. **High**: `azure-blob.presignedUploadPolicy` now throws `PRESIGN_NOT_SUPPORTED` instead of silently returning a SAS PUT URL when operators asked for POST policy semantics — Azure SAS has no body-size cap and the previous shape was a misleading mismatch (operator error message points at presignedUploadUrl + post-upload HEAD as the alternative). `b.auth.password.policy` now ships the SecLists top-10000 common-password list bundled (CC-BY-3.0, `lib/vendor/common-passwords-top-10000.txt`), loaded lazily on first `policy.check()` call; `password` / `dragon` / `qwerty` / etc. now reject with `policy/forbidden-common`; `useBundledCommon: false` per-policy bypasses if operator ships their own list. `b.network.tls` adds `removeCa(fingerprint256)` / `removeCaByLabel(label)` / `clearAll()` / `purgeExpired()` / `expiringSoon(windowMs)` so operators can rotate corp DPI CAs without process restart; every removal audits with subject + fingerprint + reason. `b.network.dns.setResultOrder("ipv6first")` now flips the order on the DoH / DoT dual-stack fallback paths too (was only sorting OS-resolver results). `b.network.dns.resolve4` / `resolve6` / `resolveAaaa` now use real DNS-protocol queries (`dns.promises.resolve4` / `_dohLookup` / `_dotLookup`) instead of aliasing `lookup()` — operator semantics now match Node's standard library (skips `/etc/hosts`, mDNS). `b.network.socket.setDefaultLinger` removed from the silent no-op path; now throws `socket/linger-not-supported` with operator guidance to use `socket.destroy()` (abort) vs `socket.end()` (graceful) since Node's public `net.Socket` has no `setLinger()`. **Internal**: every `new XxxError(...)` call across `lib/network*.js` was passing args in the wrong order (message-then-code instead of code-then-message), making `e.code` return human messages and `e.message` return slash-codes — operators relying on `e.code` for error handling got the wrong field. All 50+ throws fixed across `lib/network.js` / `network-dns.js` / `network-proxy.js` / `network-tls.js` / `network-heartbeat.js` / `network-nts.js`. **CI**: `.gitleaks.toml` adds `test/smoke.js` to the path allowlist and pins the historical commit + fingerprint that tripped the jwt rule on a `REDACTED` placeholder; `npm-publish.yml` job permissions bumped from `contents: read` to `contents: write` so `gh release upload sbom.cyclonedx.json` no longer 403s.
@@ -109,7 +109,18 @@ function create(_config) {
109
109
  cluster.requireLeader();
110
110
  opts = opts || {};
111
111
  var nowMs = Date.now();
112
- var availableAt = nowMs + (opts.delaySeconds ? C.TIME.seconds(opts.delaySeconds) : 0);
112
+ // Caller-supplied opts.availableAt wins (cron-repeat pre-computes
113
+ // the exact next-fire ms). delaySeconds is the operator-shorthand
114
+ // alternative — used when opts.availableAt isn't provided. Falling
115
+ // through delaySeconds without honouring an explicit availableAt
116
+ // loses sub-second precision (delaySeconds is integer-floored)
117
+ // and drifts on the internal clock vs the caller's intended ms.
118
+ var availableAt;
119
+ if (typeof opts.availableAt === "number" && isFinite(opts.availableAt)) {
120
+ availableAt = opts.availableAt;
121
+ } else {
122
+ availableAt = nowMs + (opts.delaySeconds ? C.TIME.seconds(opts.delaySeconds) : 0);
123
+ }
113
124
 
114
125
  var priority = (typeof opts.priority === "number" && isFinite(opts.priority))
115
126
  ? Math.floor(opts.priority) : 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.6.21",
3
+ "version": "0.6.22",
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:8877106e-46ae-498c-b7a5-02870b9bb6c1",
5
+ "serialNumber": "urn:uuid:2dcbd08d-cbaf-408b-8b68-8982ea425a0f",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-02T07:06:01.257Z",
8
+ "timestamp": "2026-05-02T07:32:02.459Z",
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.6.21",
22
+ "bom-ref": "@blamejs/core@0.6.22",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.6.21",
25
+ "version": "0.6.22",
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.6.21",
29
+ "purl": "pkg:npm/%40blamejs/core@0.6.22",
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.6.21",
57
+ "ref": "@blamejs/core@0.6.22",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]