autotel 4.1.0 → 4.2.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 (253) hide show
  1. package/dist/auto.cjs +5 -3
  2. package/dist/auto.cjs.map +1 -1
  3. package/dist/auto.js +3 -3
  4. package/dist/auto.js.map +1 -1
  5. package/dist/chunk-C_NdSu1c.cjs +34 -0
  6. package/dist/correlation-id.cjs +1 -1
  7. package/dist/correlation-id.d.cts.map +1 -1
  8. package/dist/correlation-id.d.ts.map +1 -1
  9. package/dist/correlation-id.js +1 -1
  10. package/dist/decorators.cjs +1 -1
  11. package/dist/decorators.js +1 -1
  12. package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
  13. package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
  14. package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
  15. package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
  16. package/dist/event.cjs +1 -1
  17. package/dist/event.js +1 -1
  18. package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
  19. package/dist/functional-C8B0Qa7o.cjs.map +1 -0
  20. package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
  21. package/dist/functional-r-AUIRy_.js.map +1 -0
  22. package/dist/functional.cjs +1 -1
  23. package/dist/functional.js +1 -1
  24. package/dist/http.cjs +1 -1
  25. package/dist/http.js +1 -1
  26. package/dist/index.cjs +15 -13
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts.map +1 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +14 -14
  31. package/dist/index.js.map +1 -1
  32. package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
  33. package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
  34. package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
  35. package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
  36. package/dist/instrumentation.cjs +2 -2
  37. package/dist/instrumentation.js +2 -2
  38. package/dist/logger.cjs +236 -8
  39. package/dist/logger.cjs.map +1 -0
  40. package/dist/messaging.cjs +1 -1
  41. package/dist/messaging.js +1 -1
  42. package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
  43. package/dist/node-require-CZ_PU448.cjs.map +1 -0
  44. package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
  45. package/dist/node-require-vROmTeJ8.js.map +1 -0
  46. package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
  47. package/dist/operation-context-CKBoA4Qy.js.map +1 -0
  48. package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
  49. package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
  50. package/dist/register.cjs +3 -1
  51. package/dist/register.cjs.map +1 -1
  52. package/dist/register.js +2 -2
  53. package/dist/register.js.map +1 -1
  54. package/dist/semantic-helpers.cjs +1 -1
  55. package/dist/semantic-helpers.js +1 -1
  56. package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
  57. package/dist/stable-hash-ChFBIhNt.js.map +1 -0
  58. package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
  59. package/dist/stable-hash-brKISGf1.cjs.map +1 -0
  60. package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
  61. package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
  62. package/dist/trace-helpers.cjs +1 -1
  63. package/dist/trace-helpers.js +1 -1
  64. package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
  65. package/dist/track-COUuU48p.js.map +1 -0
  66. package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
  67. package/dist/track-Cb3Q4QmS.cjs.map +1 -0
  68. package/dist/validate.cjs +1 -1
  69. package/dist/validate.js +1 -1
  70. package/dist/webhook.cjs +1 -1
  71. package/dist/webhook.js +1 -1
  72. package/dist/workflow-distributed.cjs +1 -1
  73. package/dist/workflow-distributed.js +1 -1
  74. package/dist/workflow.cjs +3 -1
  75. package/dist/workflow.cjs.map +1 -1
  76. package/dist/workflow.d.cts.map +1 -1
  77. package/dist/workflow.d.ts.map +1 -1
  78. package/dist/workflow.js +3 -3
  79. package/dist/workflow.js.map +1 -1
  80. package/dist/yaml-config.cjs +233 -4
  81. package/dist/yaml-config.cjs.map +1 -0
  82. package/dist/yaml-config.d.cts.map +1 -1
  83. package/dist/yaml-config.d.ts.map +1 -1
  84. package/dist/yaml-config.js +8 -7
  85. package/dist/yaml-config.js.map +1 -1
  86. package/package.json +1 -2
  87. package/dist/functional-DtI0u4vx.js.map +0 -1
  88. package/dist/functional-zpzNLhky.cjs.map +0 -1
  89. package/dist/logger-thMPLpOG.cjs +0 -487
  90. package/dist/logger-thMPLpOG.cjs.map +0 -1
  91. package/dist/node-require-DF5QBX6z.cjs.map +0 -1
  92. package/dist/node-require-Db1oDpLj.js.map +0 -1
  93. package/dist/operation-context-C-2hmmtP.js.map +0 -1
  94. package/dist/operation-context-n4_obUwq.cjs.map +0 -1
  95. package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
  96. package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
  97. package/dist/track-D59FfpL0.cjs.map +0 -1
  98. package/dist/track-wc0HafS_.js.map +0 -1
  99. package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
  100. package/dist/yaml-config-Ck2uB0Dp.cjs.map +0 -1
  101. package/src/attribute-redacting-processor.test.ts +0 -763
  102. package/src/attribute-redacting-processor.ts +0 -621
  103. package/src/attributes/attachers.ts +0 -161
  104. package/src/attributes/builders.ts +0 -529
  105. package/src/attributes/domains.ts +0 -42
  106. package/src/attributes/index.ts +0 -81
  107. package/src/attributes/registry.ts +0 -323
  108. package/src/attributes/types.ts +0 -211
  109. package/src/attributes/utils.ts +0 -64
  110. package/src/attributes/validators.ts +0 -266
  111. package/src/attributes.test.ts +0 -292
  112. package/src/auto.ts +0 -67
  113. package/src/autotel-logger.test.ts +0 -548
  114. package/src/autotel-logger.ts +0 -364
  115. package/src/baggage-span-processor.test.ts +0 -202
  116. package/src/baggage-span-processor.ts +0 -100
  117. package/src/business-baggage.test.ts +0 -500
  118. package/src/business-baggage.ts +0 -669
  119. package/src/circuit-breaker.test.ts +0 -341
  120. package/src/circuit-breaker.ts +0 -184
  121. package/src/config.test.ts +0 -94
  122. package/src/config.ts +0 -172
  123. package/src/correlated-events.test.ts +0 -151
  124. package/src/correlated-events.ts +0 -47
  125. package/src/correlation-id.test.ts +0 -163
  126. package/src/correlation-id.ts +0 -206
  127. package/src/db.test.ts +0 -252
  128. package/src/db.ts +0 -447
  129. package/src/decorators.test.ts +0 -153
  130. package/src/decorators.ts +0 -188
  131. package/src/define-event.test.ts +0 -41
  132. package/src/define-event.ts +0 -58
  133. package/src/devtools.ts +0 -60
  134. package/src/drain-pipeline.test.ts +0 -68
  135. package/src/drain-pipeline.ts +0 -199
  136. package/src/drain-toolkit.test.ts +0 -113
  137. package/src/drain-toolkit.ts +0 -129
  138. package/src/enricher-toolkit.test.ts +0 -67
  139. package/src/enricher-toolkit.ts +0 -79
  140. package/src/enrichers.test.ts +0 -150
  141. package/src/enrichers.ts +0 -145
  142. package/src/env-config.test.ts +0 -323
  143. package/src/env-config.ts +0 -309
  144. package/src/error-catalog.test.ts +0 -133
  145. package/src/error-catalog.ts +0 -262
  146. package/src/event-queue.test.ts +0 -864
  147. package/src/event-queue.ts +0 -699
  148. package/src/event-subscriber.ts +0 -262
  149. package/src/event-testing.ts +0 -197
  150. package/src/event.test.ts +0 -1104
  151. package/src/event.ts +0 -988
  152. package/src/events-config.ts +0 -235
  153. package/src/exporters.ts +0 -165
  154. package/src/filtering-span-processor.test.ts +0 -281
  155. package/src/filtering-span-processor.ts +0 -111
  156. package/src/flatten-attributes.test.ts +0 -76
  157. package/src/flatten-attributes.ts +0 -80
  158. package/src/functional.strict-types.typecheck.ts +0 -53
  159. package/src/functional.test.ts +0 -1464
  160. package/src/functional.ts +0 -2539
  161. package/src/functional.types.test.ts +0 -135
  162. package/src/hook.mjs +0 -15
  163. package/src/http.test.ts +0 -485
  164. package/src/http.ts +0 -424
  165. package/src/index.ts +0 -433
  166. package/src/init-auto-redactor.test.ts +0 -53
  167. package/src/init-redactor.test.ts +0 -8
  168. package/src/init.customization.test.ts +0 -665
  169. package/src/init.integrations.test.ts +0 -399
  170. package/src/init.openllmetry.test.ts +0 -194
  171. package/src/init.protocol.test.ts +0 -215
  172. package/src/init.ts +0 -2439
  173. package/src/instrumentation.test.ts +0 -108
  174. package/src/instrumentation.ts +0 -319
  175. package/src/logger.test.ts +0 -125
  176. package/src/logger.ts +0 -341
  177. package/src/messaging-adapters.test.ts +0 -595
  178. package/src/messaging-adapters.ts +0 -583
  179. package/src/messaging-testing.test.ts +0 -573
  180. package/src/messaging-testing.ts +0 -935
  181. package/src/messaging.test.ts +0 -1646
  182. package/src/messaging.ts +0 -2245
  183. package/src/metric-helpers.ts +0 -47
  184. package/src/metric-testing.ts +0 -197
  185. package/src/metric.ts +0 -446
  186. package/src/metrics.test.ts +0 -241
  187. package/src/node-require.ts +0 -123
  188. package/src/operation-context.ts +0 -93
  189. package/src/parse-error.test.ts +0 -73
  190. package/src/parse-error.ts +0 -112
  191. package/src/posthog-logs.test.ts +0 -115
  192. package/src/posthog-logs.ts +0 -77
  193. package/src/pretty-console-exporter.test.ts +0 -545
  194. package/src/pretty-console-exporter.ts +0 -413
  195. package/src/pretty-log-formatter.test.ts +0 -123
  196. package/src/pretty-log-formatter.ts +0 -210
  197. package/src/processors/canonical-log-line-processor.test.ts +0 -523
  198. package/src/processors/canonical-log-line-processor.ts +0 -396
  199. package/src/processors.ts +0 -152
  200. package/src/rate-limiter.test.ts +0 -199
  201. package/src/rate-limiter.ts +0 -98
  202. package/src/redact-values.test.ts +0 -90
  203. package/src/redact-values.ts +0 -34
  204. package/src/register.ts +0 -37
  205. package/src/request-logger.test.ts +0 -545
  206. package/src/request-logger.ts +0 -342
  207. package/src/sampling.test.ts +0 -1060
  208. package/src/sampling.ts +0 -737
  209. package/src/security-schema.test.ts +0 -45
  210. package/src/security-schema.ts +0 -107
  211. package/src/semantic-conventions.ts +0 -15
  212. package/src/semantic-helpers.test.ts +0 -226
  213. package/src/semantic-helpers.ts +0 -438
  214. package/src/shutdown.test.ts +0 -364
  215. package/src/shutdown.ts +0 -246
  216. package/src/span-name-normalizer.test.ts +0 -377
  217. package/src/span-name-normalizer.ts +0 -213
  218. package/src/stable-hash.ts +0 -27
  219. package/src/structured-error.test.ts +0 -191
  220. package/src/structured-error.ts +0 -157
  221. package/src/stub.integration.test.ts +0 -361
  222. package/src/tail-sampling-processor.test.ts +0 -230
  223. package/src/tail-sampling-processor.ts +0 -55
  224. package/src/test-span-collector.test.ts +0 -234
  225. package/src/test-span-collector.ts +0 -150
  226. package/src/testing.ts +0 -705
  227. package/src/trace-context.test.ts +0 -73
  228. package/src/trace-context.ts +0 -567
  229. package/src/trace-helpers.new.test.ts +0 -278
  230. package/src/trace-helpers.test.ts +0 -290
  231. package/src/trace-helpers.ts +0 -710
  232. package/src/trace-hybrid.test.ts +0 -42
  233. package/src/trace-hybrid.ts +0 -37
  234. package/src/tracer-provider.test.ts +0 -183
  235. package/src/tracer-provider.ts +0 -266
  236. package/src/track.test.ts +0 -154
  237. package/src/track.ts +0 -216
  238. package/src/validate.test.ts +0 -287
  239. package/src/validate.ts +0 -307
  240. package/src/validation-attributes.ts +0 -43
  241. package/src/validation.test.ts +0 -330
  242. package/src/validation.ts +0 -246
  243. package/src/variable-name-inference.test.ts +0 -178
  244. package/src/variable-name-inference.ts +0 -242
  245. package/src/webhook.test.ts +0 -649
  246. package/src/webhook.ts +0 -637
  247. package/src/workflow-distributed.test.ts +0 -786
  248. package/src/workflow-distributed.ts +0 -916
  249. package/src/workflow.async-safety.integration.test.ts +0 -345
  250. package/src/workflow.test.ts +0 -647
  251. package/src/workflow.ts +0 -810
  252. package/src/yaml-config.test.ts +0 -373
  253. package/src/yaml-config.ts +0 -351
@@ -1,64 +0,0 @@
1
- /**
2
- * Attribute utility functions
3
- */
4
-
5
- import type { AttributeValue } from '../trace-context';
6
- import {
7
- validateAttribute,
8
- autoRedactPII,
9
- defaultGuardrails,
10
- checkDeprecatedAttribute,
11
- type AttributePolicy,
12
- } from './validators';
13
-
14
- // Type for objects that have setAttributes method (spans or contexts)
15
- // Using a generic parameter to accommodate different AttributeValue types
16
- type AttributeSetter = {
17
- setAttributes: (attrs: Record<string, AttributeValue>) => void;
18
- };
19
-
20
- export function mergeAttrs(
21
- ...attrSets: Array<Record<string, unknown> | undefined>
22
- ): Record<string, unknown> {
23
- const result: Record<string, unknown> = {};
24
- for (const attrSet of attrSets) {
25
- if (attrSet) {
26
- Object.assign(result, attrSet);
27
- }
28
- }
29
- return result;
30
- }
31
-
32
- export function safeSetAttributes(
33
- span: AttributeSetter,
34
- attrs: Record<string, unknown>,
35
- policy?: AttributePolicy,
36
- ): void {
37
- // Merge user-supplied guardrails with defaults so callers can tweak
38
- // a single option without opting out of the rest
39
- const mergedGuardrails = {
40
- ...defaultGuardrails(),
41
- ...policy?.guardrails,
42
- };
43
- const effectivePolicy: AttributePolicy = {
44
- ...policy,
45
- guardrails: mergedGuardrails,
46
- };
47
-
48
- const validated = autoRedactPII(attrs, effectivePolicy);
49
-
50
- const sanitizedAttrs: Record<string, AttributeValue> = {};
51
- for (const [key, value] of Object.entries(validated)) {
52
- if (value !== undefined) {
53
- // Check for deprecated attributes and log warnings
54
- checkDeprecatedAttribute(key, effectivePolicy);
55
- const validatedValue = validateAttribute(key, value, effectivePolicy);
56
- if (validatedValue !== undefined) {
57
- // Cast to AttributeValue since validateAttribute ensures valid types
58
- sanitizedAttrs[key] = validatedValue as AttributeValue;
59
- }
60
- }
61
- }
62
-
63
- span.setAttributes(sanitizedAttrs);
64
- }
@@ -1,266 +0,0 @@
1
- /**
2
- * Attribute validation, PII detection, and guardrails
3
- * Provides safe-by-default attribute handling with configurable policies
4
- */
5
-
6
- import { REDACTOR_PATTERNS } from '../attribute-redacting-processor';
7
-
8
- export interface AttributeGuardrails {
9
- /** How to handle PII in attributes */
10
- pii?: 'allow' | 'redact' | 'hash' | 'block';
11
-
12
- /** Maximum length for attribute values */
13
- maxLength?: number;
14
-
15
- /** Validate enum values against known values */
16
- validateEnum?: boolean;
17
-
18
- /** Log warnings for deprecated attributes instead of throwing */
19
- warnDeprecated?: boolean;
20
-
21
- /** Custom deprecation warnings */
22
- deprecatedWarnings?: Record<string, string>;
23
- }
24
-
25
- export interface AttributePolicy {
26
- guardrails?: AttributeGuardrails;
27
- /** Custom deprecation warnings for specific attributes */
28
- deprecatedWarnings?: Record<string, string>;
29
- }
30
-
31
- const DEPRECATED_ATTRIBUTES = {
32
- 'enduser.id': 'user.id',
33
- 'enduser.role': 'user.roles',
34
- 'enduser.scope': undefined,
35
- 'http.method': 'http.request.method',
36
- 'http.host': 'server.address',
37
- 'http.status_code': 'http.response.status_code',
38
- 'http.target': 'url.path',
39
- 'http.url': 'url.full',
40
- 'http.user_agent': 'user_agent.original',
41
- 'http.flavor': 'network.protocol.name',
42
- 'http.scheme': 'url.scheme',
43
- 'http.server_name': 'server.address',
44
- 'db.name': 'db.namespace',
45
- 'db.operation': 'db.operation.name',
46
- 'db.statement': 'db.query.text',
47
- 'db.system': 'db.system.name',
48
- 'db.collection': 'db.collection.name',
49
- 'db.instance.id': undefined,
50
- 'db.jdbc.driver_classname': undefined,
51
- 'db.mssql.instance_name': 'mssql.instance.name',
52
- 'db.sql.table': 'db.collection.name',
53
- 'http.client_ip': 'client.address',
54
- 'user_agent.original': 'user_agent.original',
55
- } as const;
56
-
57
- const HTTP_METHODS = new Set([
58
- 'GET',
59
- 'POST',
60
- 'PUT',
61
- 'DELETE',
62
- 'PATCH',
63
- 'HEAD',
64
- 'OPTIONS',
65
- 'TRACE',
66
- 'QUERY',
67
- '_OTHER',
68
- ]);
69
-
70
- export function validateAttribute(
71
- key: string,
72
- value: unknown,
73
- policy: AttributePolicy = {},
74
- ): unknown {
75
- const { guardrails = {} } = policy;
76
-
77
- if (value === undefined || value === null) {
78
- return undefined;
79
- }
80
-
81
- // For non-string values that don't need transformation, preserve the original type
82
- if (typeof value !== 'string') {
83
- // PII checks only apply to strings
84
- // maxLength only applies to strings
85
- // validateEnum only applies to strings
86
- return value;
87
- }
88
-
89
- const stringValue = value;
90
-
91
- if (guardrails.pii) {
92
- const piiResult = applyPIIPolicy(key, stringValue, guardrails.pii);
93
- if (piiResult !== stringValue) {
94
- return piiResult;
95
- }
96
- }
97
-
98
- if (guardrails.maxLength && stringValue.length > guardrails.maxLength) {
99
- return truncateValue(key, stringValue, guardrails.maxLength);
100
- }
101
-
102
- if (guardrails.validateEnum && HTTP_METHODS.has(stringValue)) {
103
- const normalizedMethod = normalizeHTTPMethod(stringValue);
104
- if (normalizedMethod !== stringValue) {
105
- return normalizedMethod;
106
- }
107
- }
108
-
109
- return stringValue;
110
- }
111
-
112
- function applyPIIPolicy(
113
- key: string,
114
- value: string,
115
- pii: AttributeGuardrails['pii'],
116
- ): string {
117
- if (pii === 'allow') {
118
- return value;
119
- }
120
-
121
- if (pii === 'redact') {
122
- return redactIfPII(key, value);
123
- }
124
-
125
- if (pii === 'hash') {
126
- return hashIfPII(key, value);
127
- }
128
-
129
- if (pii === 'block' && isPIIKey(key)) {
130
- throw new Error(
131
- `PII attribute "${key}" is blocked by guardrails. Use pii: "allow" to enable it.`,
132
- );
133
- }
134
-
135
- return value;
136
- }
137
-
138
- function isPIIKey(key: string): boolean {
139
- const piiKeyPatterns = [
140
- 'email',
141
- 'phone',
142
- 'ssn',
143
- 'credit_card',
144
- 'password',
145
- 'secret',
146
- 'token',
147
- 'api_key',
148
- 'authorization',
149
- ];
150
- const lowerKey = key.toLowerCase();
151
- return piiKeyPatterns.some((pattern) => lowerKey.includes(pattern));
152
- }
153
-
154
- function redactIfPII(key: string, value: string): string {
155
- if (isPIIKey(key)) {
156
- // REDACTOR_PATTERNS values are RegExp patterns
157
- for (const [, pattern] of Object.entries(REDACTOR_PATTERNS)) {
158
- if (pattern instanceof RegExp && pattern.test(value)) {
159
- return '[REDACTED]';
160
- }
161
- }
162
- // If no pattern matched but key is PII, still redact
163
- return '[REDACTED]';
164
- }
165
- return value;
166
- }
167
-
168
- function hashIfPII(key: string, value: string): string {
169
- if (!isPIIKey(key)) {
170
- return value;
171
- }
172
-
173
- // Use a simple but consistent hash that produces 32-char hex
174
- // FNV-1a hash variant producing 128-bit output (32 hex chars)
175
- const FNV_PRIME = 0x01_00_01_93;
176
- const FNV_OFFSET = 0x81_1c_9d_c5;
177
-
178
- // Generate 4 32-bit hashes to produce 32 hex chars
179
- const hashes: number[] = [];
180
- for (let round = 0; round < 4; round++) {
181
- let hash = FNV_OFFSET;
182
- for (let i = 0; i < value.length; i++) {
183
- hash ^= (value.codePointAt(i) ?? 0) + round;
184
- hash = Math.imul(hash, FNV_PRIME);
185
- }
186
- hashes.push(hash >>> 0); // Convert to unsigned
187
- }
188
-
189
- return `hash_${hashes.map((h) => h.toString(16).padStart(8, '0')).join('')}`;
190
- }
191
-
192
- function truncateValue(key: string, value: string, maxLength: number): string {
193
- if (value.length <= maxLength) {
194
- return value;
195
- }
196
- return value.slice(0, maxLength - 3) + '...';
197
- }
198
-
199
- function normalizeHTTPMethod(method: string): string {
200
- const upper = method.toUpperCase();
201
- if (HTTP_METHODS.has(upper)) {
202
- return upper;
203
- }
204
- return upper;
205
- }
206
-
207
- export function checkDeprecatedAttribute(
208
- key: string,
209
- policy: AttributePolicy = {},
210
- ): string | null {
211
- const { guardrails = {}, deprecatedWarnings = {} } = policy;
212
- const { warnDeprecated = true } = guardrails;
213
-
214
- if (!warnDeprecated) {
215
- return null;
216
- }
217
-
218
- // Check if the key exists in the deprecated attributes map
219
- const isDeprecated = key in DEPRECATED_ATTRIBUTES;
220
- if (isDeprecated) {
221
- const replacement =
222
- DEPRECATED_ATTRIBUTES[key as keyof typeof DEPRECATED_ATTRIBUTES];
223
- if (replacement === undefined) {
224
- // Deprecated with no replacement (e.g., enduser.scope)
225
- console.warn(
226
- `[autotel/attributes] Attribute "${key}" is deprecated and has no replacement. ` +
227
- `Remove or find a replacement in OpenTelemetry semantic conventions.`,
228
- );
229
- } else {
230
- // Deprecated with a known replacement
231
- console.warn(
232
- `[autotel/attributes] Attribute "${key}" is deprecated. Use "${replacement}" instead.`,
233
- );
234
- }
235
- }
236
-
237
- if (deprecatedWarnings[key]) {
238
- console.warn(`[autotel/attributes] ${deprecatedWarnings[key]}`);
239
- }
240
-
241
- const replacement =
242
- DEPRECATED_ATTRIBUTES[key as keyof typeof DEPRECATED_ATTRIBUTES];
243
- return replacement ?? null;
244
- }
245
-
246
- export function autoRedactPII(
247
- attributes: Record<string, unknown>,
248
- policy: AttributePolicy = {},
249
- ): Record<string, unknown> {
250
- const { guardrails = { pii: 'redact' } } = policy;
251
-
252
- const redacted: Record<string, unknown> = {};
253
- for (const [key, value] of Object.entries(attributes)) {
254
- redacted[key] = validateAttribute(key, value, { guardrails });
255
- }
256
- return redacted;
257
- }
258
-
259
- export function defaultGuardrails(): AttributeGuardrails {
260
- return {
261
- pii: 'redact',
262
- maxLength: 255,
263
- validateEnum: true,
264
- warnDeprecated: true,
265
- };
266
- }
@@ -1,292 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { createTraceCollector, TraceCollector } from './testing';
3
- import { trace, TraceContext } from './functional';
4
- import {
5
- attrs,
6
- setUser,
7
- identify,
8
- httpServer,
9
- mergeAttrs,
10
- safeSetAttributes,
11
- transaction,
12
- } from './attributes';
13
-
14
- describe('attributes', () => {
15
- let collector: TraceCollector;
16
-
17
- beforeEach(() => {
18
- collector = createTraceCollector();
19
- });
20
-
21
- describe('Pattern A: Key builders', () => {
22
- it('should create user.id attribute', async () => {
23
- const testFn = trace((ctx: TraceContext) => async () => {
24
- ctx.setAttributes(attrs.user.id('user-123'));
25
- });
26
-
27
- await testFn();
28
-
29
- const spans = collector.getSpans();
30
- expect(spans).toHaveLength(1);
31
- expect(spans[0]!.attributes).toMatchObject({
32
- 'user.id': 'user-123',
33
- });
34
- });
35
-
36
- it('should create http.request.method attribute', async () => {
37
- const testFn = trace((ctx: TraceContext) => async () => {
38
- ctx.setAttributes(attrs.http.request.method('GET'));
39
- });
40
-
41
- await testFn();
42
-
43
- const spans = collector.getSpans();
44
- expect(spans).toHaveLength(1);
45
- expect(spans[0]!.attributes).toMatchObject({
46
- 'http.request.method': 'GET',
47
- });
48
- });
49
-
50
- it('should create multiple attributes from key builders', async () => {
51
- const testFn = trace((ctx: TraceContext) => async () => {
52
- ctx.setAttributes(
53
- mergeAttrs(
54
- attrs.user.id('user-123'),
55
- attrs.http.request.method('GET'),
56
- attrs.session.id('session-456'),
57
- ),
58
- );
59
- });
60
-
61
- await testFn();
62
-
63
- const spans = collector.getSpans();
64
- expect(spans).toHaveLength(1);
65
- expect(spans[0]!.attributes).toMatchObject({
66
- 'user.id': 'user-123',
67
- 'http.request.method': 'GET',
68
- 'session.id': 'session-456',
69
- });
70
- });
71
- });
72
-
73
- describe('Pattern B: Object builders', () => {
74
- it('should create user attributes from object', async () => {
75
- const testFn = trace((ctx: TraceContext) => async () => {
76
- const userAttrs = attrs.user.data({
77
- id: 'user-123',
78
- email: 'test@example.com',
79
- });
80
- ctx.setAttributes(userAttrs);
81
- });
82
-
83
- await testFn();
84
-
85
- const spans = collector.getSpans();
86
- expect(spans).toHaveLength(1);
87
- expect(spans[0]!.attributes).toMatchObject({
88
- 'user.id': 'user-123',
89
- 'user.email': 'test@example.com',
90
- });
91
- });
92
-
93
- it('should create HTTP server attributes from object', async () => {
94
- const testFn = trace((ctx: TraceContext) => async () => {
95
- const httpAttrs = attrs.http.server({
96
- method: 'POST',
97
- route: '/users/:id',
98
- statusCode: 200,
99
- });
100
- ctx.setAttributes(httpAttrs);
101
- });
102
-
103
- await testFn();
104
-
105
- const spans = collector.getSpans();
106
- expect(spans).toHaveLength(1);
107
- expect(spans[0]!.attributes).toMatchObject({
108
- 'http.request.method': 'POST',
109
- 'http.route': '/users/:id',
110
- 'http.response.status_code': 200,
111
- });
112
- });
113
- });
114
-
115
- describe('Attachers: attachers', () => {
116
- it('setUser should set user attributes on span (PII redacted by default)', async () => {
117
- const testFn = trace((ctx: TraceContext) => async () => {
118
- setUser(ctx, { id: 'user-123', email: 'test@example.com' });
119
- });
120
-
121
- await testFn();
122
-
123
- const spans = collector.getSpans();
124
- expect(spans).toHaveLength(1);
125
- expect(spans[0]!.attributes).toMatchObject({
126
- 'user.id': 'user-123',
127
- 'user.email': '[REDACTED]', // PII is redacted by default
128
- });
129
- });
130
-
131
- it('httpServer should set HTTP attributes and update span name', async () => {
132
- const testFn = trace((ctx: TraceContext) => async () => {
133
- httpServer(ctx, {
134
- method: 'GET',
135
- route: '/api/users',
136
- statusCode: 200,
137
- });
138
- });
139
-
140
- await testFn();
141
-
142
- const spans = collector.getSpans();
143
- expect(spans).toHaveLength(1);
144
- expect(spans[0]!.attributes).toMatchObject({
145
- 'http.request.method': 'GET',
146
- 'http.route': '/api/users',
147
- 'http.response.status_code': 200,
148
- });
149
- expect(spans[0]!.name).toBe('HTTP GET /api/users');
150
- });
151
-
152
- it('identify should bundle user, session, and device attributes', async () => {
153
- const testFn = trace((ctx: TraceContext) => async () => {
154
- identify(ctx, {
155
- user: { id: 'user-123', name: 'John Doe' },
156
- session: { id: 'session-456' },
157
- device: { id: 'device-789', manufacturer: 'Apple' },
158
- });
159
- });
160
-
161
- await testFn();
162
-
163
- const spans = collector.getSpans();
164
- expect(spans).toHaveLength(1);
165
- expect(spans[0]!.attributes).toMatchObject({
166
- 'user.id': 'user-123',
167
- 'user.name': 'John Doe',
168
- 'session.id': 'session-456',
169
- 'device.id': 'device-789',
170
- 'device.manufacturer': 'Apple',
171
- });
172
- });
173
- });
174
-
175
- describe('Domains: domains', () => {
176
- it('transaction should bundle request attributes', async () => {
177
- const testFn = trace((ctx: TraceContext) => async () => {
178
- transaction(ctx, {
179
- method: 'GET',
180
- route: '/api/users',
181
- statusCode: 200,
182
- clientIp: '192.168.1.1',
183
- });
184
- });
185
-
186
- await testFn();
187
-
188
- const spans = collector.getSpans();
189
- expect(spans).toHaveLength(1);
190
- expect(spans[0]!.attributes).toMatchObject({
191
- 'http.request.method': 'GET',
192
- 'http.route': '/api/users',
193
- 'http.response.status_code': 200,
194
- 'network.peer.address': '192.168.1.1',
195
- });
196
- });
197
- });
198
-
199
- describe('Validators: validators', () => {
200
- it('should redact PII attributes with default policy', async () => {
201
- const testFn = trace((ctx: TraceContext) => async () => {
202
- safeSetAttributes(
203
- ctx,
204
- attrs.user.data({ email: 'sensitive@example.com' }),
205
- {
206
- guardrails: { pii: 'redact' },
207
- },
208
- );
209
- });
210
-
211
- await testFn();
212
-
213
- const spans = collector.getSpans();
214
- expect(spans).toHaveLength(1);
215
- expect(spans[0]!.attributes['user.email']).toBe('[REDACTED]');
216
- });
217
-
218
- it('should allow PII when guardrails pii is "allow"', async () => {
219
- const testFn = trace((ctx: TraceContext) => async () => {
220
- safeSetAttributes(
221
- ctx,
222
- attrs.user.data({ email: 'sensitive@example.com' }),
223
- {
224
- guardrails: { pii: 'allow' },
225
- },
226
- );
227
- });
228
-
229
- await testFn();
230
-
231
- const spans = collector.getSpans();
232
- expect(spans).toHaveLength(1);
233
- expect(spans[0]!.attributes['user.email']).toBe('sensitive@example.com');
234
- });
235
-
236
- it('should hash PII when guardrails pii is "hash"', async () => {
237
- const testFn = trace((ctx: TraceContext) => async () => {
238
- safeSetAttributes(
239
- ctx,
240
- attrs.user.data({ email: 'sensitive@example.com' }),
241
- {
242
- guardrails: { pii: 'hash' },
243
- },
244
- );
245
- });
246
-
247
- await testFn();
248
-
249
- const spans = collector.getSpans();
250
- expect(spans).toHaveLength(1);
251
- expect(spans[0]!.attributes['user.email']).toMatch(/^hash_[a-f0-9]+$/);
252
- });
253
-
254
- it('should log warning for deprecated attributes', async () => {
255
- const consoleWarnSpy = vi.spyOn(console, 'warn');
256
-
257
- const testFn = trace((ctx: TraceContext) => async () => {
258
- // Use an actually deprecated attribute (http.method -> http.request.method)
259
- safeSetAttributes(
260
- ctx,
261
- { 'http.method': 'GET' },
262
- {
263
- guardrails: { warnDeprecated: true },
264
- },
265
- );
266
- });
267
-
268
- await testFn();
269
-
270
- expect(consoleWarnSpy).toHaveBeenCalledWith(
271
- expect.stringContaining('deprecated'),
272
- );
273
- consoleWarnSpy.mockRestore();
274
- });
275
-
276
- it('should truncate values exceeding maxLength', async () => {
277
- const testFn = trace((ctx: TraceContext) => async () => {
278
- safeSetAttributes(ctx, attrs.user.data({ id: 'a'.repeat(300) }), {
279
- guardrails: { maxLength: 255 },
280
- });
281
- });
282
-
283
- await testFn();
284
-
285
- const spans = collector.getSpans();
286
- expect(spans).toHaveLength(1);
287
- const userId = spans[0]!.attributes['user.id'] as string;
288
- expect(userId.length).toBeLessThanOrEqual(255);
289
- expect(userId).toMatch(/\.\.\.$/);
290
- });
291
- });
292
- });
package/src/auto.ts DELETED
@@ -1,67 +0,0 @@
1
- /**
2
- * Zero-config ESM instrumentation with auto-init from YAML/environment variables
3
- *
4
- * This module provides the simplest possible setup for OpenTelemetry instrumentation.
5
- * Just import it and everything is configured from autotel.yaml or environment variables.
6
- *
7
- * Usage with YAML config (recommended):
8
- * ```bash
9
- * # Create autotel.yaml in project root, then:
10
- * tsx --import autotel/auto src/index.ts
11
- * ```
12
- *
13
- * Usage with environment variables:
14
- * ```bash
15
- * OTEL_SERVICE_NAME=my-app tsx --import autotel/auto src/index.ts
16
- * ```
17
- *
18
- * No instrumentation.ts file needed!
19
- *
20
- * Configuration Priority (highest to lowest):
21
- * 1. YAML file (autotel.yaml or AUTOTEL_CONFIG_FILE)
22
- * 2. Environment variables (OTEL_*, AUTOTEL_*)
23
- * 3. Built-in defaults
24
- *
25
- * Environment Variables:
26
- * - OTEL_SERVICE_NAME: Service name (required for meaningful traces)
27
- * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (e.g., http://localhost:4318)
28
- * - OTEL_EXPORTER_OTLP_HEADERS: Auth headers (e.g., x-honeycomb-team=YOUR_KEY)
29
- * - AUTOTEL_INTEGRATIONS: Comma-separated list or 'true' for all (default: http,express)
30
- * - AUTOTEL_DEBUG: Set to 'true' to enable console span output
31
- * - AUTOTEL_CONFIG_FILE: Path to YAML config file (overrides autotel.yaml discovery)
32
- *
33
- * @requires Node.js 20.6.0 or later
34
- */
35
-
36
- import { register } from 'node:module';
37
- import { createAddHookMessageChannel } from 'import-in-the-middle';
38
- import { init } from './init';
39
- import { loadYamlConfig } from './yaml-config';
40
-
41
- // Register ESM hooks first (must happen before any instrumented modules load)
42
- const { registerOptions } = createAddHookMessageChannel();
43
- register('import-in-the-middle/hook.mjs', import.meta.url, registerOptions);
44
-
45
- // Load YAML config if present (init.ts will also load it, but we need values here)
46
- const yamlConfig = loadYamlConfig();
47
-
48
- // Parse auto-instrumentations from environment variable (fallback if not in YAML)
49
- const autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;
50
- const autoInstrumentations:
51
- | string[]
52
- | boolean
53
- | Record<string, { enabled?: boolean }> =
54
- autoInstrumentationsEnv === 'true'
55
- ? true // Enable all auto-instrumentations
56
- : autoInstrumentationsEnv
57
- ? autoInstrumentationsEnv.split(',').map((s) => s.trim())
58
- : (yamlConfig?.autoInstrumentations ?? ['http', 'express']); // YAML > default
59
-
60
- // Auto-initialize with YAML config merged with env var defaults
61
- // init() will load YAML again and merge properly, but we pass overrides here
62
- init({
63
- service:
64
- yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? 'unknown-service',
65
- debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === 'true',
66
- autoInstrumentations,
67
- });