autotel-web 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Privacy controls for autotel-web
3
+ *
4
+ * Provides origin filtering and privacy signal respecting (DNT, GPC)
5
+ * to ensure compliance with GDPR, CCPA, and user privacy preferences.
6
+ */
7
+ interface PrivacyConfig {
8
+ /**
9
+ * Only inject traceparent headers on requests to these origins (whitelist)
10
+ *
11
+ * If specified, traceparent will ONLY be injected on matching origins.
12
+ * Origins are matched using substring matching (e.g., "example.com" matches "https://api.example.com").
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * {
17
+ * allowedOrigins: ['api.myapp.com', 'myapp.com']
18
+ * }
19
+ * ```
20
+ */
21
+ allowedOrigins?: string[];
22
+ /**
23
+ * Never inject traceparent headers on requests to these origins (blacklist)
24
+ *
25
+ * Takes precedence over allowedOrigins.
26
+ * Origins are matched using substring matching.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * {
31
+ * blockedOrigins: ['analytics.google.com', 'facebook.com']
32
+ * }
33
+ * ```
34
+ */
35
+ blockedOrigins?: string[];
36
+ /**
37
+ * Respect the Do Not Track (DNT) browser setting
38
+ *
39
+ * If true and user has DNT enabled, no traceparent headers will be injected.
40
+ *
41
+ * @default false
42
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/doNotTrack
43
+ */
44
+ respectDoNotTrack?: boolean;
45
+ /**
46
+ * Respect the Global Privacy Control (GPC) browser signal
47
+ *
48
+ * If true and user has GPC enabled, no traceparent headers will be injected.
49
+ *
50
+ * @default false
51
+ * @see https://globalprivacycontrol.org/
52
+ */
53
+ respectGPC?: boolean;
54
+ }
55
+
56
+ /**
57
+ * Minimal browser SDK initialization
58
+ *
59
+ * Patches fetch() and XMLHttpRequest to automatically inject W3C traceparent headers.
60
+ * NO OpenTelemetry dependencies - just native browser APIs.
61
+ *
62
+ * Bundle size: ~2-5KB gzipped
63
+ */
64
+
65
+ interface AutotelWebConfig {
66
+ /**
67
+ * Service name for the browser application
68
+ * Used only for logging/debugging - not sent in headers
69
+ */
70
+ service: string;
71
+ /**
72
+ * Enable debug logging to console
73
+ * @default false
74
+ */
75
+ debug?: boolean;
76
+ /**
77
+ * Enable automatic traceparent injection on fetch calls
78
+ * @default true
79
+ */
80
+ instrumentFetch?: boolean;
81
+ /**
82
+ * Enable automatic traceparent injection on XMLHttpRequest
83
+ * @default true
84
+ */
85
+ instrumentXHR?: boolean;
86
+ /**
87
+ * Privacy controls for traceparent header injection
88
+ *
89
+ * Configure origin filtering and privacy signal respecting (DNT, GPC)
90
+ * to ensure compliance with GDPR, CCPA, and user privacy preferences.
91
+ *
92
+ * @example Basic origin filtering
93
+ * ```typescript
94
+ * {
95
+ * privacy: {
96
+ * allowedOrigins: ['api.myapp.com'], // Only inject on API calls
97
+ * respectDoNotTrack: true // Respect user's DNT setting
98
+ * }
99
+ * }
100
+ * ```
101
+ *
102
+ * @example Block third-party analytics
103
+ * ```typescript
104
+ * {
105
+ * privacy: {
106
+ * blockedOrigins: ['analytics.google.com', 'facebook.com']
107
+ * }
108
+ * }
109
+ * ```
110
+ */
111
+ privacy?: PrivacyConfig;
112
+ }
113
+ /**
114
+ * Initialize autotel-web
115
+ *
116
+ * Patches fetch() and XMLHttpRequest to auto-inject traceparent headers.
117
+ *
118
+ * **SSR-safe:** Safe to call in SSR environments (checks for window).
119
+ * **Call once:** Subsequent calls are ignored.
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * import { init } from 'autotel-web'
124
+ *
125
+ * init({ service: 'my-frontend-app' })
126
+ *
127
+ * // Now all fetch/XHR calls include traceparent headers!
128
+ * fetch('/api/users') // <-- traceparent header automatically injected
129
+ * ```
130
+ *
131
+ * @example With React (client-only)
132
+ * ```typescript
133
+ * import { useEffect } from 'react'
134
+ * import { init } from 'autotel-web'
135
+ *
136
+ * function App() {
137
+ * useEffect(() => {
138
+ * init({ service: 'my-spa' })
139
+ * }, [])
140
+ *
141
+ * return <div>...</div>
142
+ * }
143
+ * ```
144
+ */
145
+ declare function init(userConfig: AutotelWebConfig): void;
146
+
147
+ /**
148
+ * Minimal functional API for browser tracing
149
+ *
150
+ * These are DX wrappers that DON'T create real browser spans.
151
+ * The real spans and timing happen on the backend via Autotel.
152
+ *
153
+ * The browser's job is just to propagate trace context via headers.
154
+ */
155
+ /**
156
+ * Minimal trace context (browser-side)
157
+ *
158
+ * This is a lightweight version that just holds IDs.
159
+ * NO actual span object - the real span lives on the backend.
160
+ */
161
+ interface TraceContext {
162
+ /** Current trace ID (may be extracted from request) */
163
+ readonly traceId: string;
164
+ /** Current span ID (generated for this browser "span") */
165
+ readonly spanId: string;
166
+ /** Correlation ID (same as trace ID) */
167
+ readonly correlationId: string;
168
+ }
169
+ /**
170
+ * Wrap a function with trace() for better DX
171
+ *
172
+ * **Important:** This does NOT create real spans in the browser.
173
+ * It's purely for API consistency. The real tracing happens on the backend.
174
+ *
175
+ * The traceparent header is automatically injected by init() on fetch/XHR calls.
176
+ *
177
+ * @example Basic usage
178
+ * ```typescript
179
+ * const fetchUser = trace(async (id: string) => {
180
+ * const response = await fetch(`/api/users/${id}`)
181
+ * return response.json()
182
+ * })
183
+ * ```
184
+ *
185
+ * @example With context (for accessing trace IDs)
186
+ * ```typescript
187
+ * const fetchUser = trace(ctx => async (id: string) => {
188
+ * console.log('Trace ID:', ctx.traceId)
189
+ * const response = await fetch(`/api/users/${id}`)
190
+ * return response.json()
191
+ * })
192
+ * ```
193
+ */
194
+ declare function trace<T extends (...args: any[]) => any>(fn: T | ((ctx: TraceContext) => T)): T;
195
+ /**
196
+ * Get the current trace context (if any)
197
+ *
198
+ * @returns Current trace context or undefined
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * const ctx = getActiveContext()
203
+ * if (ctx) {
204
+ * console.log('Trace ID:', ctx.traceId)
205
+ * }
206
+ * ```
207
+ */
208
+ declare function getActiveContext(): TraceContext | undefined;
209
+ /**
210
+ * Manual helper to create a traceparent header
211
+ *
212
+ * Useful if you need to manually set headers or disable auto-instrumentation.
213
+ *
214
+ * @returns W3C traceparent header value
215
+ *
216
+ * @example
217
+ * ```typescript
218
+ * import { init, getTraceparent } from 'autotel-web'
219
+ *
220
+ * // Disable auto-instrumentation
221
+ * init({ service: 'my-app', instrumentFetch: false })
222
+ *
223
+ * // Manually inject headers
224
+ * fetch('/api/data', {
225
+ * headers: {
226
+ * 'traceparent': getTraceparent()
227
+ * }
228
+ * })
229
+ * ```
230
+ */
231
+ declare function getTraceparent(): string;
232
+ /**
233
+ * Extract trace context from a traceparent header
234
+ *
235
+ * Useful for SSR scenarios where you want to continue a trace from the server.
236
+ *
237
+ * @param traceparent - W3C traceparent header value
238
+ * @returns Parsed trace context or undefined if invalid
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * // In an SSR handler
243
+ * const traceparent = request.headers.get('traceparent')
244
+ * if (traceparent) {
245
+ * const ctx = extractContext(traceparent)
246
+ * console.log('Continuing trace:', ctx?.traceId)
247
+ * }
248
+ * ```
249
+ */
250
+ declare function extractContext(traceparent: string): TraceContext | undefined;
251
+
252
+ /**
253
+ * Minimal W3C Trace Context implementation for browser
254
+ *
255
+ * Generates traceparent headers in the W3C format:
256
+ * traceparent: 00-{trace-id}-{span-id}-{flags}
257
+ *
258
+ * No OpenTelemetry dependencies - just crypto.getRandomValues()
259
+ */
260
+ /**
261
+ * Generate a random 128-bit (16 byte) trace ID
262
+ * @returns 32 character hex string
263
+ */
264
+ declare function generateTraceId(): string;
265
+ /**
266
+ * Generate a random 64-bit (8 byte) span ID
267
+ * @returns 16 character hex string
268
+ */
269
+ declare function generateSpanId(): string;
270
+ /**
271
+ * Create a W3C traceparent header value
272
+ *
273
+ * Format: version-traceId-spanId-flags
274
+ * - version: 00 (W3C Trace Context spec)
275
+ * - traceId: 128-bit hex (32 chars)
276
+ * - spanId: 64-bit hex (16 chars)
277
+ * - flags: 01 (sampled)
278
+ *
279
+ * @param traceId - Optional existing trace ID (for continuing traces)
280
+ * @param parentSpanId - Optional parent span ID (unused in browser, included for API compat)
281
+ * @returns W3C traceparent header value
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * const header = createTraceparent()
286
+ * // "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
287
+ * ```
288
+ */
289
+ declare function createTraceparent(traceId?: string, _parentSpanId?: string): string;
290
+ /**
291
+ * Parse a traceparent header value
292
+ * Useful for extracting trace context from incoming headers
293
+ *
294
+ * @param traceparent - W3C traceparent header value
295
+ * @returns Parsed components or null if invalid
296
+ *
297
+ * @example
298
+ * ```typescript
299
+ * const parsed = parseTraceparent('00-4bf92f...0e4736-00f067...902b7-01')
300
+ * console.log(parsed?.traceId) // "4bf92f...0e4736"
301
+ * ```
302
+ */
303
+ declare function parseTraceparent(traceparent: string): {
304
+ version: string;
305
+ traceId: string;
306
+ spanId: string;
307
+ flags: string;
308
+ } | null;
309
+
310
+ export { type AutotelWebConfig, type PrivacyConfig, type TraceContext, createTraceparent, extractContext, generateSpanId, generateTraceId, getActiveContext, getTraceparent, init, parseTraceparent, trace };
package/dist/index.js ADDED
@@ -0,0 +1,391 @@
1
+ // src/traceparent.ts
2
+ function randomHex(bytes) {
3
+ const buffer = new Uint8Array(bytes);
4
+ crypto.getRandomValues(buffer);
5
+ return Array.from(buffer).map((b) => b.toString(16).padStart(2, "0")).join("");
6
+ }
7
+ function generateTraceId() {
8
+ return randomHex(16);
9
+ }
10
+ function generateSpanId() {
11
+ return randomHex(8);
12
+ }
13
+ function createTraceparent(traceId, _parentSpanId) {
14
+ const tid = traceId ?? generateTraceId();
15
+ const sid = generateSpanId();
16
+ const flags = "01";
17
+ return `00-${tid}-${sid}-${flags}`;
18
+ }
19
+ function parseTraceparent(traceparent) {
20
+ const parts = traceparent.split("-");
21
+ if (parts.length !== 4) {
22
+ return null;
23
+ }
24
+ const [version, traceId, spanId, flags] = parts;
25
+ if (version.length !== 2 || traceId.length !== 32 || spanId.length !== 16 || flags.length !== 2) {
26
+ return null;
27
+ }
28
+ return { version, traceId, spanId, flags };
29
+ }
30
+
31
+ // src/privacy.ts
32
+ var PrivacyManager = class {
33
+ constructor(config2) {
34
+ this.config = config2;
35
+ }
36
+ /**
37
+ * Check if traceparent header should be injected for a given URL
38
+ *
39
+ * Decision order:
40
+ * 1. Check Do Not Track (if enabled)
41
+ * 2. Check Global Privacy Control (if enabled)
42
+ * 3. Check blockedOrigins (explicit deny)
43
+ * 4. Check allowedOrigins (explicit allow, if configured)
44
+ * 5. Default: allow
45
+ *
46
+ * @param url - Full URL or relative path of the request
47
+ * @returns true if traceparent should be injected, false otherwise
48
+ */
49
+ shouldInjectTraceparent(url) {
50
+ if (this.config.respectDoNotTrack && this.isDoNotTrackEnabled()) {
51
+ return false;
52
+ }
53
+ if (this.config.respectGPC && this.isGPCEnabled()) {
54
+ return false;
55
+ }
56
+ const targetOrigin = this.extractOrigin(url);
57
+ if (this.config.blockedOrigins && this.matchesAnyOrigin(targetOrigin, this.config.blockedOrigins)) {
58
+ return false;
59
+ }
60
+ if (this.config.allowedOrigins && this.config.allowedOrigins.length > 0) {
61
+ return this.matchesAnyOrigin(targetOrigin, this.config.allowedOrigins);
62
+ }
63
+ return true;
64
+ }
65
+ /**
66
+ * Check if Do Not Track is enabled in the browser
67
+ */
68
+ isDoNotTrackEnabled() {
69
+ if (typeof navigator === "undefined") return false;
70
+ return navigator.doNotTrack === "1";
71
+ }
72
+ /**
73
+ * Check if Global Privacy Control is enabled in the browser
74
+ */
75
+ isGPCEnabled() {
76
+ if (typeof navigator === "undefined") return false;
77
+ const nav = navigator;
78
+ return nav.globalPrivacyControl === true;
79
+ }
80
+ /**
81
+ * Extract origin from a URL (handles both absolute and relative URLs)
82
+ *
83
+ * @param url - Full URL or relative path
84
+ * @returns Origin string (e.g., "https://api.example.com")
85
+ */
86
+ extractOrigin(url) {
87
+ try {
88
+ if (url.startsWith("http://") || url.startsWith("https://")) {
89
+ return new URL(url).origin;
90
+ }
91
+ if (typeof window !== "undefined") {
92
+ return new URL(url, window.location.href).origin;
93
+ }
94
+ return "";
95
+ } catch {
96
+ return "";
97
+ }
98
+ }
99
+ /**
100
+ * Check if a target origin matches any of the configured origins
101
+ *
102
+ * Uses substring matching for flexibility (e.g., "example.com" matches "https://api.example.com")
103
+ *
104
+ * @param targetOrigin - Origin to check
105
+ * @param configuredOrigins - List of allowed or blocked origins
106
+ * @returns true if any origin matches
107
+ */
108
+ matchesAnyOrigin(targetOrigin, configuredOrigins) {
109
+ return configuredOrigins.some((configuredOrigin) => {
110
+ const normalizedTarget = targetOrigin.toLowerCase();
111
+ const normalizedConfigured = configuredOrigin.toLowerCase();
112
+ return normalizedTarget.includes(normalizedConfigured);
113
+ });
114
+ }
115
+ };
116
+ function getDenialReason(privacyManager2, url) {
117
+ const config2 = privacyManager2.config;
118
+ if (config2.respectDoNotTrack && typeof navigator !== "undefined") {
119
+ if (navigator.doNotTrack === "1") {
120
+ return "Do Not Track is enabled";
121
+ }
122
+ }
123
+ if (config2.respectGPC && typeof navigator !== "undefined") {
124
+ const nav = navigator;
125
+ if (nav.globalPrivacyControl === true) {
126
+ return "Global Privacy Control is enabled";
127
+ }
128
+ }
129
+ let targetOrigin = "";
130
+ try {
131
+ if (url.startsWith("http://") || url.startsWith("https://")) {
132
+ targetOrigin = new URL(url).origin;
133
+ } else if (typeof window !== "undefined") {
134
+ targetOrigin = new URL(url, window.location.href).origin;
135
+ }
136
+ } catch {
137
+ return "Invalid URL";
138
+ }
139
+ if (config2.blockedOrigins) {
140
+ const blocked = config2.blockedOrigins.some(
141
+ (origin) => targetOrigin.toLowerCase().includes(origin.toLowerCase())
142
+ );
143
+ if (blocked) {
144
+ return `Origin ${targetOrigin} is in blockedOrigins list`;
145
+ }
146
+ }
147
+ if (config2.allowedOrigins && config2.allowedOrigins.length > 0) {
148
+ const allowed = config2.allowedOrigins.some(
149
+ (origin) => targetOrigin.toLowerCase().includes(origin.toLowerCase())
150
+ );
151
+ if (!allowed) {
152
+ return `Origin ${targetOrigin} is not in allowedOrigins list`;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+
158
+ // src/init.ts
159
+ var isInitialized = false;
160
+ var config;
161
+ var privacyManager;
162
+ var originalFetch;
163
+ var originalXHROpen;
164
+ var originalXHRSetRequestHeader;
165
+ function init(userConfig) {
166
+ if (typeof window === "undefined") {
167
+ return;
168
+ }
169
+ if (isInitialized) {
170
+ if (userConfig.debug) {
171
+ console.warn("[autotel-web] Already initialized. Skipping.");
172
+ }
173
+ return;
174
+ }
175
+ validateConfig(userConfig);
176
+ config = userConfig;
177
+ if (config.privacy) {
178
+ privacyManager = new PrivacyManager(config.privacy);
179
+ }
180
+ if (config.instrumentFetch !== false) {
181
+ patchFetch();
182
+ }
183
+ if (config.instrumentXHR !== false) {
184
+ patchXMLHttpRequest();
185
+ }
186
+ isInitialized = true;
187
+ if (config.debug) {
188
+ console.log("[autotel-web] Initialized successfully", {
189
+ service: config.service,
190
+ instrumentFetch: config.instrumentFetch !== false,
191
+ instrumentXHR: config.instrumentXHR !== false,
192
+ privacyEnabled: !!config.privacy,
193
+ privacyConfig: config.privacy ? {
194
+ allowedOrigins: config.privacy.allowedOrigins?.length ?? 0,
195
+ blockedOrigins: config.privacy.blockedOrigins?.length ?? 0,
196
+ respectDoNotTrack: config.privacy.respectDoNotTrack ?? false,
197
+ respectGPC: config.privacy.respectGPC ?? false
198
+ } : null
199
+ });
200
+ }
201
+ }
202
+ function patchFetch() {
203
+ originalFetch = window.fetch.bind(window);
204
+ window.fetch = function(input, init2) {
205
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
206
+ const headers = new Headers(init2?.headers);
207
+ if (!headers.has("traceparent")) {
208
+ if (privacyManager && !privacyManager.shouldInjectTraceparent(url)) {
209
+ if (config?.debug) {
210
+ const reason = getDenialReason(privacyManager, url);
211
+ console.log(
212
+ "[autotel-web] Skipped traceparent on fetch (privacy):",
213
+ url,
214
+ reason
215
+ );
216
+ }
217
+ } else {
218
+ headers.set("traceparent", createTraceparent());
219
+ if (config?.debug) {
220
+ console.log(
221
+ "[autotel-web] Injected traceparent on fetch:",
222
+ url,
223
+ headers.get("traceparent")
224
+ );
225
+ }
226
+ }
227
+ }
228
+ return originalFetch(input, { ...init2, headers });
229
+ };
230
+ }
231
+ function patchXMLHttpRequest() {
232
+ originalXHROpen = XMLHttpRequest.prototype.open;
233
+ originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
234
+ const xhrHasTraceparent = /* @__PURE__ */ new WeakSet();
235
+ XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
236
+ if (name.toLowerCase() === "traceparent") {
237
+ xhrHasTraceparent.add(this);
238
+ }
239
+ return originalXHRSetRequestHeader.call(this, name, value);
240
+ };
241
+ XMLHttpRequest.prototype.open = function(method, url, async = true, username, password) {
242
+ const result = originalXHROpen.call(this, method, url, async, username, password);
243
+ const urlStr = typeof url === "string" ? url : url.toString();
244
+ const xhr = this;
245
+ const originalOnReadyStateChange = xhr.onreadystatechange;
246
+ xhr.onreadystatechange = function(event) {
247
+ if (xhr.readyState === XMLHttpRequest.OPENED) {
248
+ if (!xhrHasTraceparent.has(xhr)) {
249
+ if (privacyManager && !privacyManager.shouldInjectTraceparent(urlStr)) {
250
+ if (config?.debug) {
251
+ const reason = getDenialReason(privacyManager, urlStr);
252
+ console.log(
253
+ "[autotel-web] Skipped traceparent on XHR (privacy):",
254
+ urlStr,
255
+ reason
256
+ );
257
+ }
258
+ } else {
259
+ try {
260
+ const traceparent = createTraceparent();
261
+ originalXHRSetRequestHeader.call(xhr, "traceparent", traceparent);
262
+ if (config?.debug) {
263
+ console.log(
264
+ "[autotel-web] Injected traceparent on XHR:",
265
+ urlStr,
266
+ traceparent
267
+ );
268
+ }
269
+ } catch (error) {
270
+ if (config?.debug) {
271
+ console.warn(
272
+ "[autotel-web] Failed to inject traceparent on XHR:",
273
+ error
274
+ );
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ if (originalOnReadyStateChange) {
281
+ return originalOnReadyStateChange.call(xhr, event);
282
+ }
283
+ };
284
+ return result;
285
+ };
286
+ }
287
+ function validateConfig(userConfig) {
288
+ if (!userConfig.service || typeof userConfig.service !== "string") {
289
+ throw new Error("[autotel-web] service name is required and must be a string");
290
+ }
291
+ if (userConfig.service.length === 0) {
292
+ throw new Error("[autotel-web] service name cannot be empty");
293
+ }
294
+ if (userConfig.service.length > 255) {
295
+ console.warn(
296
+ "[autotel-web] service name is very long (> 255 chars). Consider using a shorter name."
297
+ );
298
+ }
299
+ if (userConfig.privacy) {
300
+ const { allowedOrigins, blockedOrigins } = userConfig.privacy;
301
+ if ((!allowedOrigins || allowedOrigins.length === 0) && (!blockedOrigins || blockedOrigins.length === 0) && !userConfig.privacy.respectDoNotTrack && !userConfig.privacy.respectGPC) {
302
+ console.warn(
303
+ "[autotel-web] privacy config provided but all options are empty/disabled. This has no effect."
304
+ );
305
+ }
306
+ if (allowedOrigins && blockedOrigins) {
307
+ const overlap = allowedOrigins.filter(
308
+ (allowed) => blockedOrigins.some(
309
+ (blocked) => allowed.toLowerCase().includes(blocked.toLowerCase())
310
+ )
311
+ );
312
+ if (overlap.length > 0) {
313
+ console.warn(
314
+ "[autotel-web] Some allowedOrigins match blockedOrigins. Blocklist takes precedence:",
315
+ overlap
316
+ );
317
+ }
318
+ }
319
+ const allOrigins = [
320
+ ...allowedOrigins ?? [],
321
+ ...blockedOrigins ?? []
322
+ ];
323
+ allOrigins.forEach((origin) => {
324
+ if (origin.includes("://")) {
325
+ console.warn(
326
+ `[autotel-web] Origin "${origin}" includes protocol (://) - this is usually not needed. Just use the domain name.`
327
+ );
328
+ }
329
+ });
330
+ }
331
+ }
332
+
333
+ // src/functional.ts
334
+ var currentContext;
335
+ function trace(fn) {
336
+ const expectsContext = isFactoryPattern(fn);
337
+ if (expectsContext) {
338
+ return ((...args) => {
339
+ const ctx = createContext();
340
+ const actualFn = fn(ctx);
341
+ return actualFn(...args);
342
+ });
343
+ }
344
+ return fn;
345
+ }
346
+ function isFactoryPattern(fn) {
347
+ if (typeof fn !== "function") return false;
348
+ const fnStr = fn.toString();
349
+ const contextHints = ["ctx", "context", "traceContext"];
350
+ return contextHints.some((hint) => {
351
+ const regex = new RegExp(`^\\s*(?:async\\s+)?(?:function\\s*)?\\(?\\s*${hint}\\s*[,)]`);
352
+ return regex.test(fnStr);
353
+ });
354
+ }
355
+ function createContext() {
356
+ const traceparent = createTraceparent();
357
+ const parsed = parseTraceparent(traceparent);
358
+ if (!parsed) {
359
+ return {
360
+ traceId: "",
361
+ spanId: "",
362
+ correlationId: ""
363
+ };
364
+ }
365
+ const ctx = {
366
+ traceId: parsed.traceId,
367
+ spanId: parsed.spanId,
368
+ correlationId: parsed.traceId
369
+ };
370
+ currentContext = ctx;
371
+ return ctx;
372
+ }
373
+ function getActiveContext() {
374
+ return currentContext;
375
+ }
376
+ function getTraceparent() {
377
+ return createTraceparent();
378
+ }
379
+ function extractContext(traceparent) {
380
+ const parsed = parseTraceparent(traceparent);
381
+ if (!parsed) return void 0;
382
+ return {
383
+ traceId: parsed.traceId,
384
+ spanId: parsed.spanId,
385
+ correlationId: parsed.traceId
386
+ };
387
+ }
388
+
389
+ export { createTraceparent, extractContext, generateSpanId, generateTraceId, getActiveContext, getTraceparent, init, parseTraceparent, trace };
390
+ //# sourceMappingURL=index.js.map
391
+ //# sourceMappingURL=index.js.map