@blamejs/core 0.7.94 → 0.7.96
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 +4 -0
- package/lib/observability.js +91 -0
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
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.96** (2026-05-06) — `b.observability.timed(name, fn, labels)` — convenience wrapper that measures wall-clock duration of a sync or async operation and emits a counter event carrying `duration_ms` in the labels alongside `outcome: "ok" | "fail"`. Returns the wrapped function's return value verbatim; rethrows on error after emitting the failure event with `error_type` capturing the thrown error's `.name`. Operators previously hand-rolled `var t0 = Date.now(); var r = await fn(); event(name, 1, { ...labels, duration_ms: Date.now() - t0, outcome: "ok" })` patterns at every call site — `timed` is the consolidated form. Composes with the existing `b.observability.SEMCONV` constants: `b.observability.timed("db.query", async () => db.query("SELECT ..."), { [SEMCONV.DB_OPERATION_NAME]: "select" })`. Operation name MUST be a stable string (not derived from input) to keep metric cardinality bounded; dynamic per-tenant labels go in the `labels` parameter.
|
|
12
|
+
|
|
13
|
+
- **0.7.95** (2026-05-06) — `b.observability.SEMCONV` GenAI + cloud + container coverage. Twenty-eight new attribute names tracking OpenTelemetry semantic conventions for generative AI workloads, vector databases, cloud runtime context, and Kubernetes orchestration. **GenAI:** `GEN_AI_SYSTEM` / `GEN_AI_REQUEST_MODEL` / `GEN_AI_REQUEST_TEMPERATURE` / `GEN_AI_REQUEST_TOP_P` / `GEN_AI_REQUEST_TOP_K` / `GEN_AI_REQUEST_MAX_TOKENS` / `GEN_AI_REQUEST_STOP_SEQUENCES` / `GEN_AI_RESPONSE_MODEL` / `GEN_AI_RESPONSE_ID` / `GEN_AI_RESPONSE_FINISH_REASONS` / `GEN_AI_USAGE_INPUT_TOKENS` / `GEN_AI_USAGE_OUTPUT_TOKENS` / `GEN_AI_USAGE_TOTAL_TOKENS` / `GEN_AI_OPERATION_NAME` / `GEN_AI_TOOL_NAME` / `GEN_AI_TOOL_CALL_ID` / `GEN_AI_AGENT_ID` / `GEN_AI_AGENT_NAME` / `GEN_AI_AGENT_DESCRIPTION`. **Vector DB / RAG:** `DB_VECTOR_QUERY_TOP_K` / `DB_VECTOR_QUERY_DIMENSIONS` / `DB_VECTOR_QUERY_DISTANCE_METRIC`. **Cloud:** `CLOUD_PROVIDER` / `CLOUD_REGION` / `CLOUD_ACCOUNT_ID` / `CLOUD_RESOURCE_ID`. **Container / K8s:** `CONTAINER_ID` / `CONTAINER_IMAGE_NAME` / `CONTAINER_IMAGE_TAG` / `K8S_NAMESPACE_NAME` / `K8S_POD_NAME` / `K8S_DEPLOYMENT_NAME`. Operators wiring LLM client telemetry, vector-DB query traces, or Kubernetes-deployed services to an OTel collector reference these constants directly — string typos throw at access time instead of producing mis-named span attributes the OTel collector silently drops.
|
|
14
|
+
|
|
11
15
|
- **0.7.94** (2026-05-06) — `b.compliance.REGIME_MAP` + `b.compliance.describe(<posture>)` — frozen lookup table mapping each posture name to its human-readable name + statutory citation + jurisdiction + domain. `b.compliance.describe("hipaa")` returns `{ name: "Health Insurance Portability and Accountability Act", citation: "Pub. L. 104-191; 45 CFR Parts 160, 162, 164", jurisdiction: "US", domain: "health" }`. Operators rendering the deployment posture in admin UI / audit logs reach for `REGIME_MAP[posture]` instead of hand-rolling a lookup table; values track the regulatory text and update with the framework rather than going stale in operator code. Covers all 19 postures shipped through v0.7.91 (hipaa / pci-dss / soc2 / sox + the v0.7.91 expansions: wmhmda / bipa / ccpa / gdpr / dora / nis2 / cra / ai-act / lgpd-br / pipl-cn / appi-jp / pdpa-sg / pipeda-ca / uk-gdpr). `domain` field categorizes the regime (privacy / health / payment / cybersecurity / financial-reporting / etc.) so operators can render compliance dashboards grouped by domain instead of alphabetical posture.
|
|
12
16
|
|
|
13
17
|
- **0.7.93** (2026-05-06) — Adjacent-regulation incident-reporting deadline reference exported on `b.dora`. **`b.dora.DEADLINES_NIS2`** — NIS2 (Directive (EU) 2022/2555) Art. 23 deadlines: 24h early warning, 72h initial notification, 1 month final report. **`b.dora.DEADLINES_CRA`** — CRA (Regulation (EU) 2024/2847) Art. 14 deadlines: 24h early warning, 72h initial notification, 14 days final report. **`b.dora.DEADLINES_HIPAA_BREACH`** — HIPAA Breach Notification Rule (45 CFR §164.404 / §164.408) deadlines: 60 days for affected individuals, 60 days for HHS Secretary, annual aggregate report by March 1 for sub-500-individual breaches. Operators handling NIS2 / CRA / HIPAA reporting reach for these constants instead of pinning literal hour counts in their workflow code; the values track the regulatory text and update with the framework rather than going stale in operator code. The b.dora factory itself continues to enforce DORA Article 19 deadlines unchanged — operators wiring NIS2 / CRA / HIPAA workflows compose against the deadline constants directly with their own scheduler / submission code.
|
package/lib/observability.js
CHANGED
|
@@ -143,6 +143,57 @@ function safeEvent(name, value, labels) {
|
|
|
143
143
|
catch (_e) { /* hot-path observability sink — drops silent on internal throws */ }
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// timed — convenience wrapper that measures wall-clock duration of a
|
|
147
|
+
// sync or async operation and emits a counter event with
|
|
148
|
+
// duration_ms in the labels. Returns the wrapped function's return
|
|
149
|
+
// value verbatim; rethrows on error after emitting the failure event
|
|
150
|
+
// with outcome: "fail".
|
|
151
|
+
//
|
|
152
|
+
// var rows = await b.observability.timed("db.query", async function () {
|
|
153
|
+
// return await db.query("SELECT * FROM users");
|
|
154
|
+
// }, { [SEMCONV.DB_OPERATION_NAME]: "select" });
|
|
155
|
+
//
|
|
156
|
+
// On success: emits `<name>` with { ...labels, outcome: "ok",
|
|
157
|
+
// duration_ms }. On throw: emits with outcome: "fail".
|
|
158
|
+
//
|
|
159
|
+
// The operation name MUST be a stable string (not derived from input)
|
|
160
|
+
// to keep the metric cardinality bounded; operators dynamically
|
|
161
|
+
// scope-naming via prefix should use the labels parameter instead.
|
|
162
|
+
function timed(name, fn, labels) {
|
|
163
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
164
|
+
throw new TypeError("observability.timed: name must be a non-empty string");
|
|
165
|
+
}
|
|
166
|
+
if (typeof fn !== "function") {
|
|
167
|
+
throw new TypeError("observability.timed: fn must be a function");
|
|
168
|
+
}
|
|
169
|
+
var start = Date.now();
|
|
170
|
+
function _emit(outcome, extra) {
|
|
171
|
+
var allLabels = Object.assign({}, labels || {}, {
|
|
172
|
+
outcome: outcome,
|
|
173
|
+
duration_ms: Date.now() - start,
|
|
174
|
+
}, extra || {});
|
|
175
|
+
try { event(name, 1, allLabels); }
|
|
176
|
+
catch (_e) { /* drop-silent — observability sink */ }
|
|
177
|
+
}
|
|
178
|
+
var ret;
|
|
179
|
+
try { ret = fn(); }
|
|
180
|
+
catch (e) {
|
|
181
|
+
_emit("fail", { error_type: (e && e.name) || "Error" });
|
|
182
|
+
throw e;
|
|
183
|
+
}
|
|
184
|
+
if (ret && typeof ret.then === "function") {
|
|
185
|
+
return ret.then(
|
|
186
|
+
function (v) { _emit("ok"); return v; },
|
|
187
|
+
function (e) {
|
|
188
|
+
_emit("fail", { error_type: (e && e.name) || "Error" });
|
|
189
|
+
throw e;
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
_emit("ok");
|
|
194
|
+
return ret;
|
|
195
|
+
}
|
|
196
|
+
|
|
146
197
|
// OpenTelemetry semantic-convention attribute names — the operator-
|
|
147
198
|
// facing canonical vocabulary the framework's b.observability /
|
|
148
199
|
// b.tracing / b.metrics emitters use when building span / metric
|
|
@@ -220,12 +271,52 @@ var SEMCONV = Object.freeze({
|
|
|
220
271
|
TELEMETRY_SDK_NAME: "telemetry.sdk.name",
|
|
221
272
|
TELEMETRY_SDK_LANGUAGE: "telemetry.sdk.language",
|
|
222
273
|
TELEMETRY_SDK_VERSION: "telemetry.sdk.version",
|
|
274
|
+
// GenAI — OpenTelemetry semantic conventions for generative AI
|
|
275
|
+
// workloads (LLM clients, vector DB queries, agent frameworks).
|
|
276
|
+
// Tracking the otel-spec experimental namespace; covers the stable
|
|
277
|
+
// attribute set as of 2026-Q2.
|
|
278
|
+
GEN_AI_SYSTEM: "gen_ai.system",
|
|
279
|
+
GEN_AI_REQUEST_MODEL: "gen_ai.request.model",
|
|
280
|
+
GEN_AI_REQUEST_TEMPERATURE: "gen_ai.request.temperature",
|
|
281
|
+
GEN_AI_REQUEST_TOP_P: "gen_ai.request.top_p",
|
|
282
|
+
GEN_AI_REQUEST_TOP_K: "gen_ai.request.top_k",
|
|
283
|
+
GEN_AI_REQUEST_MAX_TOKENS: "gen_ai.request.max_tokens",
|
|
284
|
+
GEN_AI_REQUEST_STOP_SEQUENCES: "gen_ai.request.stop_sequences",
|
|
285
|
+
GEN_AI_RESPONSE_MODEL: "gen_ai.response.model",
|
|
286
|
+
GEN_AI_RESPONSE_ID: "gen_ai.response.id",
|
|
287
|
+
GEN_AI_RESPONSE_FINISH_REASONS: "gen_ai.response.finish_reasons",
|
|
288
|
+
GEN_AI_USAGE_INPUT_TOKENS: "gen_ai.usage.input_tokens",
|
|
289
|
+
GEN_AI_USAGE_OUTPUT_TOKENS: "gen_ai.usage.output_tokens",
|
|
290
|
+
GEN_AI_USAGE_TOTAL_TOKENS: "gen_ai.usage.total_tokens",
|
|
291
|
+
GEN_AI_OPERATION_NAME: "gen_ai.operation.name",
|
|
292
|
+
GEN_AI_TOOL_NAME: "gen_ai.tool.name",
|
|
293
|
+
GEN_AI_TOOL_CALL_ID: "gen_ai.tool.call.id",
|
|
294
|
+
GEN_AI_AGENT_ID: "gen_ai.agent.id",
|
|
295
|
+
GEN_AI_AGENT_NAME: "gen_ai.agent.name",
|
|
296
|
+
GEN_AI_AGENT_DESCRIPTION: "gen_ai.agent.description",
|
|
297
|
+
// Vector database / retrieval-augmented generation
|
|
298
|
+
DB_VECTOR_QUERY_TOP_K: "db.vector.query.top_k",
|
|
299
|
+
DB_VECTOR_QUERY_DIMENSIONS: "db.vector.query.dimensions",
|
|
300
|
+
DB_VECTOR_QUERY_DISTANCE_METRIC: "db.vector.query.distance_metric",
|
|
301
|
+
// Cloud / runtime context (frequently paired with GenAI)
|
|
302
|
+
CLOUD_PROVIDER: "cloud.provider",
|
|
303
|
+
CLOUD_REGION: "cloud.region",
|
|
304
|
+
CLOUD_ACCOUNT_ID: "cloud.account.id",
|
|
305
|
+
CLOUD_RESOURCE_ID: "cloud.resource_id",
|
|
306
|
+
// Container / orchestration
|
|
307
|
+
CONTAINER_ID: "container.id",
|
|
308
|
+
CONTAINER_IMAGE_NAME: "container.image.name",
|
|
309
|
+
CONTAINER_IMAGE_TAG: "container.image.tag",
|
|
310
|
+
K8S_NAMESPACE_NAME: "k8s.namespace.name",
|
|
311
|
+
K8S_POD_NAME: "k8s.pod.name",
|
|
312
|
+
K8S_DEPLOYMENT_NAME: "k8s.deployment.name",
|
|
223
313
|
});
|
|
224
314
|
|
|
225
315
|
module.exports = {
|
|
226
316
|
tap: tap,
|
|
227
317
|
event: event,
|
|
228
318
|
safeEvent: safeEvent,
|
|
319
|
+
timed: timed,
|
|
229
320
|
setTap: setTap,
|
|
230
321
|
SEMCONV: SEMCONV,
|
|
231
322
|
};
|
package/package.json
CHANGED
package/sbom.cyclonedx.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:f930dea4-02bf-424a-81d9-af05bce63d00",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-06T08:
|
|
8
|
+
"timestamp": "2026-05-06T08:44:44.540Z",
|
|
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.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.7.96",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.7.
|
|
25
|
+
"version": "0.7.96",
|
|
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.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.7.96",
|
|
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.
|
|
57
|
+
"ref": "@blamejs/core@0.7.96",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|