@virtu3d/event-manager 0.2.7
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/LICENSE +21 -0
- package/README.md +624 -0
- package/dist/browser/index.d.mts +631 -0
- package/dist/browser/index.d.ts +631 -0
- package/dist/browser/index.js +1140 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/index.mjs +1104 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/context/index.d.mts +111 -0
- package/dist/context/index.d.ts +111 -0
- package/dist/context/index.js +186 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/index.mjs +176 -0
- package/dist/context/index.mjs.map +1 -0
- package/dist/index.d.mts +568 -0
- package/dist/index.d.ts +568 -0
- package/dist/index.js +1044 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +999 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/context/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Context Propagation Utilities\n *\n * Works in both Node.js and Browser environments.\n * Uses @opentelemetry/api for trace context propagation.\n */\n\n/**\n * Trace context structure\n */\nexport interface TraceContext {\n traceId: string;\n spanId: string;\n traceFlags: number;\n}\n\n/**\n * Get current trace headers for manual injection into HTTP requests.\n * Returns headers like { traceparent: '00-abc123...', tracestate: '...' }\n *\n * If there's an active OTEL span, uses its context.\n * Otherwise, generates a new traceparent to enable backend trace correlation.\n *\n * @example\n * ```typescript\n * const headers = getTraceHeaders();\n * fetch('/api/data', { headers });\n * ```\n */\nexport async function getTraceHeaders(): Promise<Record<string, string>> {\n try {\n const { context, propagation, trace } = await import('@opentelemetry/api');\n const headers: Record<string, string> = {};\n propagation.inject(context.active(), headers);\n\n // If OTEL didn't provide traceparent (no active span), generate one\n // This ensures trace context is always propagated to backends\n if (!headers['traceparent']) {\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n headers['traceparent'] = generateTraceparent(traceId, spanId, 1);\n }\n\n return headers;\n } catch {\n // OTEL not available, generate traceparent manually\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n return {\n traceparent: generateTraceparent(traceId, spanId, 1),\n };\n }\n}\n\n/**\n * Synchronous version of getTraceHeaders (requires OTEL to be pre-loaded)\n * Use this when you know OTEL is already initialized.\n *\n * If there's an active OTEL span, uses its context.\n * Otherwise, generates a new traceparent to enable backend trace correlation.\n */\nexport function getTraceHeadersSync(): Record<string, string> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { context, propagation } = require('@opentelemetry/api');\n const headers: Record<string, string> = {};\n propagation.inject(context.active(), headers);\n\n // If OTEL didn't provide traceparent (no active span), generate one\n if (!headers['traceparent']) {\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n headers['traceparent'] = generateTraceparent(traceId, spanId, 1);\n }\n\n return headers;\n } catch {\n // OTEL not available, generate traceparent manually\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n return {\n traceparent: generateTraceparent(traceId, spanId, 1),\n };\n }\n}\n\n/**\n * Extract trace context from incoming headers.\n * Use this in middleware to link incoming requests to their parent trace.\n *\n * @example\n * ```typescript\n * // Express middleware\n * app.use((req, res, next) => {\n * const traceContext = extractTraceContext(req.headers);\n * if (traceContext) {\n * req.traceId = traceContext.traceId;\n * }\n * next();\n * });\n * ```\n */\nexport async function extractTraceContext(\n headers: Record<string, string | string[] | undefined>\n): Promise<TraceContext | null> {\n try {\n const { trace, context, propagation, isSpanContextValid } = await import('@opentelemetry/api');\n\n // Normalize headers to string values (lowercase keys for consistency)\n const normalizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const lowerKey = key.toLowerCase();\n if (typeof value === 'string') {\n normalizedHeaders[lowerKey] = value;\n } else if (Array.isArray(value)) {\n normalizedHeaders[lowerKey] = value[0] || '';\n }\n }\n\n const extractedContext = propagation.extract(context.active(), normalizedHeaders);\n\n // Use getSpanContext instead of getSpan - when extracting from headers,\n // there's no Span object, only SpanContext information\n const spanContext = trace.getSpanContext(extractedContext);\n\n if (!spanContext || !isSpanContextValid(spanContext)) {\n // If OTEL extraction failed, try manual parsing of traceparent\n const traceparent = normalizedHeaders['traceparent'];\n if (traceparent) {\n const parsed = parseTraceparent(traceparent);\n if (parsed) {\n return {\n traceId: parsed.traceId,\n spanId: parsed.spanId,\n traceFlags: parsed.traceFlags,\n };\n }\n }\n return null;\n }\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n };\n } catch {\n // Fallback to manual parsing if OTEL is not available\n const traceparent = headers['traceparent'] || headers['Traceparent'];\n if (traceparent) {\n const value = typeof traceparent === 'string' ? traceparent : traceparent[0];\n const parsed = parseTraceparent(value || '');\n if (parsed) {\n return {\n traceId: parsed.traceId,\n spanId: parsed.spanId,\n traceFlags: parsed.traceFlags,\n };\n }\n }\n return null;\n }\n}\n\n/**\n * Run a function within an extracted trace context.\n * This ensures any spans created within the function are linked to the parent trace.\n *\n * @example\n * ```typescript\n * await withTraceContext(req.headers, async () => {\n * // Any spans created here will be children of the incoming trace\n * await processRequest();\n * });\n * ```\n */\nexport async function withTraceContext<T>(\n headers: Record<string, string | string[] | undefined>,\n fn: () => T | Promise<T>\n): Promise<T> {\n try {\n const { context, propagation } = await import('@opentelemetry/api');\n\n // Normalize headers\n const normalizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (typeof value === 'string') {\n normalizedHeaders[key] = value;\n } else if (Array.isArray(value)) {\n normalizedHeaders[key] = value[0] || '';\n }\n }\n\n const extractedContext = propagation.extract(context.active(), normalizedHeaders);\n return context.with(extractedContext, fn);\n } catch {\n // OTEL not available, run function without context\n return fn();\n }\n}\n\n/**\n * Create a new span for tracking an operation.\n * The span will automatically be linked to the current trace context.\n *\n * @example\n * ```typescript\n * const span = await startSpan('fetch-user-data');\n * try {\n * const user = await fetchUser(id);\n * span.setAttribute('user.id', id);\n * span.setStatus({ code: SpanStatusCode.OK });\n * } catch (error) {\n * span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n * throw error;\n * } finally {\n * span.end();\n * }\n * ```\n */\nexport async function startSpan(\n name: string,\n serviceName = 'telemetry'\n): Promise<any> {\n try {\n const { trace } = await import('@opentelemetry/api');\n const tracer = trace.getTracer(serviceName);\n return tracer.startSpan(name);\n } catch {\n // Return a no-op span if OTEL is not available\n return {\n setAttribute: () => {},\n setStatus: () => {},\n addEvent: () => {},\n end: () => {},\n spanContext: () => ({ traceId: '', spanId: '', traceFlags: 0 }),\n };\n }\n}\n\n/**\n * Parse a traceparent header string into its components.\n * Format: version-traceId-spanId-traceFlags (e.g., \"00-abc123...-def456...-01\")\n */\nexport function parseTraceparent(\n traceparent: string\n): { version: string; traceId: string; spanId: string; traceFlags: number } | null {\n if (!traceparent) return null;\n\n const parts = traceparent.split('-');\n if (parts.length !== 4) return null;\n\n const [version, traceId, spanId, flags] = parts;\n\n // Validate format\n if (version.length !== 2 || traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {\n return null;\n }\n\n return {\n version,\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n/**\n * Generate a traceparent header string.\n * Useful for creating custom trace contexts.\n */\nexport function generateTraceparent(\n traceId: string,\n spanId: string,\n traceFlags = 1\n): string {\n const flagsHex = traceFlags.toString(16).padStart(2, '0');\n return `00-${traceId}-${spanId}-${flagsHex}`;\n}\n\n/**\n * Generate a random trace ID (32 hex characters)\n */\nexport function generateTraceId(): string {\n const bytes = new Uint8Array(16);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n // Fallback for environments without crypto\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate a random span ID (16 hex characters)\n */\nexport function generateSpanId(): string {\n const bytes = new Uint8Array(8);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n"]}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/context/index.ts
|
|
9
|
+
async function getTraceHeaders() {
|
|
10
|
+
try {
|
|
11
|
+
const { context, propagation, trace } = await import('@opentelemetry/api');
|
|
12
|
+
const headers = {};
|
|
13
|
+
propagation.inject(context.active(), headers);
|
|
14
|
+
if (!headers["traceparent"]) {
|
|
15
|
+
const traceId = generateTraceId();
|
|
16
|
+
const spanId = generateSpanId();
|
|
17
|
+
headers["traceparent"] = generateTraceparent(traceId, spanId, 1);
|
|
18
|
+
}
|
|
19
|
+
return headers;
|
|
20
|
+
} catch {
|
|
21
|
+
const traceId = generateTraceId();
|
|
22
|
+
const spanId = generateSpanId();
|
|
23
|
+
return {
|
|
24
|
+
traceparent: generateTraceparent(traceId, spanId, 1)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function getTraceHeadersSync() {
|
|
29
|
+
try {
|
|
30
|
+
const { context, propagation } = __require("@opentelemetry/api");
|
|
31
|
+
const headers = {};
|
|
32
|
+
propagation.inject(context.active(), headers);
|
|
33
|
+
if (!headers["traceparent"]) {
|
|
34
|
+
const traceId = generateTraceId();
|
|
35
|
+
const spanId = generateSpanId();
|
|
36
|
+
headers["traceparent"] = generateTraceparent(traceId, spanId, 1);
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
} catch {
|
|
40
|
+
const traceId = generateTraceId();
|
|
41
|
+
const spanId = generateSpanId();
|
|
42
|
+
return {
|
|
43
|
+
traceparent: generateTraceparent(traceId, spanId, 1)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function extractTraceContext(headers) {
|
|
48
|
+
try {
|
|
49
|
+
const { trace, context, propagation, isSpanContextValid } = await import('@opentelemetry/api');
|
|
50
|
+
const normalizedHeaders = {};
|
|
51
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
52
|
+
const lowerKey = key.toLowerCase();
|
|
53
|
+
if (typeof value === "string") {
|
|
54
|
+
normalizedHeaders[lowerKey] = value;
|
|
55
|
+
} else if (Array.isArray(value)) {
|
|
56
|
+
normalizedHeaders[lowerKey] = value[0] || "";
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const extractedContext = propagation.extract(context.active(), normalizedHeaders);
|
|
60
|
+
const spanContext = trace.getSpanContext(extractedContext);
|
|
61
|
+
if (!spanContext || !isSpanContextValid(spanContext)) {
|
|
62
|
+
const traceparent = normalizedHeaders["traceparent"];
|
|
63
|
+
if (traceparent) {
|
|
64
|
+
const parsed = parseTraceparent(traceparent);
|
|
65
|
+
if (parsed) {
|
|
66
|
+
return {
|
|
67
|
+
traceId: parsed.traceId,
|
|
68
|
+
spanId: parsed.spanId,
|
|
69
|
+
traceFlags: parsed.traceFlags
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
traceId: spanContext.traceId,
|
|
77
|
+
spanId: spanContext.spanId,
|
|
78
|
+
traceFlags: spanContext.traceFlags
|
|
79
|
+
};
|
|
80
|
+
} catch {
|
|
81
|
+
const traceparent = headers["traceparent"] || headers["Traceparent"];
|
|
82
|
+
if (traceparent) {
|
|
83
|
+
const value = typeof traceparent === "string" ? traceparent : traceparent[0];
|
|
84
|
+
const parsed = parseTraceparent(value || "");
|
|
85
|
+
if (parsed) {
|
|
86
|
+
return {
|
|
87
|
+
traceId: parsed.traceId,
|
|
88
|
+
spanId: parsed.spanId,
|
|
89
|
+
traceFlags: parsed.traceFlags
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function withTraceContext(headers, fn) {
|
|
97
|
+
try {
|
|
98
|
+
const { context, propagation } = await import('@opentelemetry/api');
|
|
99
|
+
const normalizedHeaders = {};
|
|
100
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
101
|
+
if (typeof value === "string") {
|
|
102
|
+
normalizedHeaders[key] = value;
|
|
103
|
+
} else if (Array.isArray(value)) {
|
|
104
|
+
normalizedHeaders[key] = value[0] || "";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const extractedContext = propagation.extract(context.active(), normalizedHeaders);
|
|
108
|
+
return context.with(extractedContext, fn);
|
|
109
|
+
} catch {
|
|
110
|
+
return fn();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function startSpan(name, serviceName = "telemetry") {
|
|
114
|
+
try {
|
|
115
|
+
const { trace } = await import('@opentelemetry/api');
|
|
116
|
+
const tracer = trace.getTracer(serviceName);
|
|
117
|
+
return tracer.startSpan(name);
|
|
118
|
+
} catch {
|
|
119
|
+
return {
|
|
120
|
+
setAttribute: () => {
|
|
121
|
+
},
|
|
122
|
+
setStatus: () => {
|
|
123
|
+
},
|
|
124
|
+
addEvent: () => {
|
|
125
|
+
},
|
|
126
|
+
end: () => {
|
|
127
|
+
},
|
|
128
|
+
spanContext: () => ({ traceId: "", spanId: "", traceFlags: 0 })
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function parseTraceparent(traceparent) {
|
|
133
|
+
if (!traceparent) return null;
|
|
134
|
+
const parts = traceparent.split("-");
|
|
135
|
+
if (parts.length !== 4) return null;
|
|
136
|
+
const [version, traceId, spanId, flags] = parts;
|
|
137
|
+
if (version.length !== 2 || traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
version,
|
|
142
|
+
traceId,
|
|
143
|
+
spanId,
|
|
144
|
+
traceFlags: parseInt(flags, 16)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function generateTraceparent(traceId, spanId, traceFlags = 1) {
|
|
148
|
+
const flagsHex = traceFlags.toString(16).padStart(2, "0");
|
|
149
|
+
return `00-${traceId}-${spanId}-${flagsHex}`;
|
|
150
|
+
}
|
|
151
|
+
function generateTraceId() {
|
|
152
|
+
const bytes = new Uint8Array(16);
|
|
153
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
154
|
+
crypto.getRandomValues(bytes);
|
|
155
|
+
} else {
|
|
156
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
157
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
161
|
+
}
|
|
162
|
+
function generateSpanId() {
|
|
163
|
+
const bytes = new Uint8Array(8);
|
|
164
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
165
|
+
crypto.getRandomValues(bytes);
|
|
166
|
+
} else {
|
|
167
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
168
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { extractTraceContext, generateSpanId, generateTraceId, generateTraceparent, getTraceHeaders, getTraceHeadersSync, parseTraceparent, startSpan, withTraceContext };
|
|
175
|
+
//# sourceMappingURL=index.mjs.map
|
|
176
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/context/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Context Propagation Utilities\n *\n * Works in both Node.js and Browser environments.\n * Uses @opentelemetry/api for trace context propagation.\n */\n\n/**\n * Trace context structure\n */\nexport interface TraceContext {\n traceId: string;\n spanId: string;\n traceFlags: number;\n}\n\n/**\n * Get current trace headers for manual injection into HTTP requests.\n * Returns headers like { traceparent: '00-abc123...', tracestate: '...' }\n *\n * If there's an active OTEL span, uses its context.\n * Otherwise, generates a new traceparent to enable backend trace correlation.\n *\n * @example\n * ```typescript\n * const headers = getTraceHeaders();\n * fetch('/api/data', { headers });\n * ```\n */\nexport async function getTraceHeaders(): Promise<Record<string, string>> {\n try {\n const { context, propagation, trace } = await import('@opentelemetry/api');\n const headers: Record<string, string> = {};\n propagation.inject(context.active(), headers);\n\n // If OTEL didn't provide traceparent (no active span), generate one\n // This ensures trace context is always propagated to backends\n if (!headers['traceparent']) {\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n headers['traceparent'] = generateTraceparent(traceId, spanId, 1);\n }\n\n return headers;\n } catch {\n // OTEL not available, generate traceparent manually\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n return {\n traceparent: generateTraceparent(traceId, spanId, 1),\n };\n }\n}\n\n/**\n * Synchronous version of getTraceHeaders (requires OTEL to be pre-loaded)\n * Use this when you know OTEL is already initialized.\n *\n * If there's an active OTEL span, uses its context.\n * Otherwise, generates a new traceparent to enable backend trace correlation.\n */\nexport function getTraceHeadersSync(): Record<string, string> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { context, propagation } = require('@opentelemetry/api');\n const headers: Record<string, string> = {};\n propagation.inject(context.active(), headers);\n\n // If OTEL didn't provide traceparent (no active span), generate one\n if (!headers['traceparent']) {\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n headers['traceparent'] = generateTraceparent(traceId, spanId, 1);\n }\n\n return headers;\n } catch {\n // OTEL not available, generate traceparent manually\n const traceId = generateTraceId();\n const spanId = generateSpanId();\n return {\n traceparent: generateTraceparent(traceId, spanId, 1),\n };\n }\n}\n\n/**\n * Extract trace context from incoming headers.\n * Use this in middleware to link incoming requests to their parent trace.\n *\n * @example\n * ```typescript\n * // Express middleware\n * app.use((req, res, next) => {\n * const traceContext = extractTraceContext(req.headers);\n * if (traceContext) {\n * req.traceId = traceContext.traceId;\n * }\n * next();\n * });\n * ```\n */\nexport async function extractTraceContext(\n headers: Record<string, string | string[] | undefined>\n): Promise<TraceContext | null> {\n try {\n const { trace, context, propagation, isSpanContextValid } = await import('@opentelemetry/api');\n\n // Normalize headers to string values (lowercase keys for consistency)\n const normalizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n const lowerKey = key.toLowerCase();\n if (typeof value === 'string') {\n normalizedHeaders[lowerKey] = value;\n } else if (Array.isArray(value)) {\n normalizedHeaders[lowerKey] = value[0] || '';\n }\n }\n\n const extractedContext = propagation.extract(context.active(), normalizedHeaders);\n\n // Use getSpanContext instead of getSpan - when extracting from headers,\n // there's no Span object, only SpanContext information\n const spanContext = trace.getSpanContext(extractedContext);\n\n if (!spanContext || !isSpanContextValid(spanContext)) {\n // If OTEL extraction failed, try manual parsing of traceparent\n const traceparent = normalizedHeaders['traceparent'];\n if (traceparent) {\n const parsed = parseTraceparent(traceparent);\n if (parsed) {\n return {\n traceId: parsed.traceId,\n spanId: parsed.spanId,\n traceFlags: parsed.traceFlags,\n };\n }\n }\n return null;\n }\n\n return {\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n traceFlags: spanContext.traceFlags,\n };\n } catch {\n // Fallback to manual parsing if OTEL is not available\n const traceparent = headers['traceparent'] || headers['Traceparent'];\n if (traceparent) {\n const value = typeof traceparent === 'string' ? traceparent : traceparent[0];\n const parsed = parseTraceparent(value || '');\n if (parsed) {\n return {\n traceId: parsed.traceId,\n spanId: parsed.spanId,\n traceFlags: parsed.traceFlags,\n };\n }\n }\n return null;\n }\n}\n\n/**\n * Run a function within an extracted trace context.\n * This ensures any spans created within the function are linked to the parent trace.\n *\n * @example\n * ```typescript\n * await withTraceContext(req.headers, async () => {\n * // Any spans created here will be children of the incoming trace\n * await processRequest();\n * });\n * ```\n */\nexport async function withTraceContext<T>(\n headers: Record<string, string | string[] | undefined>,\n fn: () => T | Promise<T>\n): Promise<T> {\n try {\n const { context, propagation } = await import('@opentelemetry/api');\n\n // Normalize headers\n const normalizedHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers)) {\n if (typeof value === 'string') {\n normalizedHeaders[key] = value;\n } else if (Array.isArray(value)) {\n normalizedHeaders[key] = value[0] || '';\n }\n }\n\n const extractedContext = propagation.extract(context.active(), normalizedHeaders);\n return context.with(extractedContext, fn);\n } catch {\n // OTEL not available, run function without context\n return fn();\n }\n}\n\n/**\n * Create a new span for tracking an operation.\n * The span will automatically be linked to the current trace context.\n *\n * @example\n * ```typescript\n * const span = await startSpan('fetch-user-data');\n * try {\n * const user = await fetchUser(id);\n * span.setAttribute('user.id', id);\n * span.setStatus({ code: SpanStatusCode.OK });\n * } catch (error) {\n * span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n * throw error;\n * } finally {\n * span.end();\n * }\n * ```\n */\nexport async function startSpan(\n name: string,\n serviceName = 'telemetry'\n): Promise<any> {\n try {\n const { trace } = await import('@opentelemetry/api');\n const tracer = trace.getTracer(serviceName);\n return tracer.startSpan(name);\n } catch {\n // Return a no-op span if OTEL is not available\n return {\n setAttribute: () => {},\n setStatus: () => {},\n addEvent: () => {},\n end: () => {},\n spanContext: () => ({ traceId: '', spanId: '', traceFlags: 0 }),\n };\n }\n}\n\n/**\n * Parse a traceparent header string into its components.\n * Format: version-traceId-spanId-traceFlags (e.g., \"00-abc123...-def456...-01\")\n */\nexport function parseTraceparent(\n traceparent: string\n): { version: string; traceId: string; spanId: string; traceFlags: number } | null {\n if (!traceparent) return null;\n\n const parts = traceparent.split('-');\n if (parts.length !== 4) return null;\n\n const [version, traceId, spanId, flags] = parts;\n\n // Validate format\n if (version.length !== 2 || traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {\n return null;\n }\n\n return {\n version,\n traceId,\n spanId,\n traceFlags: parseInt(flags, 16),\n };\n}\n\n/**\n * Generate a traceparent header string.\n * Useful for creating custom trace contexts.\n */\nexport function generateTraceparent(\n traceId: string,\n spanId: string,\n traceFlags = 1\n): string {\n const flagsHex = traceFlags.toString(16).padStart(2, '0');\n return `00-${traceId}-${spanId}-${flagsHex}`;\n}\n\n/**\n * Generate a random trace ID (32 hex characters)\n */\nexport function generateTraceId(): string {\n const bytes = new Uint8Array(16);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n // Fallback for environments without crypto\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate a random span ID (16 hex characters)\n */\nexport function generateSpanId(): string {\n const bytes = new Uint8Array(8);\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n"]}
|