@blamejs/core 0.7.90 → 0.7.92

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,10 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.7.x
10
10
 
11
+ - **0.7.92** (2026-05-06) — Retention floors + observability semconv expansion. **`b.retention.complianceFloor(<posture>, candidateMs)`** now recognizes `nis2` (3 years — NIS2 Art. 23 incident reporting), `cra` (5 years — CRA Art. 14 vulnerability handling logs), `lgpd-br` (5 years — Brazil fiscal record minimum + LGPD Art. 16), `appi-jp` (3 years — Japan APPI handler-of-record), `pdpa-sg` (1 year — PDPA breach notification audit trail), and `uk-gdpr` (6 years — UK ICO guidance + statutory limit alignment). `gdpr` continues to have no fixed minimum (Art. 5(1)(e) is "no longer than necessary" — operator-driven). **`b.observability.SEMCONV`** gains RPC attributes (`RPC_SYSTEM` / `RPC_SERVICE` / `RPC_METHOD` / `RPC_GRPC_STATUS_CODE`), additional messaging keys (`MESSAGING_CLIENT_ID` / `MESSAGING_MESSAGE_ID` / `MESSAGING_DESTINATION_PARTITION_ID` / `MESSAGING_BATCH_MESSAGE_COUNT`), network transport (`NETWORK_TRANSPORT` / `NETWORK_CONNECTION_TYPE`), process / runtime identification (`PROCESS_PID` / `PROCESS_RUNTIME_NAME` / `PROCESS_RUNTIME_VERSION`), service identification (`SERVICE_NAME` / `SERVICE_VERSION` / `SERVICE_INSTANCE_ID`), and telemetry SDK self-id (`TELEMETRY_SDK_NAME` / `TELEMETRY_SDK_LANGUAGE` / `TELEMETRY_SDK_VERSION`). Operators wiring the framework's tap into a gRPC-fronted OTel collector or an outbox-fed Kafka topic now reference the canonical attribute names directly without an aliasing table on their side.
12
+
13
+ - **0.7.91** (2026-05-06) — Compliance-posture vocabulary expanded + OpenTelemetry semantic-convention attribute table. **`b.compliance.set(<posture>)`** now accepts thirteen new posture names: `wmhmda` (Washington My Health My Data Act), `bipa` (Illinois Biometric Information Privacy Act), `ccpa` (California Consumer Privacy Act), `nis2` (EU NIS2 Directive), `cra` (EU Cyber Resilience Act), `ai-act` (EU AI Act), `lgpd-br` (Brazil LGPD), `pipl-cn` (China PIPL), `appi-jp` (Japan APPI), `pdpa-sg` (Singapore PDPA), `pipeda-ca` (Canada PIPEDA), `uk-gdpr` (UK GDPR). Existing `hipaa` / `pci-dss` / `gdpr` / `soc2` / `dora` / `sox` continue to work. Postures map to per-primitive defaults via the existing compliancePosture opt on guards, retention, dora, etc. — operators set the deployment-wide posture once and primitives that key off it pick up the right defaults. **`b.observability.SEMCONV`** — frozen attribute-name table tracking the OpenTelemetry semantic-convention stable namespace (1.27+). HTTP server attributes (`http.request.method`, `http.response.status_code`, `http.route`, `server.address`, `client.address`), URL (`url.full`, `url.path`, `url.scheme`), database (`db.system`, `db.namespace`, `db.operation.name`, `db.query.text`), messaging (`messaging.system`, `messaging.destination.name`), auth (`user.id`, `session.id`), errors (`error.type`, `exception.type`, `exception.message`). Operators wiring the framework's tap into an OTel SDK reference these constants instead of hand-rolling the names — no aliasing table on the operator side, and string typos throw at access time instead of producing mis-named span attributes that the OTel collector silently drops.
14
+
11
15
  - **0.7.90** (2026-05-06) — `b.outbox` — transactional outbox primitive for at-least-once event publication without distributed transactions. **`b.outbox.create({ externalDb, table, publisher, ... })`** returns an outbox instance with three core operations: `enqueue(event, txn)` writes the outbox row inside the operator's transaction (using the `txClient` returned by `b.externalDb.transaction`), `start()` spins a polling publisher worker that claims rows via `SELECT ... FOR UPDATE SKIP LOCKED` (Postgres) and dispatches to the operator-supplied async `publisher(event)` callback, `stop()` gracefully shuts the worker down. Failed publishes retry with exponential backoff (`retryBackoff: { initialMs, maxMs, factor }`); rows that exceed `maxAttempts` are marked `'dead'` for operator triage and an `system.outbox.deadletter` audit event fires. Schema is operator-managed: `outbox.declareSchema(externalDb)` runs an idempotent `CREATE TABLE IF NOT EXISTS ... (id, topic, payload, key, headers, enqueued_at, next_attempt_at, published_at, attempts, last_error, status)` + a partial index on `(next_attempt_at) WHERE status = 'pending'`. `pendingCount()` / `deadCount()` expose the queue depth + DLQ depth for operator dashboards. Observability events on every state transition (`outbox.enqueued` / `outbox.published` / `outbox.publish-failed` / `outbox.dead-letter`).
12
16
 
13
17
  - **0.7.89** (2026-05-06) — Three additive primitives bundled: TUS resumable uploads, WebAuthn Signal API, DPoP server-issued nonce challenge. **`b.middleware.tusUpload({ mountPath, store, ... })`** implements the [tus.io](https://tus.io) v1.0.0 resumable-upload protocol — POST creates uploads, HEAD reports offsets, PATCH appends chunks, DELETE terminates. Supported extensions: `creation`, `creation-with-upload`, `expiration`, `checksum`, `termination`. The `checksum` extension defaults to PQC-first algorithms (`sha3-512`, `shake256`) — operators add classical algorithms explicitly via `checksumAlgorithms`. A built-in `b.middleware.tusUpload.memoryStore({ maxSize })` ships for development; production operators implement the `{ create, head, append, setLength, terminate, purgeExpired, getBuffer }` shape against their object-store backend. Bounded chunk collection routes through `safeBuffer.boundedChunkCollector` (cap-enforced at push time, no 10-GiB pre-collect). Concatenation extension (parallel-chunk assembly) deferred — operators that need it compose against their store layer; re-open if a store-layer-only solution proves insufficient. **`b.auth.passkey.signalUnknownCredential` / `signalAllAcceptedCredentials` / `signalCurrentUserDetails`** add the W3C WebAuthn Signal API descriptor builders — when the browser implements `PublicKeyCredential.signal*`, operators emit the matching JSON descriptor to clean up stale passkeys, refresh user details, and surface revocations without forcing a re-registration. All three validate `rpId` / `userId` / `credentialId` shape (base64url) and refuse `name`/`displayName` longer than 256 chars. **`b.middleware.dpop({ requireNonce: true, nonceRotateSec? })`** implements RFC 9449 §8 server-issued DPoP-Nonce challenge — the middleware emits `DPoP-Nonce: <fresh>` on every 401 response, refuses proofs whose `nonce` claim isn't in the rolling current+previous pair, and refreshes the nonce on every successful response. The rolling-pair manager rotates without timers (lazy maybe-rotate on access); no operator nonce store needed. The `getNonce` callback path stays intact for operator-managed nonce flows.
package/lib/compliance.js CHANGED
@@ -38,7 +38,29 @@ var audit = lazyRequire(function () { return require("./audit"); });
38
38
  // passing an unknown name get a typo-surfacing throw at set-time, not
39
39
  // silent fall-through to no-op.
40
40
  var KNOWN_POSTURES = Object.freeze([
41
- "hipaa", "pci-dss", "gdpr", "soc2", "dora", "sox",
41
+ // ---- US Federal / Sectoral ----
42
+ "hipaa", // Health Insurance Portability and Accountability Act
43
+ "pci-dss", // Payment Card Industry Data Security Standard
44
+ "soc2", // System and Organization Controls 2
45
+ "sox", // Sarbanes-Oxley
46
+ "wmhmda", // Washington My Health My Data Act (added 2026)
47
+ "bipa", // Illinois Biometric Information Privacy Act (added 2026)
48
+ // ---- US State Privacy ----
49
+ "ccpa", // California Consumer Privacy Act / CPRA (added 2026)
50
+ // ---- EU / EEA ----
51
+ "gdpr", // General Data Protection Regulation
52
+ "dora", // EU Digital Operational Resilience Act
53
+ "nis2", // EU Network and Information Security Directive 2 (added 2026)
54
+ "cra", // EU Cyber Resilience Act (added 2026)
55
+ "ai-act", // EU AI Act (added 2026)
56
+ // ---- Latin America / APAC ----
57
+ "lgpd-br", // Brazil Lei Geral de Proteção de Dados (added 2026)
58
+ "pipl-cn", // China Personal Information Protection Law (added 2026)
59
+ "appi-jp", // Japan Act on Protection of Personal Information (added 2026)
60
+ "pdpa-sg", // Singapore Personal Data Protection Act (added 2026)
61
+ // ---- Canada / UK ----
62
+ "pipeda-ca", // Canada Personal Information Protection and Electronic Documents Act (added 2026)
63
+ "uk-gdpr", // UK General Data Protection Regulation (added 2026)
42
64
  ]);
43
65
 
44
66
  var STATE = { posture: null, setAt: null };
@@ -143,9 +143,89 @@ function safeEvent(name, value, labels) {
143
143
  catch (_e) { /* hot-path observability sink — drops silent on internal throws */ }
144
144
  }
145
145
 
146
+ // OpenTelemetry semantic-convention attribute names — the operator-
147
+ // facing canonical vocabulary the framework's b.observability /
148
+ // b.tracing / b.metrics emitters use when building span / metric
149
+ // attributes. Tracking the OTel semconv stable namespace (1.27+)
150
+ // directly here means operators wiring the framework's tap into an
151
+ // OTel SDK don't need to maintain an aliasing table — the names are
152
+ // already correct.
153
+ //
154
+ // When operators call b.observability.event() / safeEvent(), they
155
+ // pass attribute keys that should match the keys below. The map is
156
+ // frozen — adding a new attribute requires a release.
157
+ //
158
+ // Reference: https://opentelemetry.io/docs/specs/semconv/general/attributes/
159
+ var SEMCONV = Object.freeze({
160
+ // HTTP server (stable per OTel semconv)
161
+ HTTP_REQUEST_METHOD: "http.request.method",
162
+ HTTP_REQUEST_BODY_SIZE: "http.request.body.size",
163
+ HTTP_RESPONSE_STATUS_CODE: "http.response.status_code",
164
+ HTTP_RESPONSE_BODY_SIZE: "http.response.body.size",
165
+ HTTP_ROUTE: "http.route",
166
+ // Server / network
167
+ SERVER_ADDRESS: "server.address",
168
+ SERVER_PORT: "server.port",
169
+ CLIENT_ADDRESS: "client.address",
170
+ CLIENT_PORT: "client.port",
171
+ NETWORK_PEER_ADDRESS: "network.peer.address",
172
+ NETWORK_PROTOCOL_NAME: "network.protocol.name",
173
+ NETWORK_PROTOCOL_VERSION: "network.protocol.version",
174
+ // URL
175
+ URL_FULL: "url.full",
176
+ URL_PATH: "url.path",
177
+ URL_QUERY: "url.query",
178
+ URL_SCHEME: "url.scheme",
179
+ // User agent
180
+ USER_AGENT_ORIGINAL: "user_agent.original",
181
+ // Database
182
+ DB_SYSTEM: "db.system",
183
+ DB_NAMESPACE: "db.namespace",
184
+ DB_OPERATION_NAME: "db.operation.name",
185
+ DB_QUERY_TEXT: "db.query.text",
186
+ // Messaging
187
+ MESSAGING_SYSTEM: "messaging.system",
188
+ MESSAGING_OPERATION: "messaging.operation",
189
+ MESSAGING_DESTINATION_NAME: "messaging.destination.name",
190
+ // Auth / session
191
+ USER_ID: "user.id",
192
+ SESSION_ID: "session.id",
193
+ // Errors
194
+ ERROR_TYPE: "error.type",
195
+ EXCEPTION_TYPE: "exception.type",
196
+ EXCEPTION_MESSAGE: "exception.message",
197
+ EXCEPTION_STACKTRACE: "exception.stacktrace",
198
+ // RPC
199
+ RPC_SYSTEM: "rpc.system",
200
+ RPC_SERVICE: "rpc.service",
201
+ RPC_METHOD: "rpc.method",
202
+ RPC_GRPC_STATUS_CODE: "rpc.grpc.status_code",
203
+ // Messaging — additional client/server attrs
204
+ MESSAGING_CLIENT_ID: "messaging.client.id",
205
+ MESSAGING_MESSAGE_ID: "messaging.message.id",
206
+ MESSAGING_DESTINATION_PARTITION_ID: "messaging.destination.partition.id",
207
+ MESSAGING_BATCH_MESSAGE_COUNT: "messaging.batch.message_count",
208
+ // Network — transport / connection state
209
+ NETWORK_TRANSPORT: "network.transport",
210
+ NETWORK_CONNECTION_TYPE: "network.connection.type",
211
+ // Process / runtime
212
+ PROCESS_PID: "process.pid",
213
+ PROCESS_RUNTIME_NAME: "process.runtime.name",
214
+ PROCESS_RUNTIME_VERSION: "process.runtime.version",
215
+ // Service identification
216
+ SERVICE_NAME: "service.name",
217
+ SERVICE_VERSION: "service.version",
218
+ SERVICE_INSTANCE_ID: "service.instance.id",
219
+ // Telemetry SDK self-identification
220
+ TELEMETRY_SDK_NAME: "telemetry.sdk.name",
221
+ TELEMETRY_SDK_LANGUAGE: "telemetry.sdk.language",
222
+ TELEMETRY_SDK_VERSION: "telemetry.sdk.version",
223
+ });
224
+
146
225
  module.exports = {
147
226
  tap: tap,
148
227
  event: event,
149
228
  safeEvent: safeEvent,
150
229
  setTap: setTap,
230
+ SEMCONV: SEMCONV,
151
231
  };
package/lib/retention.js CHANGED
@@ -445,11 +445,17 @@ function create(opts) {
445
445
  // SOC 2 (CC1–CC9) — 1 year typical; auditor-driven
446
446
  // DORA Art. 17 (incident logs) — 5 years
447
447
  var COMPLIANCE_RETENTION_FLOOR_MS = Object.freeze({
448
- "pci-dss": C.TIME.days(365), // 12 months
449
- "hipaa": C.TIME.days(365 * 6), // 6 years
450
- "sox": C.TIME.days(365 * 7), // 7 years
451
- "soc2": C.TIME.days(365), // 1 year
452
- "dora": C.TIME.days(365 * 5), // 5 years (Article 17 incident logs)
448
+ "pci-dss": C.TIME.days(365), // 12 months — PCI-DSS §10.7.1
449
+ "hipaa": C.TIME.days(365 * 6), // 6 years — 45 CFR §164.316(b)(2)(i)
450
+ "sox": C.TIME.days(365 * 7), // 7 years — Sarbanes-Oxley §802
451
+ "soc2": C.TIME.days(365), // 1 year — typical SOC 2 audit window
452
+ "dora": C.TIME.days(365 * 5), // 5 years — DORA Article 17 incident logs
453
+ "nis2": C.TIME.days(365 * 3), // 3 years — NIS2 Art. 23 incident reporting
454
+ "cra": C.TIME.days(365 * 5), // 5 years — CRA Art. 14 vulnerability handling logs
455
+ "lgpd-br": C.TIME.days(365 * 5), // 5 years — Brazil LGPD Art. 16 + fiscal-record minimum
456
+ "appi-jp": C.TIME.days(365 * 3), // 3 years — Japan APPI handler-of-record requirement
457
+ "pdpa-sg": C.TIME.days(365 * 1), // 1 year — PDPA breach-notification audit trail
458
+ "uk-gdpr": C.TIME.days(365 * 6), // 6 years — UK ICO guidance, statutory limit alignment
453
459
  });
454
460
 
455
461
  // Operator passes a posture name + a candidate ttlMs; returns the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.7.90",
3
+ "version": "0.7.92",
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:ea6013b4-0103-48ac-8789-54d7054c2b77",
5
+ "serialNumber": "urn:uuid:3879cfe5-c335-4b6c-9133-99b33bbd5666",
6
6
  "version": 1,
7
7
  "metadata": {
8
- "timestamp": "2026-05-06T07:47:56.714Z",
8
+ "timestamp": "2026-05-06T08:05:33.048Z",
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.90",
22
+ "bom-ref": "@blamejs/core@0.7.92",
23
23
  "type": "library",
24
24
  "name": "blamejs",
25
- "version": "0.7.90",
25
+ "version": "0.7.92",
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.90",
29
+ "purl": "pkg:npm/%40blamejs/core@0.7.92",
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.90",
57
+ "ref": "@blamejs/core@0.7.92",
58
58
  "dependsOn": []
59
59
  }
60
60
  ]