@blamejs/core 0.14.18 → 0.14.20

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.
@@ -309,7 +309,12 @@ function timed(name, fn, labels) {
309
309
  // pass attribute keys that should match the keys below. The map is
310
310
  // frozen — adding a new attribute requires a release.
311
311
  //
312
- // Reference: https://opentelemetry.io/docs/specs/semconv/general/attributes/
312
+ // References:
313
+ // https://opentelemetry.io/docs/specs/semconv/general/attributes/
314
+ // https://opentelemetry.io/docs/specs/semconv/resource/ (resource,
315
+ // telemetry-sdk, deployment-environment)
316
+ // https://opentelemetry.io/docs/specs/semconv/resource/k8s/
317
+ // https://opentelemetry.io/docs/specs/semconv/resource/faas/
313
318
  var SEMCONV = Object.freeze({
314
319
  // HTTP server (stable per OTel semconv)
315
320
  HTTP_REQUEST_METHOD: "http.request.method",
@@ -370,10 +375,33 @@ var SEMCONV = Object.freeze({
370
375
  SERVICE_NAME: "service.name",
371
376
  SERVICE_VERSION: "service.version",
372
377
  SERVICE_INSTANCE_ID: "service.instance.id",
378
+ // peer.service — logical name of the remote service a span talks to,
379
+ // distinct from server.address (the host). OTel semconv (general).
380
+ PEER_SERVICE: "peer.service",
381
+ // Deployment environment (aka deployment tier: "production",
382
+ // "staging"). The bare `deployment.environment` key was deprecated in
383
+ // favour of `deployment.environment.name`; this carries the current
384
+ // stable key. OTel semconv resource/deployment-environment.
385
+ DEPLOYMENT_ENVIRONMENT_NAME: "deployment.environment.name",
373
386
  // Telemetry SDK self-identification
374
387
  TELEMETRY_SDK_NAME: "telemetry.sdk.name",
375
388
  TELEMETRY_SDK_LANGUAGE: "telemetry.sdk.language",
376
389
  TELEMETRY_SDK_VERSION: "telemetry.sdk.version",
390
+ // Telemetry distribution self-identification — the redistribution of
391
+ // an OTel SDK an operator runs (e.g. a vendor distro). OTel semconv
392
+ // resource/telemetry-sdk.
393
+ TELEMETRY_DISTRO_NAME: "telemetry.distro.name",
394
+ TELEMETRY_DISTRO_VERSION: "telemetry.distro.version",
395
+ // Instrumentation scope self-identification — the scope (library)
396
+ // that produced a span/metric. OTel semconv otel namespace.
397
+ OTEL_SCOPE_NAME: "otel.scope.name",
398
+ OTEL_SCOPE_VERSION: "otel.scope.version",
399
+ // FaaS (serverless) — function-as-a-service execution context. OTel
400
+ // semconv resource/faas + attributes-registry/faas.
401
+ FAAS_NAME: "faas.name",
402
+ FAAS_VERSION: "faas.version",
403
+ FAAS_INSTANCE: "faas.instance",
404
+ FAAS_TRIGGER: "faas.trigger",
377
405
  // GenAI — OpenTelemetry semantic conventions for generative AI
378
406
  // workloads (LLM clients, vector DB queries, agent frameworks).
379
407
  // Tracking the otel-spec experimental namespace; covers the stable
@@ -410,9 +438,19 @@ var SEMCONV = Object.freeze({
410
438
  CONTAINER_ID: "container.id",
411
439
  CONTAINER_IMAGE_NAME: "container.image.name",
412
440
  CONTAINER_IMAGE_TAG: "container.image.tag",
441
+ // Kubernetes — OTel semconv resource/k8s. Namespace / pod /
442
+ // deployment plus the surrounding workload + node + cluster context.
413
443
  K8S_NAMESPACE_NAME: "k8s.namespace.name",
414
444
  K8S_POD_NAME: "k8s.pod.name",
415
445
  K8S_DEPLOYMENT_NAME: "k8s.deployment.name",
446
+ K8S_NODE_NAME: "k8s.node.name",
447
+ K8S_CLUSTER_NAME: "k8s.cluster.name",
448
+ K8S_CONTAINER_NAME: "k8s.container.name",
449
+ K8S_STATEFULSET_NAME: "k8s.statefulset.name",
450
+ K8S_DAEMONSET_NAME: "k8s.daemonset.name",
451
+ K8S_JOB_NAME: "k8s.job.name",
452
+ K8S_CRONJOB_NAME: "k8s.cronjob.name",
453
+ K8S_REPLICASET_NAME: "k8s.replicaset.name",
416
454
  });
417
455
 
418
456
  // W3C Trace Context — parse / build the `traceparent` HTTP header
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * OpenAPI 3.1 — paths / operations builder.
3
+ * OpenAPI 3.1 / 3.2 — paths / operations + webhooks builder.
4
4
  *
5
5
  * Internal to lib/openapi.js. Holds the per-path operation table
6
6
  * (method to operationObject) and produces the final `paths` map used
@@ -13,6 +13,14 @@
13
13
  *
14
14
  * Operation methods accepted: get / put / post / delete / options /
15
15
  * head / patch / trace (RFC 9110 + OpenAPI 3.1 §4.8.5).
16
+ *
17
+ * `WebhooksBuilder` shares the same Operation Object normalisation but
18
+ * keys by a free-form webhook NAME (not a URL pattern): the top-level
19
+ * `webhooks` field is a map of named Path Item Objects describing
20
+ * out-of-band requests the API initiates (OpenAPI 3.2 §4.8.2, "Fixed
21
+ * Fields" — `webhooks`; carried forward unchanged from 3.1.0 §4.1).
22
+ * Webhook keys are not URL templates, so the `/`-prefix and
23
+ * path-template-placeholder checks are intentionally not applied.
16
24
  */
17
25
 
18
26
  var validateOpts = require("./validate-opts");
@@ -39,25 +47,23 @@ function PathsBuilder() {
39
47
  this._paths = {};
40
48
  }
41
49
 
42
- PathsBuilder.prototype.add = function (method, urlPattern, opts) {
43
- opts = opts || {};
50
+ // _buildOperation normalise a single Operation Object from operator
51
+ // opts. Shared by PathsBuilder.add and WebhooksBuilder.add. `label` is
52
+ // the caller-facing prefix used in error messages. `declaredPathParams`
53
+ // (out-param object) records every in=path parameter so the caller can
54
+ // verify path-template placeholders against it; webhooks have no URL
55
+ // template so the caller simply ignores it.
56
+ function _buildOperation(method, opts, label, declaredPathParams) {
44
57
  if (typeof method !== "string" || VALID_METHODS.indexOf(method.toLowerCase()) === -1) {
45
58
  throw new OpenApiError("openapi/bad-method",
46
- "paths.add: method must be one of " + VALID_METHODS.join(", ") +
59
+ label + ": method must be one of " + VALID_METHODS.join(", ") +
47
60
  " - got " + JSON.stringify(method));
48
61
  }
49
- validateOpts.requireNonEmptyString(urlPattern, "paths.add: urlPattern",
50
- OpenApiError, "openapi/bad-path");
51
- if (urlPattern.charAt(0) !== "/") {
52
- throw new OpenApiError("openapi/bad-path",
53
- "paths.add: urlPattern must start with '/' - got " +
54
- JSON.stringify(urlPattern));
55
- }
56
62
  validateOpts(opts, [
57
63
  "summary", "description", "operationId", "tags",
58
64
  "parameters", "requestBody", "responses",
59
65
  "security", "deprecated", "servers", "externalDocs",
60
- ], "paths.add");
66
+ ], label);
61
67
 
62
68
  var op = {};
63
69
  if (typeof opts.summary === "string") op.summary = opts.summary;
@@ -67,50 +73,65 @@ PathsBuilder.prototype.add = function (method, urlPattern, opts) {
67
73
  op.tags = opts.tags.map(function (t) {
68
74
  if (typeof t !== "string" || t.length === 0) {
69
75
  throw new OpenApiError("openapi/bad-tag",
70
- "paths.add: tags must be non-empty strings");
76
+ label + ": tags must be non-empty strings");
71
77
  }
72
78
  return t;
73
79
  });
74
80
  }
75
81
 
76
82
  // Parameters
77
- var declaredPathParams = Object.create(null);
78
83
  if (Array.isArray(opts.parameters)) {
79
84
  op.parameters = [];
80
85
  for (var i = 0; i < opts.parameters.length; i += 1) {
81
- var p = _normaliseParameter(opts.parameters[i], "paths.add: parameters[" + i + "]");
86
+ var p = _normaliseParameter(opts.parameters[i], label + ": parameters[" + i + "]");
82
87
  op.parameters.push(p);
83
88
  if (p.in === "path") declaredPathParams[p.name] = true;
84
89
  }
85
90
  }
86
- // Verify every {placeholder} in path is declared.
87
- var placeholders = _extractPathParams(urlPattern);
88
- for (var j = 0; j < placeholders.length; j += 1) {
89
- if (!declaredPathParams[placeholders[j]]) {
90
- throw new OpenApiError("openapi/missing-path-param",
91
- "paths.add: path template " + JSON.stringify(urlPattern) +
92
- " references {" + placeholders[j] +
93
- "} but no parameter with in=path name=" + JSON.stringify(placeholders[j]) +
94
- " was declared");
95
- }
96
- }
97
91
 
98
92
  // Request body
99
93
  if (opts.requestBody) {
100
- op.requestBody = _normaliseRequestBody(opts.requestBody, "paths.add: requestBody");
94
+ op.requestBody = _normaliseRequestBody(opts.requestBody, label + ": requestBody");
101
95
  }
102
96
 
103
97
  // Responses (required)
104
98
  if (!opts.responses || typeof opts.responses !== "object") {
105
99
  throw new OpenApiError("openapi/missing-responses",
106
- "paths.add: responses object is required (per OpenAPI 3.1 §4.8.5)");
100
+ label + ": responses object is required (per OpenAPI 3.1 §4.8.5)");
107
101
  }
108
- op.responses = _normaliseResponses(opts.responses, "paths.add: responses");
102
+ op.responses = _normaliseResponses(opts.responses, label + ": responses");
109
103
 
110
104
  if (Array.isArray(opts.security)) op.security = opts.security.slice();
111
105
  if (opts.deprecated === true) op.deprecated = true;
112
106
  if (Array.isArray(opts.servers)) op.servers = opts.servers.slice();
113
107
  if (opts.externalDocs) op.externalDocs = opts.externalDocs;
108
+ return op;
109
+ }
110
+
111
+ PathsBuilder.prototype.add = function (method, urlPattern, opts) {
112
+ opts = opts || {};
113
+ validateOpts.requireNonEmptyString(urlPattern, "paths.add: urlPattern",
114
+ OpenApiError, "openapi/bad-path");
115
+ if (urlPattern.charAt(0) !== "/") {
116
+ throw new OpenApiError("openapi/bad-path",
117
+ "paths.add: urlPattern must start with '/' - got " +
118
+ JSON.stringify(urlPattern));
119
+ }
120
+
121
+ var declaredPathParams = Object.create(null);
122
+ var op = _buildOperation(method, opts, "paths.add", declaredPathParams);
123
+
124
+ // Verify every {placeholder} in path is declared.
125
+ var placeholders = _extractPathParams(urlPattern);
126
+ for (var j = 0; j < placeholders.length; j += 1) {
127
+ if (!declaredPathParams[placeholders[j]]) {
128
+ throw new OpenApiError("openapi/missing-path-param",
129
+ "paths.add: path template " + JSON.stringify(urlPattern) +
130
+ " references {" + placeholders[j] +
131
+ "} but no parameter with in=path name=" + JSON.stringify(placeholders[j]) +
132
+ " was declared");
133
+ }
134
+ }
114
135
 
115
136
  if (!this._paths[urlPattern]) this._paths[urlPattern] = {};
116
137
  if (this._paths[urlPattern][method.toLowerCase()]) {
@@ -240,8 +261,63 @@ PathsBuilder.prototype.toMap = function () {
240
261
  return out;
241
262
  };
242
263
 
264
+ // WebhooksBuilder — the top-level `webhooks` field is a map of named
265
+ // Path Item Objects describing requests the API initiates out-of-band
266
+ // (OpenAPI 3.2 §4.8.2 Fixed Fields → `webhooks`; unchanged from
267
+ // 3.1.0 §4.1). Keys are free-form webhook names (e.g. "newPet"), NOT
268
+ // URL templates — no `/`-prefix and no path-template-placeholder
269
+ // validation. Each named entry holds the same Operation Objects as a
270
+ // regular path item.
271
+ function WebhooksBuilder() {
272
+ // Null-prototype map so a free-form webhook name that collides with an
273
+ // Object.prototype member (__proto__ / constructor / prototype) becomes
274
+ // an own property instead of mutating the prototype (CWE-1321).
275
+ this._webhooks = Object.create(null);
276
+ }
277
+
278
+ WebhooksBuilder.prototype.add = function (name, method, opts) {
279
+ opts = opts || {};
280
+ validateOpts.requireNonEmptyString(name, "webhook.add: name",
281
+ OpenApiError, "openapi/bad-webhook");
282
+ if (name === "__proto__" || name === "constructor" || name === "prototype") {
283
+ throw new OpenApiError("openapi/bad-webhook",
284
+ "webhook.add: name must not be a reserved object key (" + JSON.stringify(name) + ")");
285
+ }
286
+ var declaredPathParams = Object.create(null);
287
+ var op = _buildOperation(method, opts, "webhook.add", declaredPathParams);
288
+ if (!this._webhooks[name]) this._webhooks[name] = {};
289
+ if (this._webhooks[name][method.toLowerCase()]) {
290
+ throw new OpenApiError("openapi/duplicate-operation",
291
+ "webhook.add: duplicate operation " + method.toUpperCase() +
292
+ " on webhook " + JSON.stringify(name));
293
+ }
294
+ this._webhooks[name][method.toLowerCase()] = op;
295
+ return op;
296
+ };
297
+
298
+ WebhooksBuilder.prototype.count = function () {
299
+ return Object.keys(this._webhooks).length;
300
+ };
301
+
302
+ WebhooksBuilder.prototype.toMap = function () {
303
+ var sorted = Object.keys(this._webhooks).sort();
304
+ var out = {};
305
+ for (var i = 0; i < sorted.length; i += 1) {
306
+ var name = sorted[i];
307
+ var item = this._webhooks[name];
308
+ var ordered = {};
309
+ for (var j = 0; j < VALID_METHODS.length; j += 1) {
310
+ var method = VALID_METHODS[j];
311
+ if (item[method]) ordered[method] = item[method];
312
+ }
313
+ out[name] = ordered;
314
+ }
315
+ return out;
316
+ };
317
+
243
318
  module.exports = {
244
319
  PathsBuilder: PathsBuilder,
320
+ WebhooksBuilder: WebhooksBuilder,
245
321
  VALID_METHODS: VALID_METHODS,
246
322
  _extractPathParams: _extractPathParams,
247
323
  OpenApiError: OpenApiError,