@nodii/telemetry 0.0.1 → 0.1.1

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.
Files changed (66) hide show
  1. package/README.md +223 -1
  2. package/dist/active-span.d.ts +12 -0
  3. package/dist/active-span.d.ts.map +1 -0
  4. package/dist/active-span.js +20 -0
  5. package/dist/active-span.js.map +1 -0
  6. package/dist/agent-invocable.d.ts +24 -0
  7. package/dist/agent-invocable.d.ts.map +1 -0
  8. package/dist/agent-invocable.js +64 -0
  9. package/dist/agent-invocable.js.map +1 -0
  10. package/dist/audit.d.ts +47 -0
  11. package/dist/audit.d.ts.map +1 -0
  12. package/dist/audit.js +78 -0
  13. package/dist/audit.js.map +1 -0
  14. package/dist/context.d.ts +40 -0
  15. package/dist/context.d.ts.map +1 -0
  16. package/dist/context.js +70 -0
  17. package/dist/context.js.map +1 -0
  18. package/dist/index.d.ts +18 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +29 -5
  21. package/dist/index.js.map +1 -1
  22. package/dist/init.d.ts +39 -0
  23. package/dist/init.d.ts.map +1 -0
  24. package/dist/init.js +114 -0
  25. package/dist/init.js.map +1 -0
  26. package/dist/intent.d.ts +21 -0
  27. package/dist/intent.d.ts.map +1 -0
  28. package/dist/intent.js +123 -0
  29. package/dist/intent.js.map +1 -0
  30. package/dist/logger.d.ts +19 -0
  31. package/dist/logger.d.ts.map +1 -0
  32. package/dist/logger.js +99 -0
  33. package/dist/logger.js.map +1 -0
  34. package/dist/metrics.d.ts +53 -0
  35. package/dist/metrics.d.ts.map +1 -0
  36. package/dist/metrics.js +152 -0
  37. package/dist/metrics.js.map +1 -0
  38. package/dist/otlp-adapter.d.ts +44 -0
  39. package/dist/otlp-adapter.d.ts.map +1 -0
  40. package/dist/otlp-adapter.js +351 -0
  41. package/dist/otlp-adapter.js.map +1 -0
  42. package/dist/outbox-emit.d.ts +34 -0
  43. package/dist/outbox-emit.d.ts.map +1 -0
  44. package/dist/outbox-emit.js +58 -0
  45. package/dist/outbox-emit.js.map +1 -0
  46. package/dist/redaction.d.ts +51 -0
  47. package/dist/redaction.d.ts.map +1 -0
  48. package/dist/redaction.js +173 -0
  49. package/dist/redaction.js.map +1 -0
  50. package/dist/sdk-counters.d.ts +14 -0
  51. package/dist/sdk-counters.d.ts.map +1 -0
  52. package/dist/sdk-counters.js +42 -0
  53. package/dist/sdk-counters.js.map +1 -0
  54. package/dist/tracer.d.ts +63 -0
  55. package/dist/tracer.d.ts.map +1 -0
  56. package/dist/tracer.js +214 -0
  57. package/dist/tracer.js.map +1 -0
  58. package/dist/types.d.ts +230 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +66 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/uuid.d.ts +5 -0
  63. package/dist/uuid.d.ts.map +1 -0
  64. package/dist/uuid.js +34 -0
  65. package/dist/uuid.js.map +1 -0
  66. package/package.json +28 -6
@@ -0,0 +1,58 @@
1
+ // Outbox emit helper.
2
+ //
3
+ // Per D43 + the communication-doctrine § 9.1: outbox rows carry the
4
+ // propagation columns directly (`tenant_id, request_id, intent_id,
5
+ // trace_parent, trace_state`) plus a `context_envelope jsonb` blob.
6
+ //
7
+ // `@nodii/telemetry` exposes a thin emit helper that consumers call to
8
+ // stage an outbox row in their service's transaction. The library
9
+ // itself does NOT own the outbox table — that's a per-service schema.
10
+ // Consumers register a writer via `_setOutboxWriter` (typically through
11
+ // the `@nodii/telemetry/outbox` subpath adapter); the default is a
12
+ // no-op that allows `intent.begin/complete/abandon` to call through
13
+ // without crashing when the consumer hasn't wired the outbox yet.
14
+ import { getContext } from "./context";
15
+ import { getActiveSpan } from "./active-span";
16
+ const noopWriter = () => {
17
+ /* no-op default; consumers wire a real writer */
18
+ };
19
+ let writer = noopWriter;
20
+ /**
21
+ * SDK-internal: the `@nodii/telemetry/outbox` adapter or service init
22
+ * code sets the writer. Public callers MUST NOT call this directly.
23
+ */
24
+ export function _setOutboxWriter(w) {
25
+ writer = w;
26
+ }
27
+ export function _resetOutboxWriterForTests() {
28
+ writer = noopWriter;
29
+ }
30
+ /**
31
+ * Stage an outbox row. Per D43: hybrid columns + context_envelope
32
+ * jsonb. The columns are derived from the active NodiiContext + OTel
33
+ * span; the envelope is a JSON blob of the remaining context fields.
34
+ *
35
+ * Returns the resolved columns so callers (typically the intent
36
+ * lifecycle) can verify what was staged.
37
+ */
38
+ export async function emitOutbox(event) {
39
+ const ctx = getContext();
40
+ const span = getActiveSpan();
41
+ const traceParent = span && ctx ? `00-${span.trace_id}-${span.span_id}-01` : null;
42
+ const columns = {
43
+ tenant_id: ctx?.tenant_id ?? null,
44
+ request_id: ctx?.request_id ?? null,
45
+ intent_id: ctx?.intent_id ?? null,
46
+ trace_parent: traceParent,
47
+ trace_state: null,
48
+ context_envelope: {
49
+ user_id: ctx?.user_id ?? null,
50
+ actor_type: ctx?.actor_type ?? null,
51
+ on_behalf_of: ctx?.on_behalf_of ?? null,
52
+ role: ctx?.role ?? null,
53
+ },
54
+ };
55
+ await writer(event, columns);
56
+ return columns;
57
+ }
58
+ //# sourceMappingURL=outbox-emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox-emit.js","sourceRoot":"","sources":["../src/outbox-emit.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,EAAE;AACF,oEAAoE;AACpE,mEAAmE;AACnE,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,kEAAkE;AAClE,sEAAsE;AACtE,wEAAwE;AACxE,mEAAmE;AACnE,oEAAoE;AACpE,kEAAkE;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA0B9C,MAAM,UAAU,GAAiB,GAAG,EAAE;IACpC,iDAAiD;AACnD,CAAC,CAAC;AAEF,IAAI,MAAM,GAAiB,UAAU,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAe;IAC9C,MAAM,GAAG,CAAC,CAAC;AACb,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,MAAM,GAAG,UAAU,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAkB;IACjD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,MAAM,WAAW,GACf,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,MAAM,OAAO,GAAkB;QAC7B,SAAS,EAAE,GAAG,EAAE,SAAS,IAAI,IAAI;QACjC,UAAU,EAAE,GAAG,EAAE,UAAU,IAAI,IAAI;QACnC,SAAS,EAAE,GAAG,EAAE,SAAS,IAAI,IAAI;QACjC,YAAY,EAAE,WAAW;QACzB,WAAW,EAAE,IAAI;QACjB,gBAAgB,EAAE;YAChB,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,IAAI;YAC7B,UAAU,EAAE,GAAG,EAAE,UAAU,IAAI,IAAI;YACnC,YAAY,EAAE,GAAG,EAAE,YAAY,IAAI,IAAI;YACvC,IAAI,EAAE,GAAG,EAAE,IAAI,IAAI,IAAI;SACxB;KACF,CAAC;IACF,MAAM,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * D2 PII field-name allowlist. Keys whose name matches one of these
3
+ * (case-insensitive) get their value stripped to `[REDACTED]` before
4
+ * the log/span attribute is emitted.
5
+ *
6
+ * Source: the legacy telemetry doctrine D2 PII list, transcribed for
7
+ * v0.1.0. Adding new keys is an ADR-gated change.
8
+ */
9
+ export declare const PII_FIELD_NAMES: readonly string[];
10
+ /**
11
+ * Returns true iff the key name matches the D2 PII field-name list.
12
+ * Comparison is case-insensitive on the bare key (no normalization
13
+ * beyond `toLowerCase()`).
14
+ */
15
+ export declare function isPiiFieldName(key: string): boolean;
16
+ /**
17
+ * Redacts an arbitrary record by replacing values whose keys match the
18
+ * D2 PII list with `[REDACTED]`. Nested records are walked recursively.
19
+ * Arrays are walked element-by-element. Primitives at the top level are
20
+ * returned untouched (this function is for records only).
21
+ *
22
+ * Throws nothing under normal input; if a value is a non-serializable
23
+ * object (e.g. circular reference encountered via JSON.stringify
24
+ * elsewhere) the caller wraps this in `attemptRedaction()` to catch.
25
+ */
26
+ export declare function redactFields(input: Record<string, unknown>): Record<string, unknown>;
27
+ /**
28
+ * Apply the D15 regex pass to a string. Returns the redacted string.
29
+ * Each pattern that fires replaces its match with [REDACTED]; multiple
30
+ * matches per string are all replaced.
31
+ */
32
+ export declare function redactString(s: string): string;
33
+ /**
34
+ * Fail-closed wrapper. Returns:
35
+ * - `{ ok: true, redacted }` when redaction succeeded.
36
+ * - `{ ok: false, reason }` when redaction threw — caller drops the
37
+ * record and bumps `telemetry.sdk.redaction_failure_total{reason}`.
38
+ *
39
+ * Per D15 the line is DROPPED, not emitted unredacted. This is the
40
+ * canonical pass; the collector pass (D59) is defense-in-depth.
41
+ */
42
+ export type RedactionResult<T> = {
43
+ ok: true;
44
+ redacted: T;
45
+ } | {
46
+ ok: false;
47
+ reason: string;
48
+ };
49
+ export declare function attemptRedactFields(input: Record<string, unknown>): RedactionResult<Record<string, unknown>>;
50
+ export declare function attemptRedactString(s: string): RedactionResult<string>;
51
+ //# sourceMappingURL=redaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.d.ts","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAyCnC,CAAC;AAwCX;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAYD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,GACzB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAS1C;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAStE"}
@@ -0,0 +1,173 @@
1
+ // PII redaction per D15. SDK-side canonical, fail-closed. Two passes:
2
+ // 1. Structured-field-key match against D2 PII list.
3
+ // 2. Regex pass on the message string.
4
+ //
5
+ // On exception during redaction: the line is DROPPED and a
6
+ // `telemetry.sdk.redaction_failure_total{reason}` counter increments. We
7
+ // surface that counter via the metrics module (§ 5.4); this module just
8
+ // reports the drop + reason via `attemptRedaction` returning a tagged
9
+ // result. The collector-side pass (D59) is defense-in-depth — the
10
+ // SDK-side pass is canonical.
11
+ const REDACTED = "[REDACTED]";
12
+ /**
13
+ * D2 PII field-name allowlist. Keys whose name matches one of these
14
+ * (case-insensitive) get their value stripped to `[REDACTED]` before
15
+ * the log/span attribute is emitted.
16
+ *
17
+ * Source: the legacy telemetry doctrine D2 PII list, transcribed for
18
+ * v0.1.0. Adding new keys is an ADR-gated change.
19
+ */
20
+ export const PII_FIELD_NAMES = [
21
+ "password",
22
+ "passwd",
23
+ "secret",
24
+ "token",
25
+ "access_token",
26
+ "refresh_token",
27
+ "id_token",
28
+ "api_key",
29
+ "apikey",
30
+ "private_key",
31
+ "client_secret",
32
+ "session_id",
33
+ "cookie",
34
+ "authorization",
35
+ "ssn",
36
+ "tax_id",
37
+ "pan",
38
+ "aadhaar",
39
+ "credit_card",
40
+ "card_number",
41
+ "cvv",
42
+ "iban",
43
+ "account_number",
44
+ "routing_number",
45
+ "phone",
46
+ "phone_number",
47
+ "mobile",
48
+ "email",
49
+ "email_address",
50
+ "address",
51
+ "street",
52
+ "postal_code",
53
+ "zip",
54
+ "dob",
55
+ "date_of_birth",
56
+ "first_name",
57
+ "last_name",
58
+ "full_name",
59
+ "passport",
60
+ "drivers_license",
61
+ ];
62
+ const PII_FIELD_SET = new Set(PII_FIELD_NAMES.map((s) => s.toLowerCase()));
63
+ /**
64
+ * D15 regex pass — runs on `msg` strings (logs) and span attribute
65
+ * values when their key doesn't match a structured PII key but the
66
+ * value looks like a known PII pattern.
67
+ *
68
+ * Each regex is anchored loosely; matches are replaced with [REDACTED].
69
+ * Order matters: more-specific patterns first.
70
+ */
71
+ const REGEX_PATTERNS = [
72
+ // Bearer tokens / JWT-shaped: 3 base64-ish segments separated by dots.
73
+ {
74
+ name: "jwt",
75
+ re: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
76
+ },
77
+ // Credit-card-like: 13–19 digits, possibly grouped by spaces or dashes.
78
+ {
79
+ name: "credit_card",
80
+ re: /\b(?:\d[ -]?){13,19}\b/g,
81
+ },
82
+ // Email addresses.
83
+ {
84
+ name: "email",
85
+ re: /\b[\w.!#$%&'*+/=?^`{|}~-]+@[\w-]+(?:\.[\w-]+)+\b/g,
86
+ },
87
+ // SSN-like: NNN-NN-NNNN.
88
+ {
89
+ name: "ssn",
90
+ re: /\b\d{3}-\d{2}-\d{4}\b/g,
91
+ },
92
+ // Phone-like (loose, intl): + then 10–15 digits with optional dashes/spaces.
93
+ {
94
+ name: "phone_intl",
95
+ re: /\+(?:\d[ -]?){10,15}\b/g,
96
+ },
97
+ ];
98
+ /**
99
+ * Returns true iff the key name matches the D2 PII field-name list.
100
+ * Comparison is case-insensitive on the bare key (no normalization
101
+ * beyond `toLowerCase()`).
102
+ */
103
+ export function isPiiFieldName(key) {
104
+ return PII_FIELD_SET.has(key.toLowerCase());
105
+ }
106
+ /**
107
+ * Redacts an arbitrary record by replacing values whose keys match the
108
+ * D2 PII list with `[REDACTED]`. Nested records are walked recursively.
109
+ * Arrays are walked element-by-element. Primitives at the top level are
110
+ * returned untouched (this function is for records only).
111
+ *
112
+ * Throws nothing under normal input; if a value is a non-serializable
113
+ * object (e.g. circular reference encountered via JSON.stringify
114
+ * elsewhere) the caller wraps this in `attemptRedaction()` to catch.
115
+ */
116
+ export function redactFields(input) {
117
+ const out = {};
118
+ for (const [k, v] of Object.entries(input)) {
119
+ if (isPiiFieldName(k)) {
120
+ out[k] = REDACTED;
121
+ continue;
122
+ }
123
+ out[k] = redactValue(v);
124
+ }
125
+ return out;
126
+ }
127
+ function redactValue(v) {
128
+ if (v === null || v === undefined)
129
+ return v;
130
+ if (typeof v === "string")
131
+ return redactString(v);
132
+ if (Array.isArray(v))
133
+ return v.map(redactValue);
134
+ if (typeof v === "object") {
135
+ return redactFields(v);
136
+ }
137
+ return v;
138
+ }
139
+ /**
140
+ * Apply the D15 regex pass to a string. Returns the redacted string.
141
+ * Each pattern that fires replaces its match with [REDACTED]; multiple
142
+ * matches per string are all replaced.
143
+ */
144
+ export function redactString(s) {
145
+ let out = s;
146
+ for (const { re } of REGEX_PATTERNS) {
147
+ out = out.replace(re, REDACTED);
148
+ }
149
+ return out;
150
+ }
151
+ export function attemptRedactFields(input) {
152
+ try {
153
+ return { ok: true, redacted: redactFields(input) };
154
+ }
155
+ catch (err) {
156
+ return {
157
+ ok: false,
158
+ reason: err instanceof Error ? err.name : "redaction_error",
159
+ };
160
+ }
161
+ }
162
+ export function attemptRedactString(s) {
163
+ try {
164
+ return { ok: true, redacted: redactString(s) };
165
+ }
166
+ catch (err) {
167
+ return {
168
+ ok: false,
169
+ reason: err instanceof Error ? err.name : "redaction_error",
170
+ };
171
+ }
172
+ }
173
+ //# sourceMappingURL=redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redaction.js","sourceRoot":"","sources":["../src/redaction.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uDAAuD;AACvD,yCAAyC;AACzC,EAAE;AACF,2DAA2D;AAC3D,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,kEAAkE;AAClE,8BAA8B;AAE9B,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAsB;IAChD,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,cAAc;IACd,eAAe;IACf,UAAU;IACV,SAAS;IACT,QAAQ;IACR,aAAa;IACb,eAAe;IACf,YAAY;IACZ,QAAQ;IACR,eAAe;IACf,KAAK;IACL,QAAQ;IACR,KAAK;IACL,SAAS;IACT,aAAa;IACb,aAAa;IACb,KAAK;IACL,MAAM;IACN,gBAAgB;IAChB,gBAAgB;IAChB,OAAO;IACP,cAAc;IACd,QAAQ;IACR,OAAO;IACP,eAAe;IACf,SAAS;IACT,QAAQ;IACR,aAAa;IACb,KAAK;IACL,KAAK;IACL,eAAe;IACf,YAAY;IACZ,WAAW;IACX,WAAW;IACX,UAAU;IACV,iBAAiB;CACT,CAAC;AAEX,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,cAAc,GAA4C;IAC9D,uEAAuE;IACvE;QACE,IAAI,EAAE,KAAK;QACX,EAAE,EAAE,wDAAwD;KAC7D;IACD,wEAAwE;IACxE;QACE,IAAI,EAAE,aAAa;QACnB,EAAE,EAAE,yBAAyB;KAC9B;IACD,mBAAmB;IACnB;QACE,IAAI,EAAE,OAAO;QACb,EAAE,EAAE,mDAAmD;KACxD;IACD,yBAAyB;IACzB;QACE,IAAI,EAAE,KAAK;QACX,EAAE,EAAE,wBAAwB;KAC7B;IACD,6EAA6E;IAC7E;QACE,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,yBAAyB;KAC9B;CACO,CAAC;AAEX;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAC1B,KAA8B;IAE9B,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,IAAI,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;YACtB,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;YAClB,SAAS;QACX,CAAC;QACD,GAAG,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,CAAU;IAC7B,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAChD,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO,YAAY,CAAC,CAA4B,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,EAAE,EAAE,EAAE,IAAI,cAAc,EAAE,CAAC;QACpC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAeD,MAAM,UAAU,mBAAmB,CACjC,KAA8B;IAE9B,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB;SAC5D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,CAAS;IAC3C,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,iBAAiB;SAC5D,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ type LabelMap = Record<string, string>;
2
+ interface CounterPoint {
3
+ name: string;
4
+ labels: LabelMap;
5
+ value: number;
6
+ }
7
+ /** Increment by 1 (default) or by `n`. Internal SDK use. */
8
+ export declare function incSdkCounter(name: string, labels?: LabelMap, n?: number): void;
9
+ /** Snapshot — used by the metrics flush path + tests. */
10
+ export declare function snapshotSdkCounters(): readonly CounterPoint[];
11
+ /** Test-only reset. NOT exported via the public barrel. */
12
+ export declare function _resetSdkCountersForTests(): void;
13
+ export {};
14
+ //# sourceMappingURL=sdk-counters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-counters.d.ts","sourceRoot":"","sources":["../src/sdk-counters.ts"],"names":[],"mappings":"AAWA,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEvC,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAYD,4DAA4D;AAC5D,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,QAAa,EACrB,CAAC,SAAI,GACJ,IAAI,CAQN;AAED,yDAAyD;AACzD,wBAAgB,mBAAmB,IAAI,SAAS,YAAY,EAAE,CAM7D;AAED,2DAA2D;AAC3D,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
@@ -0,0 +1,42 @@
1
+ // SDK-internal observability counters. These exist BEFORE the public
2
+ // metrics API is wired (because the metrics module itself may report
3
+ // failures through them), so they are intentionally a tiny in-process
4
+ // map with a flush hook the metrics module reads.
5
+ //
6
+ // Counter names (locked):
7
+ // - `telemetry.sdk.redaction_failure_total{reason}` (D15 fail-closed drop)
8
+ // - `telemetry.sdk.cardinality_rejected_total{family, reason}` (D18)
9
+ // - `telemetry.sdk.export_failures_total{signal, reason}` (D153)
10
+ // - `telemetry.sdk.trace_propagation_missing_total` (§ 5.9)
11
+ const points = new Map();
12
+ function keyFor(name, labels) {
13
+ const labelStr = Object.entries(labels)
14
+ .sort(([a], [b]) => a.localeCompare(b))
15
+ .map(([k, v]) => `${k}=${v}`)
16
+ .join(",");
17
+ return `${name}|${labelStr}`;
18
+ }
19
+ /** Increment by 1 (default) or by `n`. Internal SDK use. */
20
+ export function incSdkCounter(name, labels = {}, n = 1) {
21
+ const k = keyFor(name, labels);
22
+ const existing = points.get(k);
23
+ if (existing) {
24
+ existing.value += n;
25
+ }
26
+ else {
27
+ points.set(k, { name, labels, value: n });
28
+ }
29
+ }
30
+ /** Snapshot — used by the metrics flush path + tests. */
31
+ export function snapshotSdkCounters() {
32
+ return Array.from(points.values()).map((p) => ({
33
+ name: p.name,
34
+ labels: { ...p.labels },
35
+ value: p.value,
36
+ }));
37
+ }
38
+ /** Test-only reset. NOT exported via the public barrel. */
39
+ export function _resetSdkCountersForTests() {
40
+ points.clear();
41
+ }
42
+ //# sourceMappingURL=sdk-counters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-counters.js","sourceRoot":"","sources":["../src/sdk-counters.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,qEAAqE;AACrE,sEAAsE;AACtE,kDAAkD;AAClD,EAAE;AACF,0BAA0B;AAC1B,6EAA6E;AAC7E,uEAAuE;AACvE,mEAAmE;AACnE,8DAA8D;AAU9D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;AAE/C,SAAS,MAAM,CAAC,IAAY,EAAE,MAAgB;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SACpC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;SAC5B,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,SAAmB,EAAE,EACrB,CAAC,GAAG,CAAC;IAEL,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/B,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;IACtB,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,mBAAmB;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE;QACvB,KAAK,EAAE,CAAC,CAAC,KAAK;KACf,CAAC,CAAC,CAAC;AACN,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,yBAAyB;IACvC,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC"}
@@ -0,0 +1,63 @@
1
+ export type SpanKind = "INTERNAL" | "SERVER" | "CLIENT" | "PRODUCER" | "CONSUMER";
2
+ export type SpanStatus = "UNSET" | "OK" | "ERROR";
3
+ export interface SpanRecord {
4
+ trace_id: string;
5
+ span_id: string;
6
+ parent_span_id: string | null;
7
+ name: string;
8
+ kind: SpanKind;
9
+ start_ns: bigint;
10
+ end_ns: bigint;
11
+ attributes: Record<string, unknown>;
12
+ status: SpanStatus;
13
+ status_message?: string;
14
+ events: SpanEvent[];
15
+ }
16
+ export interface SpanEvent {
17
+ name: string;
18
+ ts_ns: bigint;
19
+ attributes?: Record<string, unknown>;
20
+ }
21
+ /** Per-span handle the caller manipulates while the span is live. */
22
+ export interface Span {
23
+ readonly trace_id: string;
24
+ readonly span_id: string;
25
+ setAttribute(key: string, value: unknown): void;
26
+ setAttributes(attrs: Record<string, unknown>): void;
27
+ addEvent(name: string, attributes?: Record<string, unknown>): void;
28
+ recordException(err: unknown): void;
29
+ setStatus(status: SpanStatus, message?: string): void;
30
+ }
31
+ /** Backend hook. TS-12 replaces the no-op with the OTel emitter. */
32
+ export interface SpanEmitter {
33
+ emit(record: SpanRecord): void;
34
+ }
35
+ export declare function _setSpanEmitter(e: SpanEmitter): void;
36
+ export declare function _resetSpanEmitterForTests(): void;
37
+ /**
38
+ * `tracer.span(name, fn)` opens a span, runs `fn`, then closes the
39
+ * span. Mandatory attributes stamped on start; D27 status mapping
40
+ * applied automatically.
41
+ */
42
+ declare function span<T>(name: string, fn: (span: Span) => Promise<T> | T, opts?: {
43
+ kind?: SpanKind;
44
+ attributes?: Record<string, unknown>;
45
+ }): Promise<T>;
46
+ declare function withSpanAttributes<T>(attrs: Record<string, unknown>, fn: () => Promise<T> | T): Promise<T> | T;
47
+ /**
48
+ * `getSagaTracer()` — tactical hold per D149. Returns a tracer whose
49
+ * spans default to the SAGA shape (INTERNAL, parent=SERVER subtree).
50
+ * Sunsets at v0.2.0 when every service in D169 has adopted `@nodii/saga`.
51
+ */
52
+ declare function getSagaTracer(): {
53
+ span<T>(name: string, fn: (span: Span) => Promise<T> | T, opts?: {
54
+ attributes?: Record<string, unknown>;
55
+ }): Promise<T>;
56
+ };
57
+ /** Test-only reset of active span stack. */
58
+ export declare function _resetTracerStackForTests(): void;
59
+ export declare const tracer: {
60
+ readonly span: typeof span;
61
+ };
62
+ export { withSpanAttributes, getSagaTracer };
63
+ //# sourceMappingURL=tracer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracer.d.ts","sourceRoot":"","sources":["../src/tracer.ts"],"names":[],"mappings":"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"}
package/dist/tracer.js ADDED
@@ -0,0 +1,214 @@
1
+ // Tracer per D26 / D27 / D28 / D30. Surface only; the real OTel wiring
2
+ // lands in TS-12 by replacing the in-process `SpanEmitter` via
3
+ // `_setSpanEmitter`. The default emitter is a no-op so tests can run
4
+ // without an OTLP endpoint.
5
+ //
6
+ // What's locked at v0.1.0:
7
+ // - `NodiiContextSpanProcessor` semantics in `_emitSpan`: stamps the
8
+ // three mandatory attributes (`tenant_id, request_id, intent_id`)
9
+ // onto every span on start (D26).
10
+ // - 4 KB per-value cap on attributes (truncates with `_truncated`
11
+ // marker). PII redaction applied to attribute values in `onEnd`
12
+ // (D26 + D15).
13
+ // - Status mapping (D27): if `recordException` ran or any error was
14
+ // attached, the span ends with status ERROR; otherwise UNSET.
15
+ // - `getSagaTracer()` ships as a tactical hold per D149; sunsets at
16
+ // v0.2.0.
17
+ import { getContext } from "./context";
18
+ import { _setActiveSpanGetter } from "./active-span";
19
+ import { attemptRedactFields } from "./redaction";
20
+ import { incSdkCounter } from "./sdk-counters";
21
+ import { uuidv7 } from "./uuid";
22
+ const noopEmitter = {
23
+ emit() {
24
+ /* no-op; TS-12 replaces */
25
+ },
26
+ };
27
+ let emitter = noopEmitter;
28
+ export function _setSpanEmitter(e) {
29
+ emitter = e;
30
+ }
31
+ export function _resetSpanEmitterForTests() {
32
+ emitter = noopEmitter;
33
+ }
34
+ const ATTR_VALUE_CAP_BYTES = 4 * 1024;
35
+ function capValue(v) {
36
+ if (typeof v !== "string")
37
+ return v;
38
+ if (Buffer.byteLength(v, "utf8") <= ATTR_VALUE_CAP_BYTES)
39
+ return v;
40
+ // Truncate by bytes — slice to the cap, mark.
41
+ const truncated = Buffer.from(v, "utf8")
42
+ .subarray(0, ATTR_VALUE_CAP_BYTES)
43
+ .toString("utf8");
44
+ return `${truncated}[_truncated=true]`;
45
+ }
46
+ function capAttributes(attrs) {
47
+ const out = {};
48
+ for (const [k, v] of Object.entries(attrs))
49
+ out[k] = capValue(v);
50
+ return out;
51
+ }
52
+ /** Stack of active spans (per-async-scope discipline is ALS, not this stack). */
53
+ const activeStack = [];
54
+ // Wire the active-span getter so logger.ts can stamp trace_id / span_id.
55
+ _setActiveSpanGetter(() => {
56
+ const top = activeStack[activeStack.length - 1];
57
+ if (!top)
58
+ return null;
59
+ return { trace_id: top.trace_id, span_id: top.span_id };
60
+ });
61
+ /**
62
+ * `tracer.span(name, fn)` opens a span, runs `fn`, then closes the
63
+ * span. Mandatory attributes stamped on start; D27 status mapping
64
+ * applied automatically.
65
+ */
66
+ async function span(name, fn, opts) {
67
+ const ctx = getContext();
68
+ const parent = activeStack[activeStack.length - 1];
69
+ const traceId = parent ? parent.trace_id : newTraceId();
70
+ const spanId = newSpanId();
71
+ const startNs = process.hrtime.bigint();
72
+ // D26 mandatory attributes envelope. tenant_id and request_id come
73
+ // from NodiiContext when present.
74
+ const record = {
75
+ trace_id: traceId,
76
+ span_id: spanId,
77
+ parent_span_id: parent?.span_id ?? null,
78
+ name,
79
+ kind: opts?.kind ?? "INTERNAL",
80
+ start_ns: startNs,
81
+ end_ns: 0n,
82
+ attributes: {
83
+ ...(ctx
84
+ ? {
85
+ tenant_id: ctx.tenant_id,
86
+ request_id: ctx.request_id,
87
+ intent_id: ctx.intent_id,
88
+ }
89
+ : {}),
90
+ ...(opts?.attributes ? capAttributes(opts.attributes) : {}),
91
+ },
92
+ status: "UNSET",
93
+ events: [],
94
+ };
95
+ activeStack.push(record);
96
+ let recordedException = false;
97
+ const handle = {
98
+ trace_id: traceId,
99
+ span_id: spanId,
100
+ setAttribute(key, value) {
101
+ record.attributes[key] = capValue(value);
102
+ },
103
+ setAttributes(attrs) {
104
+ Object.assign(record.attributes, capAttributes(attrs));
105
+ },
106
+ addEvent(eventName, attributes) {
107
+ record.events.push({
108
+ name: eventName,
109
+ ts_ns: process.hrtime.bigint(),
110
+ attributes,
111
+ });
112
+ },
113
+ recordException(err) {
114
+ recordedException = true;
115
+ const message = err instanceof Error ? err.message : String(err);
116
+ const stack = err instanceof Error ? err.stack : undefined;
117
+ record.events.push({
118
+ name: "exception",
119
+ ts_ns: process.hrtime.bigint(),
120
+ attributes: {
121
+ "exception.message": capValue(message),
122
+ "exception.stacktrace": stack ? capValue(stack) : undefined,
123
+ },
124
+ });
125
+ },
126
+ setStatus(status, message) {
127
+ record.status = status;
128
+ record.status_message = message;
129
+ },
130
+ };
131
+ try {
132
+ const result = await fn(handle);
133
+ if (record.status === "UNSET")
134
+ record.status = "OK";
135
+ return result;
136
+ }
137
+ catch (err) {
138
+ handle.recordException(err);
139
+ record.status = "ERROR";
140
+ throw err;
141
+ }
142
+ finally {
143
+ if (recordedException && record.status !== "ERROR") {
144
+ // D27: recordException auto-paired with ERROR.
145
+ record.status = "ERROR";
146
+ }
147
+ record.end_ns = process.hrtime.bigint();
148
+ // D26 onEnd: PII redaction over attribute values. Fail-closed: if
149
+ // redaction throws, drop the span by NOT emitting + bump the
150
+ // counter. (We still pop the stack regardless.)
151
+ const redacted = attemptRedactFields(record.attributes);
152
+ if (redacted.ok) {
153
+ record.attributes = redacted.redacted;
154
+ emitter.emit(record);
155
+ }
156
+ else {
157
+ incSdkCounter("telemetry.sdk.redaction_failure_total", {
158
+ reason: redacted.reason,
159
+ });
160
+ }
161
+ activeStack.pop();
162
+ }
163
+ }
164
+ function newTraceId() {
165
+ // OTel trace_id is 16 bytes (32 hex). Reuse uuidv7's randomness path
166
+ // by stripping dashes from a fresh v7.
167
+ return uuidv7().replace(/-/g, "");
168
+ }
169
+ function newSpanId() {
170
+ // OTel span_id is 8 bytes (16 hex). Take the trailing 16 hex chars
171
+ // of a uuidv7 (the random tail, not the timestamp).
172
+ return uuidv7().replace(/-/g, "").slice(16, 32);
173
+ }
174
+ /**
175
+ * `withSpanAttributes` per D149 § 5.3 ergonomic helper. Wraps a block
176
+ * so that every span opened inside it gets the supplied attributes
177
+ * merged onto its attribute envelope. Implementation: stores the
178
+ * attributes on a stack consulted by `tracer.span` at start.
179
+ */
180
+ const attrScope = [];
181
+ function withSpanAttributes(attrs, fn) {
182
+ attrScope.push(attrs);
183
+ try {
184
+ return fn();
185
+ }
186
+ finally {
187
+ attrScope.pop();
188
+ }
189
+ }
190
+ /**
191
+ * `getSagaTracer()` — tactical hold per D149. Returns a tracer whose
192
+ * spans default to the SAGA shape (INTERNAL, parent=SERVER subtree).
193
+ * Sunsets at v0.2.0 when every service in D169 has adopted `@nodii/saga`.
194
+ */
195
+ function getSagaTracer() {
196
+ return {
197
+ span(name, fn, opts) {
198
+ return span(name, fn, {
199
+ kind: "INTERNAL",
200
+ attributes: { "saga.tactical_hold": true, ...(opts?.attributes ?? {}) },
201
+ });
202
+ },
203
+ };
204
+ }
205
+ /** Test-only reset of active span stack. */
206
+ export function _resetTracerStackForTests() {
207
+ activeStack.length = 0;
208
+ attrScope.length = 0;
209
+ }
210
+ export const tracer = {
211
+ span,
212
+ };
213
+ export { withSpanAttributes, getSagaTracer };
214
+ //# sourceMappingURL=tracer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracer.js","sourceRoot":"","sources":["../src/tracer.ts"],"names":[],"mappings":"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"}