@carecard/telemetry 3.40.0 → 3.44.0

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/README.md CHANGED
@@ -33,7 +33,7 @@ explicit narrowing in all TypeScript-family files (`.ts`, `.tsx`, `.mts`,
33
33
  ## Install
34
34
 
35
35
  ```sh
36
- npm install @carecard/telemetry@3.20.0 --save-exact
36
+ npm install @carecard/telemetry@3.44.0 --save-exact
37
37
  ```
38
38
 
39
39
  ## Bootstrap
@@ -50,7 +50,7 @@ const packageJson = require('./package.json');
50
50
  const { createTelemetryShutdown, initializeServiceTelemetry } = require('@carecard/telemetry');
51
51
 
52
52
  const telemetry = initializeServiceTelemetry({
53
- serviceName: 'ms-auth',
53
+ serviceName: 'worker',
54
54
  serviceVersion: packageJson.version,
55
55
  });
56
56
 
@@ -159,3 +159,12 @@ npm run lint
159
159
  npm run format:check
160
160
  npm audit --audit-level=high
161
161
  ```
162
+
163
+ ## Caller-owned identity and exclusions
164
+
165
+ Service names are generic bounded operational identifiers. Set `serviceNamespace`
166
+ explicitly when needed. There is no built-in application naming rule, namespace,
167
+ health route list, or framework route exclusion. Supply `ignoreIncomingRequest`
168
+ with a boolean decision for the owning service or application. Outgoing exports
169
+ remain excluded to avoid telemetry recursion. Privacy sanitization and W3C
170
+ propagation remain technical package responsibilities.
package/index.d.ts CHANGED
@@ -1,11 +1,14 @@
1
+ import type { IncomingMessage } from 'node:http';
1
2
  export interface TelemetryRuntime {
2
3
  forceFlush(): Promise<void>;
3
4
  shutdown(): Promise<void>;
4
5
  }
5
6
 
6
7
  export interface ServiceTelemetryOptions {
7
- serviceName: `app-${string}` | `ms-${string}`;
8
+ serviceName: string;
8
9
  serviceVersion: string;
10
+ serviceNamespace?: string;
11
+ ignoreIncomingRequest?: (request: IncomingMessage) => boolean;
9
12
  }
10
13
 
11
14
  export interface DurableTraceContextOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carecard/telemetry",
3
- "version": "3.40.0",
3
+ "version": "3.44.0",
4
4
  "description": "Privacy-safe OpenTelemetry tracing for CareCard Node.js services",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -19,7 +19,7 @@
19
19
  "src"
20
20
  ],
21
21
  "engines": {
22
- "node": ">=24.20.0 <25"
22
+ "node": ">=24.21.0 <25"
23
23
  },
24
24
  "sideEffects": true,
25
25
  "repository": {
@@ -53,28 +53,28 @@
53
53
  "license": "ISC",
54
54
  "dependencies": {
55
55
  "@opentelemetry/api": "1.9.1",
56
- "@opentelemetry/context-async-hooks": "2.11.0",
57
- "@opentelemetry/core": "2.11.0",
58
- "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
59
- "@opentelemetry/instrumentation": "0.221.0",
60
- "@opentelemetry/instrumentation-express": "0.69.0",
61
- "@opentelemetry/instrumentation-http": "0.221.0",
62
- "@opentelemetry/instrumentation-kafkajs": "0.30.0",
63
- "@opentelemetry/instrumentation-pg": "0.73.0",
64
- "@opentelemetry/instrumentation-undici": "0.31.0",
65
- "@opentelemetry/resources": "2.11.0",
66
- "@opentelemetry/sdk-trace-base": "2.11.0",
67
- "@opentelemetry/sdk-trace-node": "2.11.0"
56
+ "@opentelemetry/context-async-hooks": "2.12.0",
57
+ "@opentelemetry/core": "2.12.0",
58
+ "@opentelemetry/exporter-trace-otlp-http": "0.223.0",
59
+ "@opentelemetry/instrumentation": "0.223.0",
60
+ "@opentelemetry/instrumentation-express": "0.71.0",
61
+ "@opentelemetry/instrumentation-http": "0.223.0",
62
+ "@opentelemetry/instrumentation-kafkajs": "0.32.0",
63
+ "@opentelemetry/instrumentation-pg": "0.75.0",
64
+ "@opentelemetry/instrumentation-undici": "0.33.0",
65
+ "@opentelemetry/resources": "2.12.0",
66
+ "@opentelemetry/sdk-trace-base": "2.12.0",
67
+ "@opentelemetry/sdk-trace-node": "2.12.0"
68
68
  },
69
69
  "devDependencies": {
70
- "@eslint/js": "9.39.5",
71
- "@types/node": "25.9.9",
72
- "@typescript-eslint/parser": "8.71.0",
73
- "eslint": "9.39.5",
70
+ "@eslint/js": "10.0.1",
71
+ "@types/node": "24.19.2",
72
+ "@typescript-eslint/parser": "8.71.1",
73
+ "eslint": "10.12.0",
74
74
  "globals": "17.13.0",
75
75
  "husky": "9.1.7",
76
76
  "lint-staged": "17.6.0",
77
- "prettier": "3.9.9",
77
+ "prettier": "3.9.10",
78
78
  "typescript": "6.0.3"
79
79
  },
80
80
  "overrides": {
@@ -15,12 +15,26 @@ const { createTraceparentOnlyPropagator } = require('./traceparentOnlyPropagator
15
15
  let activeRuntime;
16
16
  const installedShutdownHandlers = new WeakSet();
17
17
 
18
- // Pattern: Boundary Validation - accepts only CareCard service and server-application identities.
19
- function validateServiceIdentity({ serviceName, serviceVersion }) {
20
- const namePattern = /^(?:app|ms)-[a-z0-9-]+$/;
18
+ // Pattern: Boundary Validation - validates generic operational identity metadata.
19
+ function validateServiceIdentity({
20
+ serviceName,
21
+ serviceVersion,
22
+ serviceNamespace,
23
+ ignoreIncomingRequest,
24
+ }) {
25
+ const namePattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$/;
21
26
  const versionPattern = /^\d+\.\d+\.\d+(?:-[a-z0-9.-]+)?$/;
22
- if (!namePattern.test(serviceName)) {
23
- throw new Error('serviceName must identify a CareCard server application');
27
+ if (typeof serviceName !== 'string' || !namePattern.test(serviceName)) {
28
+ throw new Error('serviceName must be a bounded operational identifier');
29
+ }
30
+ if (
31
+ serviceNamespace !== undefined &&
32
+ (typeof serviceNamespace !== 'string' || !namePattern.test(serviceNamespace))
33
+ ) {
34
+ throw new TypeError('serviceNamespace must be a bounded operational identifier');
35
+ }
36
+ if (ignoreIncomingRequest !== undefined && typeof ignoreIncomingRequest !== 'function') {
37
+ throw new TypeError('ignoreIncomingRequest must be a function');
24
38
  }
25
39
  if (!versionPattern.test(serviceVersion)) {
26
40
  throw new Error('serviceVersion must be a semantic version');
@@ -37,7 +51,9 @@ function createNoopRuntime() {
37
51
  function createResource(options) {
38
52
  return resourceFromAttributes({
39
53
  'service.name': options.serviceName,
40
- 'service.namespace': 'carecard',
54
+ ...(options.serviceNamespace === undefined
55
+ ? {}
56
+ : { 'service.namespace': options.serviceNamespace }),
41
57
  'service.version': options.serviceVersion,
42
58
  });
43
59
  }
@@ -81,6 +97,7 @@ function startTelemetry(options, configuration) {
81
97
  const instrumentations = registerServiceInstrumentations({
82
98
  endpoint: configuration.endpoint,
83
99
  tracerProvider: provider,
100
+ ignoreIncomingRequest: options.ignoreIncomingRequest,
84
101
  });
85
102
  return buildRuntime(provider, contextManager, instrumentations);
86
103
  }
@@ -7,31 +7,11 @@ const { KafkaJsInstrumentation } = require('@opentelemetry/instrumentation-kafka
7
7
  const { PgInstrumentation } = require('@opentelemetry/instrumentation-pg');
8
8
  const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici');
9
9
 
10
- const IGNORED_REQUEST_PATHS = new Set([
11
- '/api/health',
12
- '/favicon.ico',
13
- '/health',
14
- '/health-check',
15
- '/healthz',
16
- '/metrics',
17
- '/ready',
18
- '/readyz',
19
- ]);
20
-
21
10
  function requestPath(request) {
22
11
  const rawPath = request.url || request.path || '/';
23
12
  return String(rawPath).split('?')[0];
24
13
  }
25
14
 
26
- function isRoutineRequest(request) {
27
- const path = requestPath(request);
28
- return (
29
- IGNORED_REQUEST_PATHS.has(path) ||
30
- path.startsWith('/_next/image') ||
31
- path.startsWith('/_next/static/')
32
- );
33
- }
34
-
35
15
  // Pattern: Normalizer - produces the bracketed URL host form for DNS, IPv4, and IPv6 inputs.
36
16
  function normalizeRequestHost(requestHost) {
37
17
  const host = String(requestHost || '')
@@ -61,11 +41,11 @@ function isExporterRequest(request, endpoint) {
61
41
  );
62
42
  }
63
43
 
64
- function createInstrumentations(endpointUrl) {
44
+ function createInstrumentations(endpointUrl, ignoreIncomingRequest) {
65
45
  const endpoint = new URL(endpointUrl);
66
46
  return [
67
47
  new HttpInstrumentation({
68
- ignoreIncomingRequestHook: isRoutineRequest,
48
+ ignoreIncomingRequestHook: request => ignoreIncomingRequest?.(request) === true,
69
49
  ignoreOutgoingRequestHook: request => isExporterRequest(request, endpoint),
70
50
  }),
71
51
  new ExpressInstrumentation(),
@@ -82,7 +62,7 @@ function createInstrumentations(endpointUrl) {
82
62
  }
83
63
 
84
64
  function registerServiceInstrumentations(options) {
85
- const instrumentations = createInstrumentations(options.endpoint);
65
+ const instrumentations = createInstrumentations(options.endpoint, options.ignoreIncomingRequest);
86
66
  registerInstrumentations({
87
67
  instrumentations,
88
68
  tracerProvider: options.tracerProvider,
@@ -92,6 +72,5 @@ function registerServiceInstrumentations(options) {
92
72
 
93
73
  module.exports = {
94
74
  createInstrumentations,
95
- isRoutineRequest,
96
75
  registerServiceInstrumentations,
97
76
  };