autotel 6.0.0 → 6.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.
package/README.md CHANGED
@@ -1827,6 +1827,8 @@ export const checkout = withTracing({})((ctx) => async (order: Order) => {
1827
1827
  log.set({
1828
1828
  cart: { total_cents: order.totalCents, item_count: order.items.length },
1829
1829
  });
1830
+ // Set snapshot severity without fabricating a log event or exception.
1831
+ log.setLevel('info');
1830
1832
 
1831
1833
  try {
1832
1834
  await processPayment(order);
@@ -1849,6 +1851,9 @@ export const checkout = withTracing({})((ctx) => async (order: Order) => {
1849
1851
  You also get:
1850
1852
 
1851
1853
  - `log.getContext()` to inspect the accumulated request context.
1854
+ - `log.setLevel('debug' | 'info' | 'warn' | 'error')` to set canonical snapshot
1855
+ severity explicitly. An explicit level wins over later inferred warn/error
1856
+ levels.
1852
1857
  - `log.emitNow(overrides?)` to capture an immediate snapshot (emits a correlated log-style snapshot
1853
1858
  and returns `{ timestamp, traceId, spanId, correlationId, context }`).
1854
1859
 
@@ -1862,7 +1867,15 @@ import { createDrainPipeline, init } from 'autotel';
1862
1867
 
1863
1868
  const pipeline = createDrainPipeline({
1864
1869
  batch: { size: 50, intervalMs: 2000 },
1865
- retry: { maxAttempts: 3, backoff: 'exponential' },
1870
+ retry: {
1871
+ maxAttempts: 3,
1872
+ backoff: 'exponential',
1873
+ // Do not retry permanent client errors. Keep 429 retryable if desired.
1874
+ shouldRetry: (error) =>
1875
+ !(error instanceof HttpError) ||
1876
+ error.status === 429 ||
1877
+ error.status >= 500,
1878
+ },
1866
1879
  maxBufferSize: 1000,
1867
1880
  });
1868
1881
 
@@ -15,6 +15,7 @@ function createDrainPipeline(options) {
15
15
  const initialDelayMs = options?.retry?.initialDelayMs ?? 1e3;
16
16
  const maxDelayMs = options?.retry?.maxDelayMs ?? 3e4;
17
17
  const jitter = options?.retry?.jitter ?? true;
18
+ const shouldRetry = options?.retry?.shouldRetry;
18
19
  const dropPolicy = options?.dropPolicy ?? "oldest";
19
20
  const onDropped = options?.onDropped;
20
21
  if (!Number.isFinite(batchSize) || batchSize <= 0) throw new Error(`[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`);
@@ -46,7 +47,15 @@ function createDrainPipeline(options) {
46
47
  return;
47
48
  } catch (error) {
48
49
  lastError = error instanceof Error ? error : new Error(String(error));
49
- if (attempt < maxAttempts) await wait(computeDelay(attempt));
50
+ let retry = attempt < maxAttempts;
51
+ if (retry && shouldRetry) try {
52
+ retry = shouldRetry(lastError, attempt, batch);
53
+ } catch (classifierError) {
54
+ lastError = classifierError instanceof Error ? classifierError : new Error(String(classifierError));
55
+ retry = false;
56
+ }
57
+ if (retry) await wait(computeDelay(attempt));
58
+ else break;
50
59
  }
51
60
  onDropped?.(batch, lastError);
52
61
  };
@@ -1 +1 @@
1
- {"version":3,"file":"drain-pipeline.cjs","names":[],"sources":["../src/drain-pipeline.ts"],"sourcesContent":["export interface DrainPipelineOptions<T = unknown> {\n batch?: {\n /** Maximum events per batch. @default 50 */\n size?: number;\n /** Max time an event can stay buffered before flush. @default 5000 */\n intervalMs?: number;\n };\n retry?: {\n /** Total attempts including first try. @default 3 */\n maxAttempts?: number;\n /** Delay strategy between attempts. @default 'exponential' */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Base delay for first retry. @default 1000 */\n initialDelayMs?: number;\n /** Max delay cap. @default 30000 */\n maxDelayMs?: number;\n /** Add random jitter to delays. @default true */\n jitter?: boolean;\n };\n /** Max buffered events before dropping. @default 1000 */\n maxBufferSize?: number;\n /** Overflow policy. @default 'oldest' */\n dropPolicy?: 'oldest' | 'newest';\n /** Called when events are dropped from overflow or exhausted retries. */\n onDropped?: (events: T[], error?: Error) => void;\n}\n\nexport interface PipelineDrainFn<T> {\n (ctx: T): void;\n /** Flush all buffered events. */\n flush: () => Promise<void>;\n /** Flush and stop scheduling future timer work. */\n shutdown: () => Promise<void>;\n readonly pending: number;\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nexport function createDrainPipeline<T = unknown>(\n options?: DrainPipelineOptions<T>,\n): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {\n const batchSize = options?.batch?.size ?? 50;\n const intervalMs = options?.batch?.intervalMs ?? 5000;\n const maxBufferSize = options?.maxBufferSize ?? 1000;\n const maxAttempts = options?.retry?.maxAttempts ?? 3;\n const backoff = options?.retry?.backoff ?? 'exponential';\n const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;\n const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;\n const jitter = options?.retry?.jitter ?? true;\n const dropPolicy = options?.dropPolicy ?? 'oldest';\n const onDropped = options?.onDropped;\n\n if (!Number.isFinite(batchSize) || batchSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,\n );\n }\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,\n );\n }\n if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,\n );\n }\n if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,\n );\n }\n\n return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {\n const buffer: T[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n let activeFlush: Promise<void> | null = null;\n let isShutdown = false;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const computeDelay = (attempt: number): number => {\n const base =\n backoff === 'fixed'\n ? initialDelayMs\n : backoff === 'linear'\n ? initialDelayMs * attempt\n : initialDelayMs * 2 ** (attempt - 1);\n\n const bounded = Math.min(base, maxDelayMs);\n if (!jitter || bounded <= 0) return bounded;\n const factor = 0.5 + Math.random(); // [0.5, 1.5)\n return Math.max(0, Math.round(bounded * factor));\n };\n\n const sendWithRetry = async (batch: T[]): Promise<void> => {\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await drain(batch);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempt < maxAttempts) {\n await wait(computeDelay(attempt));\n }\n }\n }\n onDropped?.(batch, lastError);\n };\n\n const drainBuffer = async (): Promise<void> => {\n while (buffer.length > 0) {\n const batch = buffer.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const scheduleFlush = () => {\n if (isShutdown || timer || activeFlush) return;\n timer = setTimeout(() => {\n timer = null;\n startFlush();\n }, intervalMs);\n timer.unref?.();\n };\n\n const startFlush = () => {\n if (activeFlush || isShutdown) return;\n activeFlush = drainBuffer().finally(() => {\n activeFlush = null;\n if (isShutdown) return;\n if (buffer.length >= batchSize) {\n startFlush();\n } else if (buffer.length > 0) {\n scheduleFlush();\n }\n });\n };\n\n const push = (ctx: T) => {\n if (isShutdown) return;\n\n if (buffer.length >= maxBufferSize) {\n if (dropPolicy === 'newest') {\n onDropped?.([ctx]);\n return;\n }\n const dropped = buffer.splice(0, 1);\n onDropped?.(dropped);\n }\n\n buffer.push(ctx);\n if (buffer.length >= batchSize) {\n clearTimer();\n startFlush();\n } else {\n scheduleFlush();\n }\n };\n\n const flush = async (): Promise<void> => {\n clearTimer();\n if (activeFlush) await activeFlush;\n\n const snapshot = buffer.length;\n if (snapshot <= 0) return;\n const toFlush = buffer.splice(0, snapshot);\n while (toFlush.length > 0) {\n const batch = toFlush.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const shutdown = async (): Promise<void> => {\n isShutdown = true;\n await flush();\n };\n\n const fn = push as PipelineDrainFn<T>;\n fn.flush = flush;\n fn.shutdown = shutdown;\n Object.defineProperty(fn, 'pending', {\n enumerable: true,\n get: () => buffer.length,\n });\n return fn;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drain-pipeline.cjs","names":[],"sources":["../src/drain-pipeline.ts"],"sourcesContent":["export interface DrainPipelineOptions<T = unknown> {\n batch?: {\n /** Maximum events per batch. @default 50 */\n size?: number;\n /** Max time an event can stay buffered before flush. @default 5000 */\n intervalMs?: number;\n };\n retry?: {\n /** Total attempts including first try. @default 3 */\n maxAttempts?: number;\n /** Delay strategy between attempts. @default 'exponential' */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Base delay for first retry. @default 1000 */\n initialDelayMs?: number;\n /** Max delay cap. @default 30000 */\n maxDelayMs?: number;\n /** Add random jitter to delays. @default true */\n jitter?: boolean;\n /**\n * Decide whether a failed attempt is safe to repeat. Return false for\n * permanent failures such as an HTTP 4xx response. @default always retry\n */\n shouldRetry?: (\n error: Error,\n attempt: number,\n batch: readonly T[],\n ) => boolean;\n };\n /** Max buffered events before dropping. @default 1000 */\n maxBufferSize?: number;\n /** Overflow policy. @default 'oldest' */\n dropPolicy?: 'oldest' | 'newest';\n /** Called when events are dropped from overflow or exhausted retries. */\n onDropped?: (events: T[], error?: Error) => void;\n}\n\nexport interface PipelineDrainFn<T> {\n (ctx: T): void;\n /** Flush all buffered events. */\n flush: () => Promise<void>;\n /** Flush and stop scheduling future timer work. */\n shutdown: () => Promise<void>;\n readonly pending: number;\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nexport function createDrainPipeline<T = unknown>(\n options?: DrainPipelineOptions<T>,\n): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {\n const batchSize = options?.batch?.size ?? 50;\n const intervalMs = options?.batch?.intervalMs ?? 5000;\n const maxBufferSize = options?.maxBufferSize ?? 1000;\n const maxAttempts = options?.retry?.maxAttempts ?? 3;\n const backoff = options?.retry?.backoff ?? 'exponential';\n const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;\n const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;\n const jitter = options?.retry?.jitter ?? true;\n const shouldRetry = options?.retry?.shouldRetry;\n const dropPolicy = options?.dropPolicy ?? 'oldest';\n const onDropped = options?.onDropped;\n\n if (!Number.isFinite(batchSize) || batchSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,\n );\n }\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,\n );\n }\n if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,\n );\n }\n if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,\n );\n }\n\n return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {\n const buffer: T[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n let activeFlush: Promise<void> | null = null;\n let isShutdown = false;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const computeDelay = (attempt: number): number => {\n const base =\n backoff === 'fixed'\n ? initialDelayMs\n : backoff === 'linear'\n ? initialDelayMs * attempt\n : initialDelayMs * 2 ** (attempt - 1);\n\n const bounded = Math.min(base, maxDelayMs);\n if (!jitter || bounded <= 0) return bounded;\n const factor = 0.5 + Math.random(); // [0.5, 1.5)\n return Math.max(0, Math.round(bounded * factor));\n };\n\n const sendWithRetry = async (batch: T[]): Promise<void> => {\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await drain(batch);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n let retry = attempt < maxAttempts;\n if (retry && shouldRetry) {\n try {\n retry = shouldRetry(lastError, attempt, batch);\n } catch (classifierError) {\n lastError =\n classifierError instanceof Error\n ? classifierError\n : new Error(String(classifierError));\n retry = false;\n }\n }\n if (retry) {\n await wait(computeDelay(attempt));\n } else {\n break;\n }\n }\n }\n onDropped?.(batch, lastError);\n };\n\n const drainBuffer = async (): Promise<void> => {\n while (buffer.length > 0) {\n const batch = buffer.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const scheduleFlush = () => {\n if (isShutdown || timer || activeFlush) return;\n timer = setTimeout(() => {\n timer = null;\n startFlush();\n }, intervalMs);\n timer.unref?.();\n };\n\n const startFlush = () => {\n if (activeFlush || isShutdown) return;\n activeFlush = drainBuffer().finally(() => {\n activeFlush = null;\n if (isShutdown) return;\n if (buffer.length >= batchSize) {\n startFlush();\n } else if (buffer.length > 0) {\n scheduleFlush();\n }\n });\n };\n\n const push = (ctx: T) => {\n if (isShutdown) return;\n\n if (buffer.length >= maxBufferSize) {\n if (dropPolicy === 'newest') {\n onDropped?.([ctx]);\n return;\n }\n const dropped = buffer.splice(0, 1);\n onDropped?.(dropped);\n }\n\n buffer.push(ctx);\n if (buffer.length >= batchSize) {\n clearTimer();\n startFlush();\n } else {\n scheduleFlush();\n }\n };\n\n const flush = async (): Promise<void> => {\n clearTimer();\n if (activeFlush) await activeFlush;\n\n const snapshot = buffer.length;\n if (snapshot <= 0) return;\n const toFlush = buffer.splice(0, snapshot);\n while (toFlush.length > 0) {\n const batch = toFlush.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const shutdown = async (): Promise<void> => {\n isShutdown = true;\n await flush();\n };\n\n const fn = push as PipelineDrainFn<T>;\n fn.flush = flush;\n fn.shutdown = shutdown;\n Object.defineProperty(fn, 'pending', {\n enumerable: true,\n get: () => buffer.length,\n });\n return fn;\n };\n}\n"],"mappings":"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"}
@@ -17,6 +17,11 @@ interface DrainPipelineOptions<T = unknown> {
17
17
  maxDelayMs?: number;
18
18
  /** Add random jitter to delays. @default true */
19
19
  jitter?: boolean;
20
+ /**
21
+ * Decide whether a failed attempt is safe to repeat. Return false for
22
+ * permanent failures such as an HTTP 4xx response. @default always retry
23
+ */
24
+ shouldRetry?: (error: Error, attempt: number, batch: readonly T[]) => boolean;
20
25
  };
21
26
  /** Max buffered events before dropping. @default 1000 */
22
27
  maxBufferSize?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"drain-pipeline.d.cts","names":[],"sources":["../src/drain-pipeline.ts"],"mappings":";UAAiB,qBAAqB;EACpC;;IAEE;;IAEA;;EAEF;;IAEE;;IAEA;;IAEA;;IAEA;;IAEA;;;EAGF;;EAEA;;EAEA,aAAa,QAAQ,KAAK,QAAQ;;UAGnB,gBAAgB;GAC9B,KAAK;;EAEN,aAAa;;EAEb,gBAAgB;WACP;;iBAUK,oBAAoB,aAClC,UAAU,qBAAqB,MAC7B,QAAQ,OAAO,eAAe,kBAAkB,gBAAgB"}
1
+ {"version":3,"file":"drain-pipeline.d.cts","names":[],"sources":["../src/drain-pipeline.ts"],"mappings":";UAAiB,qBAAqB;EACpC;;IAEE;;IAEA;;EAEF;;IAEE;;IAEA;;IAEA;;IAEA;;IAEA;;;;;IAKA,eACE,OAAO,OACP,iBACA,gBAAgB;;;EAIpB;;EAEA;;EAEA,aAAa,QAAQ,KAAK,QAAQ;;UAGnB,gBAAgB;GAC9B,KAAK;;EAEN,aAAa;;EAEb,gBAAgB;WACP;;iBAUK,oBAAoB,aAClC,UAAU,qBAAqB,MAC7B,QAAQ,OAAO,eAAe,kBAAkB,gBAAgB"}
@@ -17,6 +17,11 @@ interface DrainPipelineOptions<T = unknown> {
17
17
  maxDelayMs?: number;
18
18
  /** Add random jitter to delays. @default true */
19
19
  jitter?: boolean;
20
+ /**
21
+ * Decide whether a failed attempt is safe to repeat. Return false for
22
+ * permanent failures such as an HTTP 4xx response. @default always retry
23
+ */
24
+ shouldRetry?: (error: Error, attempt: number, batch: readonly T[]) => boolean;
20
25
  };
21
26
  /** Max buffered events before dropping. @default 1000 */
22
27
  maxBufferSize?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"drain-pipeline.d.ts","names":[],"sources":["../src/drain-pipeline.ts"],"mappings":";UAAiB,qBAAqB;EACpC;;IAEE;;IAEA;;EAEF;;IAEE;;IAEA;;IAEA;;IAEA;;IAEA;;;EAGF;;EAEA;;EAEA,aAAa,QAAQ,KAAK,QAAQ;;UAGnB,gBAAgB;GAC9B,KAAK;;EAEN,aAAa;;EAEb,gBAAgB;WACP;;iBAUK,oBAAoB,aAClC,UAAU,qBAAqB,MAC7B,QAAQ,OAAO,eAAe,kBAAkB,gBAAgB"}
1
+ {"version":3,"file":"drain-pipeline.d.ts","names":[],"sources":["../src/drain-pipeline.ts"],"mappings":";UAAiB,qBAAqB;EACpC;;IAEE;;IAEA;;EAEF;;IAEE;;IAEA;;IAEA;;IAEA;;IAEA;;;;;IAKA,eACE,OAAO,OACP,iBACA,gBAAgB;;;EAIpB;;EAEA;;EAEA,aAAa,QAAQ,KAAK,QAAQ;;UAGnB,gBAAgB;GAC9B,KAAK;;EAEN,aAAa;;EAEb,gBAAgB;WACP;;iBAUK,oBAAoB,aAClC,UAAU,qBAAqB,MAC7B,QAAQ,OAAO,eAAe,kBAAkB,gBAAgB"}
@@ -13,6 +13,7 @@ function createDrainPipeline(options) {
13
13
  const initialDelayMs = options?.retry?.initialDelayMs ?? 1e3;
14
14
  const maxDelayMs = options?.retry?.maxDelayMs ?? 3e4;
15
15
  const jitter = options?.retry?.jitter ?? true;
16
+ const shouldRetry = options?.retry?.shouldRetry;
16
17
  const dropPolicy = options?.dropPolicy ?? "oldest";
17
18
  const onDropped = options?.onDropped;
18
19
  if (!Number.isFinite(batchSize) || batchSize <= 0) throw new Error(`[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`);
@@ -44,7 +45,15 @@ function createDrainPipeline(options) {
44
45
  return;
45
46
  } catch (error) {
46
47
  lastError = error instanceof Error ? error : new Error(String(error));
47
- if (attempt < maxAttempts) await wait(computeDelay(attempt));
48
+ let retry = attempt < maxAttempts;
49
+ if (retry && shouldRetry) try {
50
+ retry = shouldRetry(lastError, attempt, batch);
51
+ } catch (classifierError) {
52
+ lastError = classifierError instanceof Error ? classifierError : new Error(String(classifierError));
53
+ retry = false;
54
+ }
55
+ if (retry) await wait(computeDelay(attempt));
56
+ else break;
48
57
  }
49
58
  onDropped?.(batch, lastError);
50
59
  };
@@ -1 +1 @@
1
- {"version":3,"file":"drain-pipeline.js","names":[],"sources":["../src/drain-pipeline.ts"],"sourcesContent":["export interface DrainPipelineOptions<T = unknown> {\n batch?: {\n /** Maximum events per batch. @default 50 */\n size?: number;\n /** Max time an event can stay buffered before flush. @default 5000 */\n intervalMs?: number;\n };\n retry?: {\n /** Total attempts including first try. @default 3 */\n maxAttempts?: number;\n /** Delay strategy between attempts. @default 'exponential' */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Base delay for first retry. @default 1000 */\n initialDelayMs?: number;\n /** Max delay cap. @default 30000 */\n maxDelayMs?: number;\n /** Add random jitter to delays. @default true */\n jitter?: boolean;\n };\n /** Max buffered events before dropping. @default 1000 */\n maxBufferSize?: number;\n /** Overflow policy. @default 'oldest' */\n dropPolicy?: 'oldest' | 'newest';\n /** Called when events are dropped from overflow or exhausted retries. */\n onDropped?: (events: T[], error?: Error) => void;\n}\n\nexport interface PipelineDrainFn<T> {\n (ctx: T): void;\n /** Flush all buffered events. */\n flush: () => Promise<void>;\n /** Flush and stop scheduling future timer work. */\n shutdown: () => Promise<void>;\n readonly pending: number;\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nexport function createDrainPipeline<T = unknown>(\n options?: DrainPipelineOptions<T>,\n): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {\n const batchSize = options?.batch?.size ?? 50;\n const intervalMs = options?.batch?.intervalMs ?? 5000;\n const maxBufferSize = options?.maxBufferSize ?? 1000;\n const maxAttempts = options?.retry?.maxAttempts ?? 3;\n const backoff = options?.retry?.backoff ?? 'exponential';\n const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;\n const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;\n const jitter = options?.retry?.jitter ?? true;\n const dropPolicy = options?.dropPolicy ?? 'oldest';\n const onDropped = options?.onDropped;\n\n if (!Number.isFinite(batchSize) || batchSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,\n );\n }\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,\n );\n }\n if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,\n );\n }\n if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,\n );\n }\n\n return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {\n const buffer: T[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n let activeFlush: Promise<void> | null = null;\n let isShutdown = false;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const computeDelay = (attempt: number): number => {\n const base =\n backoff === 'fixed'\n ? initialDelayMs\n : backoff === 'linear'\n ? initialDelayMs * attempt\n : initialDelayMs * 2 ** (attempt - 1);\n\n const bounded = Math.min(base, maxDelayMs);\n if (!jitter || bounded <= 0) return bounded;\n const factor = 0.5 + Math.random(); // [0.5, 1.5)\n return Math.max(0, Math.round(bounded * factor));\n };\n\n const sendWithRetry = async (batch: T[]): Promise<void> => {\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await drain(batch);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempt < maxAttempts) {\n await wait(computeDelay(attempt));\n }\n }\n }\n onDropped?.(batch, lastError);\n };\n\n const drainBuffer = async (): Promise<void> => {\n while (buffer.length > 0) {\n const batch = buffer.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const scheduleFlush = () => {\n if (isShutdown || timer || activeFlush) return;\n timer = setTimeout(() => {\n timer = null;\n startFlush();\n }, intervalMs);\n timer.unref?.();\n };\n\n const startFlush = () => {\n if (activeFlush || isShutdown) return;\n activeFlush = drainBuffer().finally(() => {\n activeFlush = null;\n if (isShutdown) return;\n if (buffer.length >= batchSize) {\n startFlush();\n } else if (buffer.length > 0) {\n scheduleFlush();\n }\n });\n };\n\n const push = (ctx: T) => {\n if (isShutdown) return;\n\n if (buffer.length >= maxBufferSize) {\n if (dropPolicy === 'newest') {\n onDropped?.([ctx]);\n return;\n }\n const dropped = buffer.splice(0, 1);\n onDropped?.(dropped);\n }\n\n buffer.push(ctx);\n if (buffer.length >= batchSize) {\n clearTimer();\n startFlush();\n } else {\n scheduleFlush();\n }\n };\n\n const flush = async (): Promise<void> => {\n clearTimer();\n if (activeFlush) await activeFlush;\n\n const snapshot = buffer.length;\n if (snapshot <= 0) return;\n const toFlush = buffer.splice(0, snapshot);\n while (toFlush.length > 0) {\n const batch = toFlush.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const shutdown = async (): Promise<void> => {\n isShutdown = true;\n await flush();\n };\n\n const fn = push as PipelineDrainFn<T>;\n fn.flush = flush;\n fn.shutdown = shutdown;\n Object.defineProperty(fn, 'pending', {\n enumerable: true,\n get: () => buffer.length,\n });\n return fn;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drain-pipeline.js","names":[],"sources":["../src/drain-pipeline.ts"],"sourcesContent":["export interface DrainPipelineOptions<T = unknown> {\n batch?: {\n /** Maximum events per batch. @default 50 */\n size?: number;\n /** Max time an event can stay buffered before flush. @default 5000 */\n intervalMs?: number;\n };\n retry?: {\n /** Total attempts including first try. @default 3 */\n maxAttempts?: number;\n /** Delay strategy between attempts. @default 'exponential' */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Base delay for first retry. @default 1000 */\n initialDelayMs?: number;\n /** Max delay cap. @default 30000 */\n maxDelayMs?: number;\n /** Add random jitter to delays. @default true */\n jitter?: boolean;\n /**\n * Decide whether a failed attempt is safe to repeat. Return false for\n * permanent failures such as an HTTP 4xx response. @default always retry\n */\n shouldRetry?: (\n error: Error,\n attempt: number,\n batch: readonly T[],\n ) => boolean;\n };\n /** Max buffered events before dropping. @default 1000 */\n maxBufferSize?: number;\n /** Overflow policy. @default 'oldest' */\n dropPolicy?: 'oldest' | 'newest';\n /** Called when events are dropped from overflow or exhausted retries. */\n onDropped?: (events: T[], error?: Error) => void;\n}\n\nexport interface PipelineDrainFn<T> {\n (ctx: T): void;\n /** Flush all buffered events. */\n flush: () => Promise<void>;\n /** Flush and stop scheduling future timer work. */\n shutdown: () => Promise<void>;\n readonly pending: number;\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nexport function createDrainPipeline<T = unknown>(\n options?: DrainPipelineOptions<T>,\n): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {\n const batchSize = options?.batch?.size ?? 50;\n const intervalMs = options?.batch?.intervalMs ?? 5000;\n const maxBufferSize = options?.maxBufferSize ?? 1000;\n const maxAttempts = options?.retry?.maxAttempts ?? 3;\n const backoff = options?.retry?.backoff ?? 'exponential';\n const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;\n const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;\n const jitter = options?.retry?.jitter ?? true;\n const shouldRetry = options?.retry?.shouldRetry;\n const dropPolicy = options?.dropPolicy ?? 'oldest';\n const onDropped = options?.onDropped;\n\n if (!Number.isFinite(batchSize) || batchSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,\n );\n }\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,\n );\n }\n if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,\n );\n }\n if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,\n );\n }\n\n return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {\n const buffer: T[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n let activeFlush: Promise<void> | null = null;\n let isShutdown = false;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const computeDelay = (attempt: number): number => {\n const base =\n backoff === 'fixed'\n ? initialDelayMs\n : backoff === 'linear'\n ? initialDelayMs * attempt\n : initialDelayMs * 2 ** (attempt - 1);\n\n const bounded = Math.min(base, maxDelayMs);\n if (!jitter || bounded <= 0) return bounded;\n const factor = 0.5 + Math.random(); // [0.5, 1.5)\n return Math.max(0, Math.round(bounded * factor));\n };\n\n const sendWithRetry = async (batch: T[]): Promise<void> => {\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await drain(batch);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n let retry = attempt < maxAttempts;\n if (retry && shouldRetry) {\n try {\n retry = shouldRetry(lastError, attempt, batch);\n } catch (classifierError) {\n lastError =\n classifierError instanceof Error\n ? classifierError\n : new Error(String(classifierError));\n retry = false;\n }\n }\n if (retry) {\n await wait(computeDelay(attempt));\n } else {\n break;\n }\n }\n }\n onDropped?.(batch, lastError);\n };\n\n const drainBuffer = async (): Promise<void> => {\n while (buffer.length > 0) {\n const batch = buffer.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const scheduleFlush = () => {\n if (isShutdown || timer || activeFlush) return;\n timer = setTimeout(() => {\n timer = null;\n startFlush();\n }, intervalMs);\n timer.unref?.();\n };\n\n const startFlush = () => {\n if (activeFlush || isShutdown) return;\n activeFlush = drainBuffer().finally(() => {\n activeFlush = null;\n if (isShutdown) return;\n if (buffer.length >= batchSize) {\n startFlush();\n } else if (buffer.length > 0) {\n scheduleFlush();\n }\n });\n };\n\n const push = (ctx: T) => {\n if (isShutdown) return;\n\n if (buffer.length >= maxBufferSize) {\n if (dropPolicy === 'newest') {\n onDropped?.([ctx]);\n return;\n }\n const dropped = buffer.splice(0, 1);\n onDropped?.(dropped);\n }\n\n buffer.push(ctx);\n if (buffer.length >= batchSize) {\n clearTimer();\n startFlush();\n } else {\n scheduleFlush();\n }\n };\n\n const flush = async (): Promise<void> => {\n clearTimer();\n if (activeFlush) await activeFlush;\n\n const snapshot = buffer.length;\n if (snapshot <= 0) return;\n const toFlush = buffer.splice(0, snapshot);\n while (toFlush.length > 0) {\n const batch = toFlush.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const shutdown = async (): Promise<void> => {\n isShutdown = true;\n await flush();\n };\n\n const fn = push as PipelineDrainFn<T>;\n fn.flush = flush;\n fn.shutdown = shutdown;\n Object.defineProperty(fn, 'pending', {\n enumerable: true,\n get: () => buffer.length,\n });\n return fn;\n };\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -108,6 +108,7 @@ function getRequestLogger(ctx, options) {
108
108
  const contextState = {};
109
109
  let emitted = false;
110
110
  let lastSnapshot = null;
111
+ let hasExplicitLevel = false;
111
112
  const addLogEvent = (level, message, fields) => {
112
113
  const attrs = fields ? require_structured_error.flattenToAttributes(fields) : void 0;
113
114
  require_correlated_events.emitCorrelatedEvent(activeContext, `log.${level}`, {
@@ -125,6 +126,12 @@ function getRequestLogger(ctx, options) {
125
126
  mergeInto$1(contextState, fields);
126
127
  activeContext.setAttributes(require_structured_error.flattenToAttributes(fields));
127
128
  },
129
+ setLevel(level) {
130
+ sealCheck("log.setLevel()", ["autotel.log.level"]);
131
+ if (emitted) return;
132
+ hasExplicitLevel = true;
133
+ activeContext.setAttribute("autotel.log.level", level);
134
+ },
128
135
  info(message, fields) {
129
136
  const keys = fields ? ["message", ...Object.keys(fields).filter((k) => k !== "requestLogs")] : ["message"];
130
137
  sealCheck("log.info()", keys);
@@ -140,7 +147,7 @@ function getRequestLogger(ctx, options) {
140
147
  sealCheck("log.warn()", keys);
141
148
  if (emitted) return;
142
149
  addLogEvent("warn", message, fields);
143
- activeContext.setAttribute("autotel.log.level", "warn");
150
+ if (!hasExplicitLevel) activeContext.setAttribute("autotel.log.level", "warn");
144
151
  if (fields) {
145
152
  mergeInto$1(contextState, fields);
146
153
  activeContext.setAttributes(require_structured_error.flattenToAttributes(fields));
@@ -157,7 +164,7 @@ function getRequestLogger(ctx, options) {
157
164
  mergeInto$1(contextState, fields);
158
165
  activeContext.setAttributes(require_structured_error.flattenToAttributes(fields));
159
166
  }
160
- activeContext.setAttribute("autotel.log.level", "error");
167
+ if (!hasExplicitLevel) activeContext.setAttribute("autotel.log.level", "error");
161
168
  },
162
169
  getContext() {
163
170
  return { ...contextState };
@@ -261,6 +268,7 @@ function createNoopRequestLogger() {
261
268
  });
262
269
  return {
263
270
  set() {},
271
+ setLevel() {},
264
272
  info() {},
265
273
  warn() {},
266
274
  error() {},
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["otelTraceApi","autotelTraceFn","hashJson","mergeInto","nodeAsyncHooks","otelTrace","createTraceContext","flattenToAttributes","createStructuredError"],"sources":["../src/trace-hybrid.ts","../src/define-event.ts","../src/request-logger.ts","../src/error-catalog.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts"],"sourcesContent":["/**\n * Hybrid `trace` export: callable like autotel's `trace(fn)`, AND exposes the\n * full `@opentelemetry/api` `TraceAPI` surface (`trace.getActiveSpan()`,\n * `trace.getTracer()`, …) so existing OTel code \"just works\" when imported\n * from `autotel`.\n *\n * Implementation: `Object.assign` mutates the autotel `trace` function to\n * attach the OTel TraceAPI methods. Because every reference to `trace` across\n * autotel resolves to the same function instance, this is a one-time, global\n * augmentation.\n */\n\nimport { trace as otelTraceApi } from '@opentelemetry/api';\nimport { trace as autotelTraceFn } from './functional';\n\nconst otelMethods = {\n // Class methods on TraceAPI — bind to the singleton.\n setGlobalTracerProvider:\n otelTraceApi.setGlobalTracerProvider.bind(otelTraceApi),\n getTracerProvider: otelTraceApi.getTracerProvider.bind(otelTraceApi),\n getTracer: otelTraceApi.getTracer.bind(otelTraceApi),\n disable: otelTraceApi.disable.bind(otelTraceApi),\n // Instance fields on TraceAPI — already standalone, copy by reference.\n wrapSpanContext: otelTraceApi.wrapSpanContext,\n isSpanContextValid: otelTraceApi.isSpanContextValid,\n deleteSpan: otelTraceApi.deleteSpan,\n getSpan: otelTraceApi.getSpan,\n getActiveSpan: otelTraceApi.getActiveSpan,\n getSpanContext: otelTraceApi.getSpanContext,\n setSpan: otelTraceApi.setSpan,\n setSpanContext: otelTraceApi.setSpanContext,\n};\n\nexport const trace: typeof autotelTraceFn & typeof otelTraceApi = Object.assign(\n autotelTraceFn,\n otelMethods,\n) as typeof autotelTraceFn & typeof otelTraceApi;\n","import { track } from './track';\nimport { hashJson } from './stable-hash';\nimport type { EventSchemaMetadata } from './event-subscriber';\n\ntype SafeParseResult<T> =\n { success: true; data: T } | { success: false; error: unknown };\n\nexport interface SchemaLike<T> {\n safeParse(input: unknown): SafeParseResult<T>;\n}\n\nexport interface DefineEventOptions<S> {\n toJsonSchema?: (schema: S) => unknown;\n}\n\nexport interface DefinedEvent<Name extends string, Payload> {\n readonly name: Name;\n readonly schemaMetadata?: EventSchemaMetadata;\n track(payload: Payload): void;\n}\n\nexport function defineEvent<\n Name extends string,\n Payload,\n S extends SchemaLike<Payload>,\n>(\n name: Name,\n schema: S,\n options: DefineEventOptions<S> = {},\n): DefinedEvent<Name, Payload> {\n const jsonSchema = options.toJsonSchema?.(schema);\n const schemaMetadata = jsonSchema\n ? {\n source: 'zod' as const,\n jsonSchema,\n hash: hashJson(jsonSchema),\n }\n : undefined;\n\n return {\n name,\n schemaMetadata,\n track(payload: Payload) {\n const parsed = schema.safeParse(payload);\n if (!parsed.success) {\n throw new Error(\n `Invalid payload for event \"${name}\". Schema validation failed.`,\n );\n }\n track(\n name,\n parsed.data,\n schemaMetadata ? { schema: schemaMetadata } : undefined,\n );\n },\n };\n}\n","// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { trace as otelTrace } from '@opentelemetry/api';\nimport type { TraceContext } from './trace-context';\nimport { createTraceContext } from './trace-context';\nimport { recordStructuredError } from './structured-error';\nimport { flattenToAttributes } from './flatten-attributes';\nimport { emitCorrelatedEvent } from './correlated-events';\n\nconst POST_EMIT_FORK_HINT =\n \"For intentional background work tied to this request, use log.fork('label', fn) when available.\";\n\nfunction warnPostEmit(method: string, detail: string): void {\n console.warn(\n `[autotel] ${method} called after the wide event was emitted — ${detail} This data will not appear in observability. ${POST_EMIT_FORK_HINT}`,\n );\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n mergeInto(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n );\n } else if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {\n target[key] = [...targetVal, ...sourceVal];\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nconst requestContextStore =\n new nodeAsyncHooks.AsyncLocalStorage<TraceContext>();\n\nexport function runWithRequestContext<T>(ctx: TraceContext, fn: () => T): T {\n return requestContextStore.run(ctx, fn);\n}\n\nexport interface RequestLogger {\n set(fields: Record<string, unknown>): void;\n info(message: string, fields?: Record<string, unknown>): void;\n warn(message: string, fields?: Record<string, unknown>): void;\n error(error: Error | string, fields?: Record<string, unknown>): void;\n getContext(): Record<string, unknown>;\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;\n fork(\n label: string,\n fn: () => void | Promise<void>,\n options?: ForkOptions,\n ): void;\n}\n\nexport interface RequestLogSnapshot {\n timestamp: string;\n traceId: string;\n spanId: string;\n correlationId: string;\n context: Record<string, unknown>;\n}\n\nexport interface RequestLoggerOptions {\n /** Callback invoked by emitNow() for manual fan-out. */\n onEmit?: (snapshot: RequestLogSnapshot) => void | Promise<void>;\n}\n\n/**\n * Optional lifecycle hooks for adapters that need to track child loggers\n * spawned by `log.fork()` (e.g. active logger maps in framework integrations).\n */\nexport interface ForkLifecycle {\n /** Called after the child logger is created, before `fn` runs. */\n onChildEnter?: (child: RequestLogger) => void;\n /** Called after the child has finished (emit + drain), success or failure. */\n onChildExit?: (child: RequestLogger) => void;\n}\n\nexport interface ForkOptions {\n lifecycle?: ForkLifecycle;\n}\n\nfunction resolveContext(ctx?: TraceContext): TraceContext {\n if (ctx) return ctx;\n\n const stored = requestContextStore.getStore();\n if (stored) return stored;\n\n const span = otelTrace.getActiveSpan();\n if (!span) {\n throw new Error(\n '[autotel] getRequestLogger() requires an active span or runWithRequestContext(). Wrap your handler with trace() or use runWithRequestContext().',\n );\n }\n return createTraceContext(span);\n}\n\nexport function getRequestLogger(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger {\n const activeContext = resolveContext(ctx);\n const contextState: Record<string, unknown> = {};\n let emitted = false;\n let lastSnapshot: RequestLogSnapshot | null = null;\n\n const addLogEvent = (\n level: 'info' | 'warn' | 'error',\n message: string,\n fields?: Record<string, unknown>,\n ) => {\n const attrs = fields ? flattenToAttributes(fields) : undefined;\n emitCorrelatedEvent(activeContext, `log.${level}`, {\n message,\n ...attrs,\n });\n };\n\n const sealCheck = (method: string, keys: string[]): void => {\n if (emitted) {\n warnPostEmit(\n method,\n `Keys dropped: ${keys.length > 0 ? keys.join(', ') : '(empty)'}.`,\n );\n }\n };\n\n return {\n set(fields: Record<string, unknown>) {\n sealCheck('log.set()', Object.keys(fields));\n if (emitted) return;\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n },\n\n info(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.info()', keys);\n if (emitted) return;\n addLogEvent('info', message, fields);\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n warn(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.warn()', keys);\n if (emitted) return;\n addLogEvent('warn', message, fields);\n activeContext.setAttribute('autotel.log.level', 'warn');\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n error(error: Error | string, fields?: Record<string, unknown>) {\n const keys = fields ? [...Object.keys(fields), 'error'] : ['error'];\n sealCheck('log.error()', keys);\n if (emitted) return;\n const err = typeof error === 'string' ? new Error(error) : error;\n recordStructuredError(activeContext, err);\n addLogEvent('error', err.message, fields);\n\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n activeContext.setAttribute('autotel.log.level', 'error');\n },\n\n getContext() {\n return { ...contextState };\n },\n\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot {\n if (emitted) {\n warnPostEmit('log.emitNow()', 'Ignoring duplicate emit.');\n return lastSnapshot as RequestLogSnapshot;\n }\n\n const mergedContext = {\n ...contextState,\n ...overrides,\n };\n const flattened = flattenToAttributes(mergedContext);\n activeContext.setAttributes(flattened);\n\n const snapshot: RequestLogSnapshot = {\n timestamp: new Date().toISOString(),\n traceId: activeContext.traceId,\n spanId: activeContext.spanId,\n correlationId: activeContext.correlationId,\n context: mergedContext,\n };\n\n emitCorrelatedEvent(activeContext, 'log.emit.manual', {\n ...flattened,\n });\n\n if (options?.onEmit) {\n Promise.resolve(options.onEmit(snapshot)).catch((error) => {\n console.warn('[autotel] request logger onEmit failed:', error);\n });\n }\n\n emitted = true;\n lastSnapshot = snapshot;\n return snapshot;\n },\n\n fork(\n label: string,\n fn: () => void | Promise<void>,\n forkOptions?: ForkOptions,\n ): void {\n const parentRequestId = activeContext.correlationId;\n if (typeof parentRequestId !== 'string' || parentRequestId.length === 0) {\n throw new Error(\n '[autotel] log.fork() requires the parent logger to have a correlationId. ' +\n 'Ensure the request was created by autotel middleware.',\n );\n }\n\n const tracer = otelTrace.getTracer('autotel.request-logger');\n const lifecycle = forkOptions?.lifecycle;\n void tracer.startActiveSpan(`request.fork:${label}`, (childSpan) => {\n const childContext: TraceContext = {\n ...createTraceContext(childSpan),\n correlationId: crypto.randomUUID(),\n };\n\n requestContextStore.run(childContext, () => {\n const childLog = getRequestLogger(childContext);\n childLog.set({\n operation: label,\n _parentCorrelationId: parentRequestId,\n });\n\n lifecycle?.onChildEnter?.(childLog);\n\n void Promise.resolve()\n .then(() => fn())\n .then(() => {\n childLog.emitNow();\n })\n .catch((error_: unknown) => {\n const error =\n error_ instanceof Error ? error_ : new Error(String(error_));\n childLog.error(error);\n childLog.emitNow();\n })\n .finally(() => {\n try {\n lifecycle?.onChildExit?.(childLog);\n } catch (hookError) {\n console.warn(\n '[autotel] fork onChildExit hook threw:',\n hookError,\n );\n }\n childSpan.end();\n });\n });\n });\n },\n };\n}\n\n/**\n * Returns `true` when a request-logger context can be resolved without throwing —\n * i.e. an explicit `ctx` was provided, a `runWithRequestContext()` scope is active,\n * or there is an active OpenTelemetry span.\n *\n * Use this to branch on observability availability instead of wrapping\n * {@link getRequestLogger} in try/catch.\n */\nexport function hasRequestContext(ctx?: TraceContext): boolean {\n if (ctx) return true;\n if (requestContextStore.getStore()) return true;\n return otelTrace.getActiveSpan() != null;\n}\n\n/**\n * Like {@link getRequestLogger}, but returns `null` instead of throwing when no\n * request context is available. Intended for best-effort instrumentation where a\n * missing telemetry context must never crash business logic.\n */\nexport function getRequestLoggerSafe(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger | null {\n if (!hasRequestContext(ctx)) return null;\n return getRequestLogger(ctx, options);\n}\n\n/**\n * A no-op {@link RequestLogger} whose methods do nothing. Used as a fallback by\n * best-effort instrumentation so wrapped handlers can run un-instrumented without\n * branching on logger presence.\n */\nexport function createNoopRequestLogger(): RequestLogger {\n const snapshot = (): RequestLogSnapshot => ({\n timestamp: new Date().toISOString(),\n traceId: '',\n spanId: '',\n correlationId: '',\n context: {},\n });\n\n return {\n set() {},\n info() {},\n warn() {},\n error() {},\n getContext() {\n return {};\n },\n emitNow() {\n return snapshot();\n },\n fork(_label, fn) {\n void Promise.resolve().then(() => fn());\n },\n };\n}\n","/**\n * Typed error and audit catalogs.\n *\n * Group related errors into one catalog and get a refactor-safe builder per\n * code, with autocomplete at every call site and typed message parameters.\n *\n * @example\n * ```typescript\n * import { defineErrorCatalog } from 'autotel';\n *\n * export const billing = defineErrorCatalog('billing', {\n * PAYMENT_DECLINED: {\n * status: 402,\n * message: 'Card declined',\n * why: 'The issuer rejected the charge',\n * fix: 'Try a different payment method',\n * },\n * INSUFFICIENT_FUNDS: {\n * status: 402,\n * message: ({ available, required }: { available: number; required: number }) =>\n * `Insufficient funds: $${available} of $${required}`,\n * },\n * });\n *\n * throw billing.PAYMENT_DECLINED({ cause: stripeError });\n * throw billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });\n *\n * // In a catch block — refactor-safe, no magic strings:\n * if (billing.PAYMENT_DECLINED.match(err)) { ... }\n * ```\n */\n\nimport {\n createStructuredError,\n type StructuredError,\n} from './structured-error';\n\nconst catalogCodeKey = Symbol.for('autotel.catalog.code');\n\n/** Definition of a single error in a catalog. */\nexport interface ErrorCatalogEntry {\n /**\n * Human-readable message. Use a function to interpolate typed parameters;\n * the parameter type flows through to the call site.\n */\n message: string | ((params: never) => string);\n /** HTTP status to surface to clients. */\n status?: number;\n /** Stable error code. Defaults to `${namespace}.${KEY}`. */\n code?: string | number;\n /** Why it happened. A function receives the same params as `message`. */\n why?: string | ((params: never) => string);\n /** What the caller should do next. */\n fix?: string;\n /** Docs or runbook link. */\n link?: string;\n /** Error name. Defaults to the catalog key. */\n name?: string;\n}\n\n/** Per-call options passed alongside (or instead of) typed params. */\nexport interface ErrorBuildOptions {\n cause?: unknown;\n details?: Record<string, unknown>;\n /** Backend-only context. Never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\ntype ParamsOf<E> = E extends { message: (params: infer P) => string }\n ? P\n : E extends { why: (params: infer P) => string }\n ? P\n : void;\n\ntype BuilderArgs<E extends ErrorCatalogEntry> =\n ParamsOf<E> extends void\n ? [options?: ErrorBuildOptions]\n : [params: ParamsOf<E>, options?: ErrorBuildOptions];\n\n/** A callable error factory produced by {@link defineErrorCatalog}. */\nexport interface ErrorBuilder<E extends ErrorCatalogEntry> {\n (...args: BuilderArgs<E>): StructuredError;\n /** Stable code assigned to every error from this entry. */\n readonly code: string | number;\n /** True when `error` was produced by this catalog entry. */\n match(error: unknown): boolean;\n}\n\nexport type ErrorCatalog<T extends Record<string, ErrorCatalogEntry>> = {\n readonly [K in keyof T]: ErrorBuilder<T[K]>;\n};\n\nfunction readCatalogCode(error: unknown): string | number | undefined {\n if (error === null || typeof error !== 'object') return undefined;\n return (error as Record<symbol, unknown>)[catalogCodeKey] as\n string | number | undefined;\n}\n\n/** True when `error` was produced by any autotel error catalog. */\nexport function isCatalogError(error: unknown): error is StructuredError {\n return readCatalogCode(error) !== undefined;\n}\n\n/** Returns the catalog code of `error`, or `undefined` if it has none. */\nexport function getCatalogCode(error: unknown): string | number | undefined {\n return readCatalogCode(error);\n}\n\n/**\n * Define a typed error catalog. Returns an object whose keys are error\n * builders. Each builder produces a {@link StructuredError} carrying the\n * entry's message, status, code, why, fix, and link.\n */\nexport function defineErrorCatalog<\n const T extends Record<string, ErrorCatalogEntry>,\n>(namespace: string, entries: T): ErrorCatalog<T> {\n const catalog: Record<string, ErrorBuilder<ErrorCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n ErrorCatalogEntry,\n ][]) {\n const code = entry.code ?? `${namespace}.${key}`;\n const usesParams =\n typeof entry.message === 'function' || typeof entry.why === 'function';\n\n const builder = ((\n paramsOrOptions?: unknown,\n maybeOptions?: ErrorBuildOptions,\n ): StructuredError => {\n const params = usesParams ? paramsOrOptions : undefined;\n const options = (usesParams ? maybeOptions : paramsOrOptions) as\n ErrorBuildOptions | undefined;\n\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n const why =\n typeof entry.why === 'function'\n ? (entry.why as (p: unknown) => string)(params)\n : entry.why;\n\n const error = createStructuredError({\n message,\n name: entry.name ?? key,\n code,\n ...(entry.status === undefined ? {} : { status: entry.status }),\n ...(why === undefined ? {} : { why }),\n ...(entry.fix === undefined ? {} : { fix: entry.fix }),\n ...(entry.link === undefined ? {} : { link: entry.link }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n ...(options?.details === undefined ? {} : { details: options.details }),\n ...(options?.internal === undefined\n ? {}\n : { internal: options.internal }),\n });\n\n Object.defineProperty(error, catalogCodeKey, {\n value: code,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n return error;\n }) as ErrorBuilder<ErrorCatalogEntry>;\n\n Object.defineProperty(builder, 'code', {\n value: code,\n enumerable: true,\n });\n Object.defineProperty(builder, 'match', {\n value: (error: unknown): boolean => readCatalogCode(error) === code,\n enumerable: false,\n });\n\n catalog[key] = builder;\n }\n\n return Object.freeze(catalog) as ErrorCatalog<T>;\n}\n\n/** Severity of an audit action. */\nexport type AuditSeverity = 'info' | 'warn' | 'critical';\n\n/** Definition of a single action in an audit catalog. */\nexport interface AuditCatalogEntry {\n /** Human-readable description. Use a function for typed params. */\n message?: string | ((params: never) => string);\n /** Stable action name. Defaults to `${namespace}.${KEY}`. */\n action?: string;\n /** Severity of the action. Defaults to `'info'`. */\n severity?: AuditSeverity;\n}\n\n/** A resolved audit action descriptor produced by an audit catalog. */\nexport interface AuditAction {\n readonly action: string;\n readonly severity: AuditSeverity;\n readonly message?: string;\n}\n\ntype AuditDescriptorArgs<E extends AuditCatalogEntry> =\n ParamsOf<E> extends void ? [] : [params: ParamsOf<E>];\n\n/** A callable audit-action descriptor produced by {@link defineAuditCatalog}. */\nexport interface AuditDescriptor<E extends AuditCatalogEntry> {\n (...args: AuditDescriptorArgs<E>): AuditAction;\n readonly action: string;\n readonly severity: AuditSeverity;\n}\n\nexport type AuditCatalog<T extends Record<string, AuditCatalogEntry>> = {\n readonly [K in keyof T]: AuditDescriptor<T[K]>;\n};\n\n/**\n * Define a typed audit catalog. Returns typed action descriptors you can pass\n * to `track()` or audit helpers without scattering magic strings.\n */\nexport function defineAuditCatalog<\n const T extends Record<string, AuditCatalogEntry>,\n>(namespace: string, entries: T): AuditCatalog<T> {\n const catalog: Record<string, AuditDescriptor<AuditCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n AuditCatalogEntry,\n ][]) {\n const action = entry.action ?? `${namespace}.${key}`;\n const severity: AuditSeverity = entry.severity ?? 'info';\n\n const descriptor = ((params?: unknown): AuditAction => {\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n return Object.freeze({\n action,\n severity,\n ...(message === undefined ? {} : { message }),\n });\n }) as AuditDescriptor<AuditCatalogEntry>;\n\n Object.defineProperty(descriptor, 'action', {\n value: action,\n enumerable: true,\n });\n Object.defineProperty(descriptor, 'severity', {\n value: severity,\n enumerable: true,\n });\n\n catalog[key] = descriptor;\n }\n\n return Object.freeze(catalog) as AuditCatalog<T>;\n}\n","export interface DrainOptions<TContext, TConfig, TPayload = TContext> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Return null to skip draining (e.g. missing API key in dev). */\n resolve: () => TConfig | null | Promise<TConfig | null>;\n /** Transform contexts into payloads. Defaults to identity. */\n transform?: (contexts: TContext[]) => TPayload[];\n /** Transport implementation. */\n send: (payloads: TPayload[], config: TConfig) => Promise<void>;\n}\n\nexport interface HttpDrainRequest {\n url: string;\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface HttpDrainOptions<\n TContext,\n TConfig,\n TPayload = TContext,\n> extends Omit<DrainOptions<TContext, TConfig, TPayload>, 'send'> {\n encode: (payloads: TPayload[], config: TConfig) => HttpDrainRequest | null;\n timeoutMs?: number;\n retries?: number;\n resolveTimeoutMs?: (config: TConfig) => number | undefined;\n resolveRetries?: (config: TConfig) => number | undefined;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_RETRIES = 2;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n t.unref?.();\n });\n}\n\nasync function postWithRetry(options: {\n name: string;\n request: HttpDrainRequest;\n timeoutMs: number;\n retries: number;\n}): Promise<void> {\n const { name, request, timeoutMs, retries } = options;\n const attempts = Math.max(1, retries);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n timeout.unref?.();\n try {\n const response = await fetch(request.url, {\n method: 'POST',\n headers: request.headers,\n body: request.body,\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(\n `[autotel/${name}] HTTP ${response.status} draining ${request.url}`,\n );\n }\n return;\n } catch (error) {\n lastError = error;\n if (attempt < attempts) {\n await delay(100 * attempt);\n }\n } finally {\n clearTimeout(timeout);\n }\n }\n\n throw lastError;\n}\n\nexport function defineDrain<TContext, TConfig, TPayload = TContext>(\n options: DrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return async (ctx: TContext | TContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx];\n if (contexts.length === 0) return;\n\n const config = await options.resolve();\n if (!config) return;\n\n const payloads = options.transform\n ? options.transform(contexts)\n : (contexts as unknown as TPayload[]);\n\n if (payloads.length === 0) return;\n\n try {\n await options.send(payloads, config);\n } catch (error) {\n console.error(`[autotel/${options.name}] drain failed:`, error);\n }\n };\n}\n\nexport function defineHttpDrain<TContext, TConfig, TPayload = TContext>(\n options: HttpDrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return defineDrain<TContext, TConfig, TPayload>({\n name: options.name,\n resolve: options.resolve,\n transform: options.transform,\n send: async (payloads, config) => {\n const request = options.encode(payloads, config);\n if (!request) return;\n const timeoutMs =\n options.resolveTimeoutMs?.(config) ??\n options.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const retries =\n options.resolveRetries?.(config) ?? options.retries ?? DEFAULT_RETRIES;\n\n await postWithRetry({\n name: options.name,\n request,\n timeoutMs,\n retries,\n });\n },\n });\n}\n","export interface EnrichContext<TEvent extends Record<string, unknown>> {\n event: TEvent;\n request?: {\n method?: string;\n path?: string;\n requestId?: string;\n };\n response?: {\n status?: number;\n };\n headers?: Record<string, string>;\n}\n\nexport interface EnricherDefinition<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Top-level field to merge computed values into. */\n field: keyof TEvent & string;\n /** Return undefined to skip enrichment. */\n compute: (ctx: EnrichContext<TEvent>) => TValue | undefined;\n}\n\nexport interface EnricherOptions {\n /** Replace existing field value instead of merge. Default false. */\n overwrite?: boolean;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (isPlainObject(sourceVal) && isPlainObject(targetVal)) {\n mergeInto(targetVal, sourceVal);\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nexport function defineEnricher<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n>(\n def: EnricherDefinition<TEvent, TValue>,\n options: EnricherOptions = {},\n): (ctx: EnrichContext<TEvent>) => void {\n return (ctx: EnrichContext<TEvent>) => {\n let computed: TValue | undefined;\n try {\n computed = def.compute(ctx);\n } catch (error) {\n console.error(`[autotel/${def.name}] enrich failed:`, error);\n return;\n }\n\n if (!computed) return;\n\n if (options.overwrite || !isPlainObject(ctx.event[def.field])) {\n (ctx.event as Record<string, unknown>)[def.field] = computed;\n return;\n }\n\n mergeInto(\n ctx.event[def.field] as unknown as Record<string, unknown>,\n computed as unknown as Record<string, unknown>,\n );\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["otelTraceApi","autotelTraceFn","hashJson","mergeInto","nodeAsyncHooks","otelTrace","createTraceContext","flattenToAttributes","createStructuredError"],"sources":["../src/trace-hybrid.ts","../src/define-event.ts","../src/request-logger.ts","../src/error-catalog.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts"],"sourcesContent":["/**\n * Hybrid `trace` export: callable like autotel's `trace(fn)`, AND exposes the\n * full `@opentelemetry/api` `TraceAPI` surface (`trace.getActiveSpan()`,\n * `trace.getTracer()`, …) so existing OTel code \"just works\" when imported\n * from `autotel`.\n *\n * Implementation: `Object.assign` mutates the autotel `trace` function to\n * attach the OTel TraceAPI methods. Because every reference to `trace` across\n * autotel resolves to the same function instance, this is a one-time, global\n * augmentation.\n */\n\nimport { trace as otelTraceApi } from '@opentelemetry/api';\nimport { trace as autotelTraceFn } from './functional';\n\nconst otelMethods = {\n // Class methods on TraceAPI — bind to the singleton.\n setGlobalTracerProvider:\n otelTraceApi.setGlobalTracerProvider.bind(otelTraceApi),\n getTracerProvider: otelTraceApi.getTracerProvider.bind(otelTraceApi),\n getTracer: otelTraceApi.getTracer.bind(otelTraceApi),\n disable: otelTraceApi.disable.bind(otelTraceApi),\n // Instance fields on TraceAPI — already standalone, copy by reference.\n wrapSpanContext: otelTraceApi.wrapSpanContext,\n isSpanContextValid: otelTraceApi.isSpanContextValid,\n deleteSpan: otelTraceApi.deleteSpan,\n getSpan: otelTraceApi.getSpan,\n getActiveSpan: otelTraceApi.getActiveSpan,\n getSpanContext: otelTraceApi.getSpanContext,\n setSpan: otelTraceApi.setSpan,\n setSpanContext: otelTraceApi.setSpanContext,\n};\n\nexport const trace: typeof autotelTraceFn & typeof otelTraceApi = Object.assign(\n autotelTraceFn,\n otelMethods,\n) as typeof autotelTraceFn & typeof otelTraceApi;\n","import { track } from './track';\nimport { hashJson } from './stable-hash';\nimport type { EventSchemaMetadata } from './event-subscriber';\n\ntype SafeParseResult<T> =\n { success: true; data: T } | { success: false; error: unknown };\n\nexport interface SchemaLike<T> {\n safeParse(input: unknown): SafeParseResult<T>;\n}\n\nexport interface DefineEventOptions<S> {\n toJsonSchema?: (schema: S) => unknown;\n}\n\nexport interface DefinedEvent<Name extends string, Payload> {\n readonly name: Name;\n readonly schemaMetadata?: EventSchemaMetadata;\n track(payload: Payload): void;\n}\n\nexport function defineEvent<\n Name extends string,\n Payload,\n S extends SchemaLike<Payload>,\n>(\n name: Name,\n schema: S,\n options: DefineEventOptions<S> = {},\n): DefinedEvent<Name, Payload> {\n const jsonSchema = options.toJsonSchema?.(schema);\n const schemaMetadata = jsonSchema\n ? {\n source: 'zod' as const,\n jsonSchema,\n hash: hashJson(jsonSchema),\n }\n : undefined;\n\n return {\n name,\n schemaMetadata,\n track(payload: Payload) {\n const parsed = schema.safeParse(payload);\n if (!parsed.success) {\n throw new Error(\n `Invalid payload for event \"${name}\". Schema validation failed.`,\n );\n }\n track(\n name,\n parsed.data,\n schemaMetadata ? { schema: schemaMetadata } : undefined,\n );\n },\n };\n}\n","// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { trace as otelTrace } from '@opentelemetry/api';\nimport type { TraceContext } from './trace-context';\nimport { createTraceContext } from './trace-context';\nimport { recordStructuredError } from './structured-error';\nimport { flattenToAttributes } from './flatten-attributes';\nimport { emitCorrelatedEvent } from './correlated-events';\n\nconst POST_EMIT_FORK_HINT =\n \"For intentional background work tied to this request, use log.fork('label', fn) when available.\";\n\nfunction warnPostEmit(method: string, detail: string): void {\n console.warn(\n `[autotel] ${method} called after the wide event was emitted — ${detail} This data will not appear in observability. ${POST_EMIT_FORK_HINT}`,\n );\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n mergeInto(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n );\n } else if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {\n target[key] = [...targetVal, ...sourceVal];\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nconst requestContextStore =\n new nodeAsyncHooks.AsyncLocalStorage<TraceContext>();\n\nexport function runWithRequestContext<T>(ctx: TraceContext, fn: () => T): T {\n return requestContextStore.run(ctx, fn);\n}\n\nexport interface RequestLogger {\n set(fields: Record<string, unknown>): void;\n /** Set snapshot severity without adding a log event or exception. */\n setLevel(level: RequestLogLevel): void;\n info(message: string, fields?: Record<string, unknown>): void;\n warn(message: string, fields?: Record<string, unknown>): void;\n error(error: Error | string, fields?: Record<string, unknown>): void;\n getContext(): Record<string, unknown>;\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;\n fork(\n label: string,\n fn: () => void | Promise<void>,\n options?: ForkOptions,\n ): void;\n}\n\nexport type RequestLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface RequestLogSnapshot {\n timestamp: string;\n traceId: string;\n spanId: string;\n correlationId: string;\n context: Record<string, unknown>;\n}\n\nexport interface RequestLoggerOptions {\n /** Callback invoked by emitNow() for manual fan-out. */\n onEmit?: (snapshot: RequestLogSnapshot) => void | Promise<void>;\n}\n\n/**\n * Optional lifecycle hooks for adapters that need to track child loggers\n * spawned by `log.fork()` (e.g. active logger maps in framework integrations).\n */\nexport interface ForkLifecycle {\n /** Called after the child logger is created, before `fn` runs. */\n onChildEnter?: (child: RequestLogger) => void;\n /** Called after the child has finished (emit + drain), success or failure. */\n onChildExit?: (child: RequestLogger) => void;\n}\n\nexport interface ForkOptions {\n lifecycle?: ForkLifecycle;\n}\n\nfunction resolveContext(ctx?: TraceContext): TraceContext {\n if (ctx) return ctx;\n\n const stored = requestContextStore.getStore();\n if (stored) return stored;\n\n const span = otelTrace.getActiveSpan();\n if (!span) {\n throw new Error(\n '[autotel] getRequestLogger() requires an active span or runWithRequestContext(). Wrap your handler with trace() or use runWithRequestContext().',\n );\n }\n return createTraceContext(span);\n}\n\nexport function getRequestLogger(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger {\n const activeContext = resolveContext(ctx);\n const contextState: Record<string, unknown> = {};\n let emitted = false;\n let lastSnapshot: RequestLogSnapshot | null = null;\n let hasExplicitLevel = false;\n\n const addLogEvent = (\n level: 'info' | 'warn' | 'error',\n message: string,\n fields?: Record<string, unknown>,\n ) => {\n const attrs = fields ? flattenToAttributes(fields) : undefined;\n emitCorrelatedEvent(activeContext, `log.${level}`, {\n message,\n ...attrs,\n });\n };\n\n const sealCheck = (method: string, keys: string[]): void => {\n if (emitted) {\n warnPostEmit(\n method,\n `Keys dropped: ${keys.length > 0 ? keys.join(', ') : '(empty)'}.`,\n );\n }\n };\n\n return {\n set(fields: Record<string, unknown>) {\n sealCheck('log.set()', Object.keys(fields));\n if (emitted) return;\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n },\n\n setLevel(level: RequestLogLevel) {\n sealCheck('log.setLevel()', ['autotel.log.level']);\n if (emitted) return;\n hasExplicitLevel = true;\n activeContext.setAttribute('autotel.log.level', level);\n },\n\n info(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.info()', keys);\n if (emitted) return;\n addLogEvent('info', message, fields);\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n warn(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.warn()', keys);\n if (emitted) return;\n addLogEvent('warn', message, fields);\n if (!hasExplicitLevel) {\n activeContext.setAttribute('autotel.log.level', 'warn');\n }\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n error(error: Error | string, fields?: Record<string, unknown>) {\n const keys = fields ? [...Object.keys(fields), 'error'] : ['error'];\n sealCheck('log.error()', keys);\n if (emitted) return;\n const err = typeof error === 'string' ? new Error(error) : error;\n recordStructuredError(activeContext, err);\n addLogEvent('error', err.message, fields);\n\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n if (!hasExplicitLevel) {\n activeContext.setAttribute('autotel.log.level', 'error');\n }\n },\n\n getContext() {\n return { ...contextState };\n },\n\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot {\n if (emitted) {\n warnPostEmit('log.emitNow()', 'Ignoring duplicate emit.');\n return lastSnapshot as RequestLogSnapshot;\n }\n\n const mergedContext = {\n ...contextState,\n ...overrides,\n };\n const flattened = flattenToAttributes(mergedContext);\n activeContext.setAttributes(flattened);\n\n const snapshot: RequestLogSnapshot = {\n timestamp: new Date().toISOString(),\n traceId: activeContext.traceId,\n spanId: activeContext.spanId,\n correlationId: activeContext.correlationId,\n context: mergedContext,\n };\n\n emitCorrelatedEvent(activeContext, 'log.emit.manual', {\n ...flattened,\n });\n\n if (options?.onEmit) {\n Promise.resolve(options.onEmit(snapshot)).catch((error) => {\n console.warn('[autotel] request logger onEmit failed:', error);\n });\n }\n\n emitted = true;\n lastSnapshot = snapshot;\n return snapshot;\n },\n\n fork(\n label: string,\n fn: () => void | Promise<void>,\n forkOptions?: ForkOptions,\n ): void {\n const parentRequestId = activeContext.correlationId;\n if (typeof parentRequestId !== 'string' || parentRequestId.length === 0) {\n throw new Error(\n '[autotel] log.fork() requires the parent logger to have a correlationId. ' +\n 'Ensure the request was created by autotel middleware.',\n );\n }\n\n const tracer = otelTrace.getTracer('autotel.request-logger');\n const lifecycle = forkOptions?.lifecycle;\n void tracer.startActiveSpan(`request.fork:${label}`, (childSpan) => {\n const childContext: TraceContext = {\n ...createTraceContext(childSpan),\n correlationId: crypto.randomUUID(),\n };\n\n requestContextStore.run(childContext, () => {\n const childLog = getRequestLogger(childContext);\n childLog.set({\n operation: label,\n _parentCorrelationId: parentRequestId,\n });\n\n lifecycle?.onChildEnter?.(childLog);\n\n void Promise.resolve()\n .then(() => fn())\n .then(() => {\n childLog.emitNow();\n })\n .catch((error_: unknown) => {\n const error =\n error_ instanceof Error ? error_ : new Error(String(error_));\n childLog.error(error);\n childLog.emitNow();\n })\n .finally(() => {\n try {\n lifecycle?.onChildExit?.(childLog);\n } catch (hookError) {\n console.warn(\n '[autotel] fork onChildExit hook threw:',\n hookError,\n );\n }\n childSpan.end();\n });\n });\n });\n },\n };\n}\n\n/**\n * Returns `true` when a request-logger context can be resolved without throwing —\n * i.e. an explicit `ctx` was provided, a `runWithRequestContext()` scope is active,\n * or there is an active OpenTelemetry span.\n *\n * Use this to branch on observability availability instead of wrapping\n * {@link getRequestLogger} in try/catch.\n */\nexport function hasRequestContext(ctx?: TraceContext): boolean {\n if (ctx) return true;\n if (requestContextStore.getStore()) return true;\n return otelTrace.getActiveSpan() != null;\n}\n\n/**\n * Like {@link getRequestLogger}, but returns `null` instead of throwing when no\n * request context is available. Intended for best-effort instrumentation where a\n * missing telemetry context must never crash business logic.\n */\nexport function getRequestLoggerSafe(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger | null {\n if (!hasRequestContext(ctx)) return null;\n return getRequestLogger(ctx, options);\n}\n\n/**\n * A no-op {@link RequestLogger} whose methods do nothing. Used as a fallback by\n * best-effort instrumentation so wrapped handlers can run un-instrumented without\n * branching on logger presence.\n */\nexport function createNoopRequestLogger(): RequestLogger {\n const snapshot = (): RequestLogSnapshot => ({\n timestamp: new Date().toISOString(),\n traceId: '',\n spanId: '',\n correlationId: '',\n context: {},\n });\n\n return {\n set() {},\n setLevel() {},\n info() {},\n warn() {},\n error() {},\n getContext() {\n return {};\n },\n emitNow() {\n return snapshot();\n },\n fork(_label, fn) {\n void Promise.resolve().then(() => fn());\n },\n };\n}\n","/**\n * Typed error and audit catalogs.\n *\n * Group related errors into one catalog and get a refactor-safe builder per\n * code, with autocomplete at every call site and typed message parameters.\n *\n * @example\n * ```typescript\n * import { defineErrorCatalog } from 'autotel';\n *\n * export const billing = defineErrorCatalog('billing', {\n * PAYMENT_DECLINED: {\n * status: 402,\n * message: 'Card declined',\n * why: 'The issuer rejected the charge',\n * fix: 'Try a different payment method',\n * },\n * INSUFFICIENT_FUNDS: {\n * status: 402,\n * message: ({ available, required }: { available: number; required: number }) =>\n * `Insufficient funds: $${available} of $${required}`,\n * },\n * });\n *\n * throw billing.PAYMENT_DECLINED({ cause: stripeError });\n * throw billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });\n *\n * // In a catch block — refactor-safe, no magic strings:\n * if (billing.PAYMENT_DECLINED.match(err)) { ... }\n * ```\n */\n\nimport {\n createStructuredError,\n type StructuredError,\n} from './structured-error';\n\nconst catalogCodeKey = Symbol.for('autotel.catalog.code');\n\n/** Definition of a single error in a catalog. */\nexport interface ErrorCatalogEntry {\n /**\n * Human-readable message. Use a function to interpolate typed parameters;\n * the parameter type flows through to the call site.\n */\n message: string | ((params: never) => string);\n /** HTTP status to surface to clients. */\n status?: number;\n /** Stable error code. Defaults to `${namespace}.${KEY}`. */\n code?: string | number;\n /** Why it happened. A function receives the same params as `message`. */\n why?: string | ((params: never) => string);\n /** What the caller should do next. */\n fix?: string;\n /** Docs or runbook link. */\n link?: string;\n /** Error name. Defaults to the catalog key. */\n name?: string;\n}\n\n/** Per-call options passed alongside (or instead of) typed params. */\nexport interface ErrorBuildOptions {\n cause?: unknown;\n details?: Record<string, unknown>;\n /** Backend-only context. Never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\ntype ParamsOf<E> = E extends { message: (params: infer P) => string }\n ? P\n : E extends { why: (params: infer P) => string }\n ? P\n : void;\n\ntype BuilderArgs<E extends ErrorCatalogEntry> =\n ParamsOf<E> extends void\n ? [options?: ErrorBuildOptions]\n : [params: ParamsOf<E>, options?: ErrorBuildOptions];\n\n/** A callable error factory produced by {@link defineErrorCatalog}. */\nexport interface ErrorBuilder<E extends ErrorCatalogEntry> {\n (...args: BuilderArgs<E>): StructuredError;\n /** Stable code assigned to every error from this entry. */\n readonly code: string | number;\n /** True when `error` was produced by this catalog entry. */\n match(error: unknown): boolean;\n}\n\nexport type ErrorCatalog<T extends Record<string, ErrorCatalogEntry>> = {\n readonly [K in keyof T]: ErrorBuilder<T[K]>;\n};\n\nfunction readCatalogCode(error: unknown): string | number | undefined {\n if (error === null || typeof error !== 'object') return undefined;\n return (error as Record<symbol, unknown>)[catalogCodeKey] as\n string | number | undefined;\n}\n\n/** True when `error` was produced by any autotel error catalog. */\nexport function isCatalogError(error: unknown): error is StructuredError {\n return readCatalogCode(error) !== undefined;\n}\n\n/** Returns the catalog code of `error`, or `undefined` if it has none. */\nexport function getCatalogCode(error: unknown): string | number | undefined {\n return readCatalogCode(error);\n}\n\n/**\n * Define a typed error catalog. Returns an object whose keys are error\n * builders. Each builder produces a {@link StructuredError} carrying the\n * entry's message, status, code, why, fix, and link.\n */\nexport function defineErrorCatalog<\n const T extends Record<string, ErrorCatalogEntry>,\n>(namespace: string, entries: T): ErrorCatalog<T> {\n const catalog: Record<string, ErrorBuilder<ErrorCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n ErrorCatalogEntry,\n ][]) {\n const code = entry.code ?? `${namespace}.${key}`;\n const usesParams =\n typeof entry.message === 'function' || typeof entry.why === 'function';\n\n const builder = ((\n paramsOrOptions?: unknown,\n maybeOptions?: ErrorBuildOptions,\n ): StructuredError => {\n const params = usesParams ? paramsOrOptions : undefined;\n const options = (usesParams ? maybeOptions : paramsOrOptions) as\n ErrorBuildOptions | undefined;\n\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n const why =\n typeof entry.why === 'function'\n ? (entry.why as (p: unknown) => string)(params)\n : entry.why;\n\n const error = createStructuredError({\n message,\n name: entry.name ?? key,\n code,\n ...(entry.status === undefined ? {} : { status: entry.status }),\n ...(why === undefined ? {} : { why }),\n ...(entry.fix === undefined ? {} : { fix: entry.fix }),\n ...(entry.link === undefined ? {} : { link: entry.link }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n ...(options?.details === undefined ? {} : { details: options.details }),\n ...(options?.internal === undefined\n ? {}\n : { internal: options.internal }),\n });\n\n Object.defineProperty(error, catalogCodeKey, {\n value: code,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n return error;\n }) as ErrorBuilder<ErrorCatalogEntry>;\n\n Object.defineProperty(builder, 'code', {\n value: code,\n enumerable: true,\n });\n Object.defineProperty(builder, 'match', {\n value: (error: unknown): boolean => readCatalogCode(error) === code,\n enumerable: false,\n });\n\n catalog[key] = builder;\n }\n\n return Object.freeze(catalog) as ErrorCatalog<T>;\n}\n\n/** Severity of an audit action. */\nexport type AuditSeverity = 'info' | 'warn' | 'critical';\n\n/** Definition of a single action in an audit catalog. */\nexport interface AuditCatalogEntry {\n /** Human-readable description. Use a function for typed params. */\n message?: string | ((params: never) => string);\n /** Stable action name. Defaults to `${namespace}.${KEY}`. */\n action?: string;\n /** Severity of the action. Defaults to `'info'`. */\n severity?: AuditSeverity;\n}\n\n/** A resolved audit action descriptor produced by an audit catalog. */\nexport interface AuditAction {\n readonly action: string;\n readonly severity: AuditSeverity;\n readonly message?: string;\n}\n\ntype AuditDescriptorArgs<E extends AuditCatalogEntry> =\n ParamsOf<E> extends void ? [] : [params: ParamsOf<E>];\n\n/** A callable audit-action descriptor produced by {@link defineAuditCatalog}. */\nexport interface AuditDescriptor<E extends AuditCatalogEntry> {\n (...args: AuditDescriptorArgs<E>): AuditAction;\n readonly action: string;\n readonly severity: AuditSeverity;\n}\n\nexport type AuditCatalog<T extends Record<string, AuditCatalogEntry>> = {\n readonly [K in keyof T]: AuditDescriptor<T[K]>;\n};\n\n/**\n * Define a typed audit catalog. Returns typed action descriptors you can pass\n * to `track()` or audit helpers without scattering magic strings.\n */\nexport function defineAuditCatalog<\n const T extends Record<string, AuditCatalogEntry>,\n>(namespace: string, entries: T): AuditCatalog<T> {\n const catalog: Record<string, AuditDescriptor<AuditCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n AuditCatalogEntry,\n ][]) {\n const action = entry.action ?? `${namespace}.${key}`;\n const severity: AuditSeverity = entry.severity ?? 'info';\n\n const descriptor = ((params?: unknown): AuditAction => {\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n return Object.freeze({\n action,\n severity,\n ...(message === undefined ? {} : { message }),\n });\n }) as AuditDescriptor<AuditCatalogEntry>;\n\n Object.defineProperty(descriptor, 'action', {\n value: action,\n enumerable: true,\n });\n Object.defineProperty(descriptor, 'severity', {\n value: severity,\n enumerable: true,\n });\n\n catalog[key] = descriptor;\n }\n\n return Object.freeze(catalog) as AuditCatalog<T>;\n}\n","export interface DrainOptions<TContext, TConfig, TPayload = TContext> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Return null to skip draining (e.g. missing API key in dev). */\n resolve: () => TConfig | null | Promise<TConfig | null>;\n /** Transform contexts into payloads. Defaults to identity. */\n transform?: (contexts: TContext[]) => TPayload[];\n /** Transport implementation. */\n send: (payloads: TPayload[], config: TConfig) => Promise<void>;\n}\n\nexport interface HttpDrainRequest {\n url: string;\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface HttpDrainOptions<\n TContext,\n TConfig,\n TPayload = TContext,\n> extends Omit<DrainOptions<TContext, TConfig, TPayload>, 'send'> {\n encode: (payloads: TPayload[], config: TConfig) => HttpDrainRequest | null;\n timeoutMs?: number;\n retries?: number;\n resolveTimeoutMs?: (config: TConfig) => number | undefined;\n resolveRetries?: (config: TConfig) => number | undefined;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_RETRIES = 2;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n t.unref?.();\n });\n}\n\nasync function postWithRetry(options: {\n name: string;\n request: HttpDrainRequest;\n timeoutMs: number;\n retries: number;\n}): Promise<void> {\n const { name, request, timeoutMs, retries } = options;\n const attempts = Math.max(1, retries);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n timeout.unref?.();\n try {\n const response = await fetch(request.url, {\n method: 'POST',\n headers: request.headers,\n body: request.body,\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(\n `[autotel/${name}] HTTP ${response.status} draining ${request.url}`,\n );\n }\n return;\n } catch (error) {\n lastError = error;\n if (attempt < attempts) {\n await delay(100 * attempt);\n }\n } finally {\n clearTimeout(timeout);\n }\n }\n\n throw lastError;\n}\n\nexport function defineDrain<TContext, TConfig, TPayload = TContext>(\n options: DrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return async (ctx: TContext | TContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx];\n if (contexts.length === 0) return;\n\n const config = await options.resolve();\n if (!config) return;\n\n const payloads = options.transform\n ? options.transform(contexts)\n : (contexts as unknown as TPayload[]);\n\n if (payloads.length === 0) return;\n\n try {\n await options.send(payloads, config);\n } catch (error) {\n console.error(`[autotel/${options.name}] drain failed:`, error);\n }\n };\n}\n\nexport function defineHttpDrain<TContext, TConfig, TPayload = TContext>(\n options: HttpDrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return defineDrain<TContext, TConfig, TPayload>({\n name: options.name,\n resolve: options.resolve,\n transform: options.transform,\n send: async (payloads, config) => {\n const request = options.encode(payloads, config);\n if (!request) return;\n const timeoutMs =\n options.resolveTimeoutMs?.(config) ??\n options.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const retries =\n options.resolveRetries?.(config) ?? options.retries ?? DEFAULT_RETRIES;\n\n await postWithRetry({\n name: options.name,\n request,\n timeoutMs,\n retries,\n });\n },\n });\n}\n","export interface EnrichContext<TEvent extends Record<string, unknown>> {\n event: TEvent;\n request?: {\n method?: string;\n path?: string;\n requestId?: string;\n };\n response?: {\n status?: number;\n };\n headers?: Record<string, string>;\n}\n\nexport interface EnricherDefinition<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Top-level field to merge computed values into. */\n field: keyof TEvent & string;\n /** Return undefined to skip enrichment. */\n compute: (ctx: EnrichContext<TEvent>) => TValue | undefined;\n}\n\nexport interface EnricherOptions {\n /** Replace existing field value instead of merge. Default false. */\n overwrite?: boolean;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (isPlainObject(sourceVal) && isPlainObject(targetVal)) {\n mergeInto(targetVal, sourceVal);\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nexport function defineEnricher<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n>(\n def: EnricherDefinition<TEvent, TValue>,\n options: EnricherOptions = {},\n): (ctx: EnrichContext<TEvent>) => void {\n return (ctx: EnrichContext<TEvent>) => {\n let computed: TValue | undefined;\n try {\n computed = def.compute(ctx);\n } catch (error) {\n console.error(`[autotel/${def.name}] enrich failed:`, error);\n return;\n }\n\n if (!computed) return;\n\n if (options.overwrite || !isPlainObject(ctx.event[def.field])) {\n (ctx.event as Record<string, unknown>)[def.field] = computed;\n return;\n }\n\n mergeInto(\n ctx.event[def.field] as unknown as Record<string, unknown>,\n computed as unknown as Record<string, unknown>,\n );\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,MAAM,cAAc;CAElB,yBACEA,yBAAa,wBAAwB,KAAKA,wBAAY;CACxD,mBAAmBA,yBAAa,kBAAkB,KAAKA,wBAAY;CACnE,WAAWA,yBAAa,UAAU,KAAKA,wBAAY;CACnD,SAASA,yBAAa,QAAQ,KAAKA,wBAAY;CAE/C,iBAAiBA,yBAAa;CAC9B,oBAAoBA,yBAAa;CACjC,YAAYA,yBAAa;CACzB,SAASA,yBAAa;CACtB,eAAeA,yBAAa;CAC5B,gBAAgBA,yBAAa;CAC7B,SAASA,yBAAa;CACtB,gBAAgBA,yBAAa;AAC/B;AAEA,MAAa,QAAqD,OAAO,OACvEC,0BACA,WACF;;;;ACfA,SAAgB,YAKd,MACA,QACA,UAAiC,CAAC,GACL;CAC7B,MAAM,aAAa,QAAQ,eAAe,MAAM;CAChD,MAAM,iBAAiB,aACnB;EACE,QAAQ;EACR;EACA,MAAMC,6BAAS,UAAU;CAC3B,IACA;CAEJ,OAAO;EACL;EACA;EACA,MAAM,SAAkB;GACtB,MAAM,SAAS,OAAO,UAAU,OAAO;GACvC,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,MACR,8BAA8B,KAAK,6BACrC;GAEF,mBACE,MACA,OAAO,MACP,iBAAiB,EAAE,QAAQ,eAAe,IAAI,MAChD;EACF;CACF;AACF;;;;AC/CA,MAAM,sBACJ;AAEF,SAAS,aAAa,QAAgB,QAAsB;CAC1D,QAAQ,KACN,aAAa,OAAO,6CAA6C,OAAO,+CAA+C,qBACzH;AACF;AAEA,SAASC,YACP,QACA,QACM;CACN,KAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,YAAY,OAAO;EACzB,IAAI,cAAc,QAAW;EAC7B,MAAM,YAAY,OAAO;EACzB,IACE,cAAc,QACd,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,cAAc,QACd,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,GAExB,YACE,WACA,SACF;OACK,IAAI,MAAM,QAAQ,SAAS,KAAK,MAAM,QAAQ,SAAS,GAC5D,OAAO,OAAO,CAAC,GAAG,WAAW,GAAG,SAAS;OAEzC,OAAO,OAAO;CAElB;AACF;AAEA,MAAM,sBACJ,IAAIC,iBAAe,kBAAgC;AAErD,SAAgB,sBAAyB,KAAmB,IAAgB;CAC1E,OAAO,oBAAoB,IAAI,KAAK,EAAE;AACxC;AAgDA,SAAS,eAAe,KAAkC;CACxD,IAAI,KAAK,OAAO;CAEhB,MAAM,SAAS,oBAAoB,SAAS;CAC5C,IAAI,QAAQ,OAAO;CAEnB,MAAM,OAAOC,yBAAU,cAAc;CACrC,IAAI,CAAC,MACH,MAAM,IAAI,MACR,iJACF;CAEF,OAAOC,gCAAmB,IAAI;AAChC;AAEA,SAAgB,iBACd,KACA,SACe;CACf,MAAM,gBAAgB,eAAe,GAAG;CACxC,MAAM,eAAwC,CAAC;CAC/C,IAAI,UAAU;CACd,IAAI,eAA0C;CAC9C,IAAI,mBAAmB;CAEvB,MAAM,eACJ,OACA,SACA,WACG;EACH,MAAM,QAAQ,SAASC,6CAAoB,MAAM,IAAI;EACrD,8CAAoB,eAAe,OAAO,SAAS;GACjD;GACA,GAAG;EACL,CAAC;CACH;CAEA,MAAM,aAAa,QAAgB,SAAyB;EAC1D,IAAI,SACF,aACE,QACA,iBAAiB,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,UAAU,EACjE;CAEJ;CAEA,OAAO;EACL,IAAI,QAAiC;GACnC,UAAU,aAAa,OAAO,KAAK,MAAM,CAAC;GAC1C,IAAI,SAAS;GACb,YAAU,cAAc,MAAM;GAC9B,cAAc,cAAcA,6CAAoB,MAAM,CAAC;EACzD;EAEA,SAAS,OAAwB;GAC/B,UAAU,kBAAkB,CAAC,mBAAmB,CAAC;GACjD,IAAI,SAAS;GACb,mBAAmB;GACnB,cAAc,aAAa,qBAAqB,KAAK;EACvD;EAEA,KAAK,SAAiB,QAAkC;GACtD,MAAM,OAAO,SACT,CAAC,WAAW,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM,aAAa,CAAC,IACrE,CAAC,SAAS;GACd,UAAU,cAAc,IAAI;GAC5B,IAAI,SAAS;GACb,YAAY,QAAQ,SAAS,MAAM;GACnC,IAAI,QAAQ;IACV,YAAU,cAAc,MAAM;IAC9B,cAAc,cAAcA,6CAAoB,MAAM,CAAC;GACzD;EACF;EAEA,KAAK,SAAiB,QAAkC;GACtD,MAAM,OAAO,SACT,CAAC,WAAW,GAAG,OAAO,KAAK,MAAM,CAAC,CAAC,QAAQ,MAAM,MAAM,aAAa,CAAC,IACrE,CAAC,SAAS;GACd,UAAU,cAAc,IAAI;GAC5B,IAAI,SAAS;GACb,YAAY,QAAQ,SAAS,MAAM;GACnC,IAAI,CAAC,kBACH,cAAc,aAAa,qBAAqB,MAAM;GAExD,IAAI,QAAQ;IACV,YAAU,cAAc,MAAM;IAC9B,cAAc,cAAcA,6CAAoB,MAAM,CAAC;GACzD;EACF;EAEA,MAAM,OAAuB,QAAkC;GAC7D,MAAM,OAAO,SAAS,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,OAAO,IAAI,CAAC,OAAO;GAClE,UAAU,eAAe,IAAI;GAC7B,IAAI,SAAS;GACb,MAAM,MAAM,OAAO,UAAU,WAAW,IAAI,MAAM,KAAK,IAAI;GAC3D,+CAAsB,eAAe,GAAG;GACxC,YAAY,SAAS,IAAI,SAAS,MAAM;GAExC,IAAI,QAAQ;IACV,YAAU,cAAc,MAAM;IAC9B,cAAc,cAAcA,6CAAoB,MAAM,CAAC;GACzD;GACA,IAAI,CAAC,kBACH,cAAc,aAAa,qBAAqB,OAAO;EAE3D;EAEA,aAAa;GACX,OAAO,EAAE,GAAG,aAAa;EAC3B;EAEA,QAAQ,WAAyD;GAC/D,IAAI,SAAS;IACX,aAAa,iBAAiB,0BAA0B;IACxD,OAAO;GACT;GAEA,MAAM,gBAAgB;IACpB,GAAG;IACH,GAAG;GACL;GACA,MAAM,YAAYA,6CAAoB,aAAa;GACnD,cAAc,cAAc,SAAS;GAErC,MAAM,WAA+B;IACnC,4BAAW,IAAI,KAAK,EAAC,CAAC,YAAY;IAClC,SAAS,cAAc;IACvB,QAAQ,cAAc;IACtB,eAAe,cAAc;IAC7B,SAAS;GACX;GAEA,8CAAoB,eAAe,mBAAmB,EACpD,GAAG,UACL,CAAC;GAED,IAAI,SAAS,QACX,QAAQ,QAAQ,QAAQ,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO,UAAU;IACzD,QAAQ,KAAK,2CAA2C,KAAK;GAC/D,CAAC;GAGH,UAAU;GACV,eAAe;GACf,OAAO;EACT;EAEA,KACE,OACA,IACA,aACM;GACN,MAAM,kBAAkB,cAAc;GACtC,IAAI,OAAO,oBAAoB,YAAY,gBAAgB,WAAW,GACpE,MAAM,IAAI,MACR,gIAEF;GAGF,MAAM,SAASF,yBAAU,UAAU,wBAAwB;GAC3D,MAAM,YAAY,aAAa;GAC/B,AAAK,OAAO,gBAAgB,gBAAgB,UAAU,cAAc;IAClE,MAAM,eAA6B;KACjC,GAAGC,gCAAmB,SAAS;KAC/B,eAAe,OAAO,WAAW;IACnC;IAEA,oBAAoB,IAAI,oBAAoB;KAC1C,MAAM,WAAW,iBAAiB,YAAY;KAC9C,SAAS,IAAI;MACX,WAAW;MACX,sBAAsB;KACxB,CAAC;KAED,WAAW,eAAe,QAAQ;KAElC,AAAK,QAAQ,QAAQ,CAAC,CACnB,WAAW,GAAG,CAAC,CAAC,CAChB,WAAW;MACV,SAAS,QAAQ;KACnB,CAAC,CAAC,CACD,OAAO,WAAoB;MAC1B,MAAM,QACJ,kBAAkB,QAAQ,SAAS,IAAI,MAAM,OAAO,MAAM,CAAC;MAC7D,SAAS,MAAM,KAAK;MACpB,SAAS,QAAQ;KACnB,CAAC,CAAC,CACD,cAAc;MACb,IAAI;OACF,WAAW,cAAc,QAAQ;MACnC,SAAS,WAAW;OAClB,QAAQ,KACN,0CACA,SACF;MACF;MACA,UAAU,IAAI;KAChB,CAAC;IACL,CAAC;GACH,CAAC;EACH;CACF;AACF;;;;;;;;;AAUA,SAAgB,kBAAkB,KAA6B;CAC7D,IAAI,KAAK,OAAO;CAChB,IAAI,oBAAoB,SAAS,GAAG,OAAO;CAC3C,OAAOD,yBAAU,cAAc,KAAK;AACtC;;;;;;AAOA,SAAgB,qBACd,KACA,SACsB;CACtB,IAAI,CAAC,kBAAkB,GAAG,GAAG,OAAO;CACpC,OAAO,iBAAiB,KAAK,OAAO;AACtC;;;;;;AAOA,SAAgB,0BAAyC;CACvD,MAAM,kBAAsC;EAC1C,4BAAW,IAAI,KAAK,EAAC,CAAC,YAAY;EAClC,SAAS;EACT,QAAQ;EACR,eAAe;EACf,SAAS,CAAC;CACZ;CAEA,OAAO;EACL,MAAM,CAAC;EACP,WAAW,CAAC;EACZ,OAAO,CAAC;EACR,OAAO,CAAC;EACR,QAAQ,CAAC;EACT,aAAa;GACX,OAAO,CAAC;EACV;EACA,UAAU;GACR,OAAO,SAAS;EAClB;EACA,KAAK,QAAQ,IAAI;GACf,AAAK,QAAQ,QAAQ,CAAC,CAAC,WAAW,GAAG,CAAC;EACxC;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpUA,MAAM,iBAAiB,OAAO,IAAI,sBAAsB;AAuDxD,SAAS,gBAAgB,OAA6C;CACpE,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;CACxD,OAAQ,MAAkC;AAE5C;;AAGA,SAAgB,eAAe,OAA0C;CACvE,OAAO,gBAAgB,KAAK,MAAM;AACpC;;AAGA,SAAgB,eAAe,OAA6C;CAC1E,OAAO,gBAAgB,KAAK;AAC9B;;;;;;AAOA,SAAgB,mBAEd,WAAmB,SAA6B;CAChD,MAAM,UAA2D,CAAC;CAElE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAG5C;EACH,MAAM,OAAO,MAAM,QAAQ,GAAG,UAAU,GAAG;EAC3C,MAAM,aACJ,OAAO,MAAM,YAAY,cAAc,OAAO,MAAM,QAAQ;EAE9D,MAAM,YACJ,iBACA,iBACoB;GACpB,MAAM,SAAS,aAAa,kBAAkB;GAC9C,MAAM,UAAW,aAAa,eAAe;GAG7C,MAAM,UACJ,OAAO,MAAM,YAAY,aACpB,MAAM,QAAmC,MAAM,IAChD,MAAM;GACZ,MAAM,MACJ,OAAO,MAAM,QAAQ,aAChB,MAAM,IAA+B,MAAM,IAC5C,MAAM;GAEZ,MAAM,QAAQG,+CAAsB;IAClC;IACA,MAAM,MAAM,QAAQ;IACpB;IACA,GAAI,MAAM,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;IAC7D,GAAI,QAAQ,SAAY,CAAC,IAAI,EAAE,IAAI;IACnC,GAAI,MAAM,QAAQ,SAAY,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI;IACpD,GAAI,MAAM,SAAS,SAAY,CAAC,IAAI,EAAE,MAAM,MAAM,KAAK;IACvD,GAAI,SAAS,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;IAC/D,GAAI,SAAS,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,QAAQ,QAAQ;IACrE,GAAI,SAAS,aAAa,SACtB,CAAC,IACD,EAAE,UAAU,QAAQ,SAAS;GACnC,CAAC;GAED,OAAO,eAAe,OAAO,gBAAgB;IAC3C,OAAO;IACP,YAAY;IACZ,UAAU;IACV,cAAc;GAChB,CAAC;GAED,OAAO;EACT;EAEA,OAAO,eAAe,SAAS,QAAQ;GACrC,OAAO;GACP,YAAY;EACd,CAAC;EACD,OAAO,eAAe,SAAS,SAAS;GACtC,QAAQ,UAA4B,gBAAgB,KAAK,MAAM;GAC/D,YAAY;EACd,CAAC;EAED,QAAQ,OAAO;CACjB;CAEA,OAAO,OAAO,OAAO,OAAO;AAC9B;;;;;AAwCA,SAAgB,mBAEd,WAAmB,SAA6B;CAChD,MAAM,UAA8D,CAAC;CAErE,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAG5C;EACH,MAAM,SAAS,MAAM,UAAU,GAAG,UAAU,GAAG;EAC/C,MAAM,WAA0B,MAAM,YAAY;EAElD,MAAM,eAAe,WAAkC;GACrD,MAAM,UACJ,OAAO,MAAM,YAAY,aACpB,MAAM,QAAmC,MAAM,IAChD,MAAM;GACZ,OAAO,OAAO,OAAO;IACnB;IACA;IACA,GAAI,YAAY,SAAY,CAAC,IAAI,EAAE,QAAQ;GAC7C,CAAC;EACH;EAEA,OAAO,eAAe,YAAY,UAAU;GAC1C,OAAO;GACP,YAAY;EACd,CAAC;EACD,OAAO,eAAe,YAAY,YAAY;GAC5C,OAAO;GACP,YAAY;EACd,CAAC;EAED,QAAQ,OAAO;CACjB;CAEA,OAAO,OAAO,OAAO,OAAO;AAC9B;;;;ACrOA,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AAExB,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAS,YAAY;EAE9B,AADU,WAAW,SAAS,EAC9B,CAAC,CAAC,QAAQ;CACZ,CAAC;AACH;AAEA,eAAe,cAAc,SAKX;CAChB,MAAM,EAAE,MAAM,SAAS,WAAW,YAAY;CAC9C,MAAM,WAAW,KAAK,IAAI,GAAG,OAAO;CACpC,IAAI;CAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,UAAU,WAAW;EACpD,MAAM,aAAa,IAAI,gBAAgB;EACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,SAAS;EAC9D,QAAQ,QAAQ;EAChB,IAAI;GACF,MAAM,WAAW,MAAM,MAAM,QAAQ,KAAK;IACxC,QAAQ;IACR,SAAS,QAAQ;IACjB,MAAM,QAAQ;IACd,QAAQ,WAAW;GACrB,CAAC;GACD,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MACR,YAAY,KAAK,SAAS,SAAS,OAAO,YAAY,QAAQ,KAChE;GAEF;EACF,SAAS,OAAO;GACd,YAAY;GACZ,IAAI,UAAU,UACZ,MAAM,MAAM,MAAM,OAAO;EAE7B,UAAU;GACR,aAAa,OAAO;EACtB;CACF;CAEA,MAAM;AACR;AAEA,SAAgB,YACd,SAC+C;CAC/C,OAAO,OAAO,QAA+B;EAC3C,MAAM,WAAW,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,GAAG;EAChD,IAAI,SAAS,WAAW,GAAG;EAE3B,MAAM,SAAS,MAAM,QAAQ,QAAQ;EACrC,IAAI,CAAC,QAAQ;EAEb,MAAM,WAAW,QAAQ,YACrB,QAAQ,UAAU,QAAQ,IACzB;EAEL,IAAI,SAAS,WAAW,GAAG;EAE3B,IAAI;GACF,MAAM,QAAQ,KAAK,UAAU,MAAM;EACrC,SAAS,OAAO;GACd,QAAQ,MAAM,YAAY,QAAQ,KAAK,kBAAkB,KAAK;EAChE;CACF;AACF;AAEA,SAAgB,gBACd,SAC+C;CAC/C,OAAO,YAAyC;EAC9C,MAAM,QAAQ;EACd,SAAS,QAAQ;EACjB,WAAW,QAAQ;EACnB,MAAM,OAAO,UAAU,WAAW;GAChC,MAAM,UAAU,QAAQ,OAAO,UAAU,MAAM;GAC/C,IAAI,CAAC,SAAS;GACd,MAAM,YACJ,QAAQ,mBAAmB,MAAM,KACjC,QAAQ,aACR;GACF,MAAM,UACJ,QAAQ,iBAAiB,MAAM,KAAK,QAAQ,WAAW;GAEzD,MAAM,cAAc;IAClB,MAAM,QAAQ;IACd;IACA;IACA;GACF,CAAC;EACH;CACF,CAAC;AACH;;;;AClGA,SAAS,cAAc,OAAkD;CACvE,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,UACP,QACA,QACM;CACN,KAAK,MAAM,OAAO,QAAQ;EACxB,MAAM,YAAY,OAAO;EACzB,IAAI,cAAc,QAAW;EAC7B,MAAM,YAAY,OAAO;EACzB,IAAI,cAAc,SAAS,KAAK,cAAc,SAAS,GACrD,UAAU,WAAW,SAAS;OAE9B,OAAO,OAAO;CAElB;AACF;AAEA,SAAgB,eAId,KACA,UAA2B,CAAC,GACU;CACtC,QAAQ,QAA+B;EACrC,IAAI;EACJ,IAAI;GACF,WAAW,IAAI,QAAQ,GAAG;EAC5B,SAAS,OAAO;GACd,QAAQ,MAAM,YAAY,IAAI,KAAK,mBAAmB,KAAK;GAC3D;EACF;EAEA,IAAI,CAAC,UAAU;EAEf,IAAI,QAAQ,aAAa,CAAC,cAAc,IAAI,MAAM,IAAI,MAAM,GAAG;GAC7D,AAAC,IAAI,MAAkC,IAAI,SAAS;GACpD;EACF;EAEA,UACE,IAAI,MAAM,IAAI,QACd,QACF;CACF;AACF"}
package/dist/index.d.cts CHANGED
@@ -391,6 +391,8 @@ declare function shutdown(): Promise<void>;
391
391
  declare function runWithRequestContext<T>(ctx: TraceContext$1, fn: () => T): T;
392
392
  interface RequestLogger {
393
393
  set(fields: Record<string, unknown>): void;
394
+ /** Set snapshot severity without adding a log event or exception. */
395
+ setLevel(level: RequestLogLevel): void;
394
396
  info(message: string, fields?: Record<string, unknown>): void;
395
397
  warn(message: string, fields?: Record<string, unknown>): void;
396
398
  error(error: Error | string, fields?: Record<string, unknown>): void;
@@ -398,6 +400,7 @@ interface RequestLogger {
398
400
  emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;
399
401
  fork(label: string, fn: () => void | Promise<void>, options?: ForkOptions): void;
400
402
  }
403
+ type RequestLogLevel = 'debug' | 'info' | 'warn' | 'error';
401
404
  interface RequestLogSnapshot {
402
405
  timestamp: string;
403
406
  traceId: string;
@@ -634,5 +637,5 @@ declare function defineEnricher<TEvent extends Record<string, unknown>, TValue e
634
637
  */
635
638
  declare function formatDuration(ms: number): string;
636
639
  //#endregion
637
- export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, type AttributeGuardrails, type AttributePolicy, AttributeRedactingProcessor, type AttributeRedactingProcessorOptions, type AttributeRedactorConfig, type AttributeRedactorFn, type AttributeRedactorPreset, type AttributeValue, type Attributes, type AuditAction, type AuditCatalog, type AuditCatalogEntry, type AuditDescriptor, type AuditSeverity, type AutotelConfig, type Baggage, type BaggageEntry, type BaggageError, type BaggageFieldDefinition, type BaggageFieldType, BaggageSpanProcessor, type BaggageSpanProcessorOptions, type BuiltinPatternName, BusinessBaggage, type BusinessBaggageValues, CORRELATION_ID_BAGGAGE_KEY, type ClientAttrs, type CloudAttrs, type CodeAttrs, type ConsumerConfig, type ConsumerContext, type ContainerAttrs, type Context, type DBAttrs, type DBConfig, type DefineEventOptions, type DefinedEvent, type DeploymentAttrs, type DeviceAttrs, type DrainOptions, type DrainPipelineOptions, type EnrichContext, type EnricherDefinition, type EnricherOptions, type ErrorAttrs, type ErrorBuildOptions, type ErrorBuilder, type ErrorCatalog, type ErrorCatalogEntry, Event, type EventAttributes, type EventSubscriber, type EventsOptions, type Exception, type ExceptionAttrs, type FaaSAttrs, type FeatureFlagAttrs, FilteringSpanProcessor, type FilteringSpanProcessorOptions, type ForkLifecycle, type ForkOptions, type FunnelStatus, type GraphQLAttrs, HTTPAttributes, type HTTPClientAttrs, type HTTPConfig, type HTTPServerAttrs, type HrTime, type HttpDrainOptions, type HttpDrainRequest, type InstrumentOptions, type K8sAttrs, type LagMetricsConfig, type Link, type MaskFn, type MessagingAttrs, type MessagingConfig, type MessagingOperation, type MessagingSystem, Metric, type MetricsOptions, NORMALIZER_PATTERNS, NORMALIZER_PRESETS, type NetworkAttrs, NeverSampler, type OTelAttrs, type OperationContext, type TraceContext as OtelTraceContext, type OutcomeStatus, type ParsedError, type PeerAttrs, type PipelineDrainFn, type ProcessAttrs, type ProcessHandlersConfig, type ProducerConfig, type ProducerContext, REDACTOR_PATTERNS, REDACTOR_PRESETS, ROOT_CONTEXT, type RPCAttrs, RandomSampler, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type SafeBaggageOptions, type SafeBaggageSchema, type Sampler, type SamplingContext, type SamplingPreset, type SchemaLike, type ServerAddressAttrs, ServiceAttributes, type ServiceAttrs, type SessionAttrs, type SingleInstrumentOptions, type Span, type SpanAttributes, type SpanContext, type SpanFilterPredicate, SpanKind, type SpanLink, type SpanNameNormalizerConfig, type SpanNameNormalizerFn, type SpanNameNormalizerPreset, SpanNameNormalizingProcessor, type SpanNameNormalizingProcessorOptions, type SpanOptions, SpanStatusCode, type StepConfig, type StepContext, type StepStatus, type StringRedactor, type StructuredError, type StructuredErrorInput, type TLSAttrs, type TextMapGetter, type TextMapSetter, type ThreadAttrs, type TimeInput, type TraceContext$1 as TraceContext, type TraceFlags, type TraceState, type Tracer, type TracerProvider, URLAttributes, type URLAttrs, type UserAttrs, UserIdSampler, type ValuePatternConfig, type WithBaggageOptions, type WithNewContextOptions, type WorkflowConfig, type WorkflowContext, type WorkflowStatus, attrs, autoRedactPII, builtinPatterns, context, createAttributeRedactor, createCounter, createDeterministicTraceId, createDrainPipeline, createHistogram, createLinkFromHeaders, createNoopRequestLogger, createObservableGauge, createRedactedSpan, createSafeBaggageSchema, createStringRedactor, createStructuredError, createUpDownCounter, ctx, dbClient, defineAuditCatalog, defineBaggageSchema, defineDrain, defineEnricher, defineErrorCatalog, defineEvent, defineHttpDrain, enrichWithTraceContext, extractLinksFromBatch, finalizeSpan, flattenMetadata, flattenToAttributes, flush, formatDuration, generateCorrelationId, getActiveContext, getActiveSpan, getActiveTraceContext, getAutotelTracer, getAutotelTracerProvider, getCatalogCode, getCorrelationId, getCurrentWorkflowContext, getEventQueue, getEvents, getMeter, getMetrics, getOperationContext, getOrCreateCorrelationId, getRequestLogger, getRequestLoggerSafe, getStructuredErrorAttributes, getTraceContext, getTracer, hasRequestContext, httpClient, httpRequestHeaderAttribute, httpResponseHeaderAttribute, httpServer, identify, init, instrument, isCatalogError, isInWorkflow, isInitialized, isLoggerLocked, isTracing, lockLogger, mergeAttrs, mergeServiceResource, normalizeAttributeRedactorConfig, otelTrace, parseError, propagation, recordStructuredError, request, resetEvents, resetMetrics, resolveSamplingPreset, resolveTraceUrl, runInOperationContext, runWithCorrelationId, runWithRequestContext, runWithSpan, safeSetAttributes, samplingPresets, setAutotelTracerProvider, setCorrelationId, setCorrelationIdInBaggage, setDevice, setError, setException, setSession, setUser, shutdown, span, structuredErrorToJSON, toAttributeValue, trace, traceConsumer, traceDB, traceHTTP, traceMessaging, traceProducer, traceStep, traceWorkflow, track, validateAttribute, withBaggage, withNewContext, withTracing };
640
+ export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, type AttributeGuardrails, type AttributePolicy, AttributeRedactingProcessor, type AttributeRedactingProcessorOptions, type AttributeRedactorConfig, type AttributeRedactorFn, type AttributeRedactorPreset, type AttributeValue, type Attributes, type AuditAction, type AuditCatalog, type AuditCatalogEntry, type AuditDescriptor, type AuditSeverity, type AutotelConfig, type Baggage, type BaggageEntry, type BaggageError, type BaggageFieldDefinition, type BaggageFieldType, BaggageSpanProcessor, type BaggageSpanProcessorOptions, type BuiltinPatternName, BusinessBaggage, type BusinessBaggageValues, CORRELATION_ID_BAGGAGE_KEY, type ClientAttrs, type CloudAttrs, type CodeAttrs, type ConsumerConfig, type ConsumerContext, type ContainerAttrs, type Context, type DBAttrs, type DBConfig, type DefineEventOptions, type DefinedEvent, type DeploymentAttrs, type DeviceAttrs, type DrainOptions, type DrainPipelineOptions, type EnrichContext, type EnricherDefinition, type EnricherOptions, type ErrorAttrs, type ErrorBuildOptions, type ErrorBuilder, type ErrorCatalog, type ErrorCatalogEntry, Event, type EventAttributes, type EventSubscriber, type EventsOptions, type Exception, type ExceptionAttrs, type FaaSAttrs, type FeatureFlagAttrs, FilteringSpanProcessor, type FilteringSpanProcessorOptions, type ForkLifecycle, type ForkOptions, type FunnelStatus, type GraphQLAttrs, HTTPAttributes, type HTTPClientAttrs, type HTTPConfig, type HTTPServerAttrs, type HrTime, type HttpDrainOptions, type HttpDrainRequest, type InstrumentOptions, type K8sAttrs, type LagMetricsConfig, type Link, type MaskFn, type MessagingAttrs, type MessagingConfig, type MessagingOperation, type MessagingSystem, Metric, type MetricsOptions, NORMALIZER_PATTERNS, NORMALIZER_PRESETS, type NetworkAttrs, NeverSampler, type OTelAttrs, type OperationContext, type TraceContext as OtelTraceContext, type OutcomeStatus, type ParsedError, type PeerAttrs, type PipelineDrainFn, type ProcessAttrs, type ProcessHandlersConfig, type ProducerConfig, type ProducerContext, REDACTOR_PATTERNS, REDACTOR_PRESETS, ROOT_CONTEXT, type RPCAttrs, RandomSampler, type RequestLogLevel, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type SafeBaggageOptions, type SafeBaggageSchema, type Sampler, type SamplingContext, type SamplingPreset, type SchemaLike, type ServerAddressAttrs, ServiceAttributes, type ServiceAttrs, type SessionAttrs, type SingleInstrumentOptions, type Span, type SpanAttributes, type SpanContext, type SpanFilterPredicate, SpanKind, type SpanLink, type SpanNameNormalizerConfig, type SpanNameNormalizerFn, type SpanNameNormalizerPreset, SpanNameNormalizingProcessor, type SpanNameNormalizingProcessorOptions, type SpanOptions, SpanStatusCode, type StepConfig, type StepContext, type StepStatus, type StringRedactor, type StructuredError, type StructuredErrorInput, type TLSAttrs, type TextMapGetter, type TextMapSetter, type ThreadAttrs, type TimeInput, type TraceContext$1 as TraceContext, type TraceFlags, type TraceState, type Tracer, type TracerProvider, URLAttributes, type URLAttrs, type UserAttrs, UserIdSampler, type ValuePatternConfig, type WithBaggageOptions, type WithNewContextOptions, type WorkflowConfig, type WorkflowContext, type WorkflowStatus, attrs, autoRedactPII, builtinPatterns, context, createAttributeRedactor, createCounter, createDeterministicTraceId, createDrainPipeline, createHistogram, createLinkFromHeaders, createNoopRequestLogger, createObservableGauge, createRedactedSpan, createSafeBaggageSchema, createStringRedactor, createStructuredError, createUpDownCounter, ctx, dbClient, defineAuditCatalog, defineBaggageSchema, defineDrain, defineEnricher, defineErrorCatalog, defineEvent, defineHttpDrain, enrichWithTraceContext, extractLinksFromBatch, finalizeSpan, flattenMetadata, flattenToAttributes, flush, formatDuration, generateCorrelationId, getActiveContext, getActiveSpan, getActiveTraceContext, getAutotelTracer, getAutotelTracerProvider, getCatalogCode, getCorrelationId, getCurrentWorkflowContext, getEventQueue, getEvents, getMeter, getMetrics, getOperationContext, getOrCreateCorrelationId, getRequestLogger, getRequestLoggerSafe, getStructuredErrorAttributes, getTraceContext, getTracer, hasRequestContext, httpClient, httpRequestHeaderAttribute, httpResponseHeaderAttribute, httpServer, identify, init, instrument, isCatalogError, isInWorkflow, isInitialized, isLoggerLocked, isTracing, lockLogger, mergeAttrs, mergeServiceResource, normalizeAttributeRedactorConfig, otelTrace, parseError, propagation, recordStructuredError, request, resetEvents, resetMetrics, resolveSamplingPreset, resolveTraceUrl, runInOperationContext, runWithCorrelationId, runWithRequestContext, runWithSpan, safeSetAttributes, samplingPresets, setAutotelTracerProvider, setCorrelationId, setCorrelationIdInBaggage, setDevice, setError, setException, setSession, setUser, shutdown, span, structuredErrorToJSON, toAttributeValue, trace, traceConsumer, traceDB, traceHTTP, traceMessaging, traceProducer, traceStep, traceWorkflow, track, validateAttribute, withBaggage, withNewContext, withTracing };
638
641
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/baggage-span-processor.ts","../src/trace-hybrid.ts","../src/operation-context.ts","../src/rate-limiter.ts","../src/event-queue.ts","../src/track.ts","../src/shutdown.ts","../src/request-logger.ts","../src/structured-error.ts","../src/error-catalog.ts","../src/flatten-attributes.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts","../src/pretty-log-formatter.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/baggage-span-processor.ts","../src/trace-hybrid.ts","../src/operation-context.ts","../src/rate-limiter.ts","../src/event-queue.ts","../src/track.ts","../src/shutdown.ts","../src/request-logger.ts","../src/structured-error.ts","../src/error-catalog.ts","../src/flatten-attributes.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts","../src/pretty-log-formatter.ts"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -391,6 +391,8 @@ declare function shutdown(): Promise<void>;
391
391
  declare function runWithRequestContext<T>(ctx: TraceContext$1, fn: () => T): T;
392
392
  interface RequestLogger {
393
393
  set(fields: Record<string, unknown>): void;
394
+ /** Set snapshot severity without adding a log event or exception. */
395
+ setLevel(level: RequestLogLevel): void;
394
396
  info(message: string, fields?: Record<string, unknown>): void;
395
397
  warn(message: string, fields?: Record<string, unknown>): void;
396
398
  error(error: Error | string, fields?: Record<string, unknown>): void;
@@ -398,6 +400,7 @@ interface RequestLogger {
398
400
  emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;
399
401
  fork(label: string, fn: () => void | Promise<void>, options?: ForkOptions): void;
400
402
  }
403
+ type RequestLogLevel = 'debug' | 'info' | 'warn' | 'error';
401
404
  interface RequestLogSnapshot {
402
405
  timestamp: string;
403
406
  traceId: string;
@@ -634,5 +637,5 @@ declare function defineEnricher<TEvent extends Record<string, unknown>, TValue e
634
637
  */
635
638
  declare function formatDuration(ms: number): string;
636
639
  //#endregion
637
- export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, type AttributeGuardrails, type AttributePolicy, AttributeRedactingProcessor, type AttributeRedactingProcessorOptions, type AttributeRedactorConfig, type AttributeRedactorFn, type AttributeRedactorPreset, type AttributeValue, type Attributes, type AuditAction, type AuditCatalog, type AuditCatalogEntry, type AuditDescriptor, type AuditSeverity, type AutotelConfig, type Baggage, type BaggageEntry, type BaggageError, type BaggageFieldDefinition, type BaggageFieldType, BaggageSpanProcessor, type BaggageSpanProcessorOptions, type BuiltinPatternName, BusinessBaggage, type BusinessBaggageValues, CORRELATION_ID_BAGGAGE_KEY, type ClientAttrs, type CloudAttrs, type CodeAttrs, type ConsumerConfig, type ConsumerContext, type ContainerAttrs, type Context, type DBAttrs, type DBConfig, type DefineEventOptions, type DefinedEvent, type DeploymentAttrs, type DeviceAttrs, type DrainOptions, type DrainPipelineOptions, type EnrichContext, type EnricherDefinition, type EnricherOptions, type ErrorAttrs, type ErrorBuildOptions, type ErrorBuilder, type ErrorCatalog, type ErrorCatalogEntry, Event, type EventAttributes, type EventSubscriber, type EventsOptions, type Exception, type ExceptionAttrs, type FaaSAttrs, type FeatureFlagAttrs, FilteringSpanProcessor, type FilteringSpanProcessorOptions, type ForkLifecycle, type ForkOptions, type FunnelStatus, type GraphQLAttrs, HTTPAttributes, type HTTPClientAttrs, type HTTPConfig, type HTTPServerAttrs, type HrTime, type HttpDrainOptions, type HttpDrainRequest, type InstrumentOptions, type K8sAttrs, type LagMetricsConfig, type Link, type MaskFn, type MessagingAttrs, type MessagingConfig, type MessagingOperation, type MessagingSystem, Metric, type MetricsOptions, NORMALIZER_PATTERNS, NORMALIZER_PRESETS, type NetworkAttrs, NeverSampler, type OTelAttrs, type OperationContext, type TraceContext as OtelTraceContext, type OutcomeStatus, type ParsedError, type PeerAttrs, type PipelineDrainFn, type ProcessAttrs, type ProcessHandlersConfig, type ProducerConfig, type ProducerContext, REDACTOR_PATTERNS, REDACTOR_PRESETS, ROOT_CONTEXT, type RPCAttrs, RandomSampler, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type SafeBaggageOptions, type SafeBaggageSchema, type Sampler, type SamplingContext, type SamplingPreset, type SchemaLike, type ServerAddressAttrs, ServiceAttributes, type ServiceAttrs, type SessionAttrs, type SingleInstrumentOptions, type Span, type SpanAttributes, type SpanContext, type SpanFilterPredicate, SpanKind, type SpanLink, type SpanNameNormalizerConfig, type SpanNameNormalizerFn, type SpanNameNormalizerPreset, SpanNameNormalizingProcessor, type SpanNameNormalizingProcessorOptions, type SpanOptions, SpanStatusCode, type StepConfig, type StepContext, type StepStatus, type StringRedactor, type StructuredError, type StructuredErrorInput, type TLSAttrs, type TextMapGetter, type TextMapSetter, type ThreadAttrs, type TimeInput, type TraceContext$1 as TraceContext, type TraceFlags, type TraceState, type Tracer, type TracerProvider, URLAttributes, type URLAttrs, type UserAttrs, UserIdSampler, type ValuePatternConfig, type WithBaggageOptions, type WithNewContextOptions, type WorkflowConfig, type WorkflowContext, type WorkflowStatus, attrs, autoRedactPII, builtinPatterns, context, createAttributeRedactor, createCounter, createDeterministicTraceId, createDrainPipeline, createHistogram, createLinkFromHeaders, createNoopRequestLogger, createObservableGauge, createRedactedSpan, createSafeBaggageSchema, createStringRedactor, createStructuredError, createUpDownCounter, ctx, dbClient, defineAuditCatalog, defineBaggageSchema, defineDrain, defineEnricher, defineErrorCatalog, defineEvent, defineHttpDrain, enrichWithTraceContext, extractLinksFromBatch, finalizeSpan, flattenMetadata, flattenToAttributes, flush, formatDuration, generateCorrelationId, getActiveContext, getActiveSpan, getActiveTraceContext, getAutotelTracer, getAutotelTracerProvider, getCatalogCode, getCorrelationId, getCurrentWorkflowContext, getEventQueue, getEvents, getMeter, getMetrics, getOperationContext, getOrCreateCorrelationId, getRequestLogger, getRequestLoggerSafe, getStructuredErrorAttributes, getTraceContext, getTracer, hasRequestContext, httpClient, httpRequestHeaderAttribute, httpResponseHeaderAttribute, httpServer, identify, init, instrument, isCatalogError, isInWorkflow, isInitialized, isLoggerLocked, isTracing, lockLogger, mergeAttrs, mergeServiceResource, normalizeAttributeRedactorConfig, otelTrace, parseError, propagation, recordStructuredError, request, resetEvents, resetMetrics, resolveSamplingPreset, resolveTraceUrl, runInOperationContext, runWithCorrelationId, runWithRequestContext, runWithSpan, safeSetAttributes, samplingPresets, setAutotelTracerProvider, setCorrelationId, setCorrelationIdInBaggage, setDevice, setError, setException, setSession, setUser, shutdown, span, structuredErrorToJSON, toAttributeValue, trace, traceConsumer, traceDB, traceHTTP, traceMessaging, traceProducer, traceStep, traceWorkflow, track, validateAttribute, withBaggage, withNewContext, withTracing };
640
+ export { AUTOTEL_SAMPLING_TAIL_EVALUATED, AUTOTEL_SAMPLING_TAIL_KEEP, AdaptiveSampler, AlwaysSampler, type AttributeGuardrails, type AttributePolicy, AttributeRedactingProcessor, type AttributeRedactingProcessorOptions, type AttributeRedactorConfig, type AttributeRedactorFn, type AttributeRedactorPreset, type AttributeValue, type Attributes, type AuditAction, type AuditCatalog, type AuditCatalogEntry, type AuditDescriptor, type AuditSeverity, type AutotelConfig, type Baggage, type BaggageEntry, type BaggageError, type BaggageFieldDefinition, type BaggageFieldType, BaggageSpanProcessor, type BaggageSpanProcessorOptions, type BuiltinPatternName, BusinessBaggage, type BusinessBaggageValues, CORRELATION_ID_BAGGAGE_KEY, type ClientAttrs, type CloudAttrs, type CodeAttrs, type ConsumerConfig, type ConsumerContext, type ContainerAttrs, type Context, type DBAttrs, type DBConfig, type DefineEventOptions, type DefinedEvent, type DeploymentAttrs, type DeviceAttrs, type DrainOptions, type DrainPipelineOptions, type EnrichContext, type EnricherDefinition, type EnricherOptions, type ErrorAttrs, type ErrorBuildOptions, type ErrorBuilder, type ErrorCatalog, type ErrorCatalogEntry, Event, type EventAttributes, type EventSubscriber, type EventsOptions, type Exception, type ExceptionAttrs, type FaaSAttrs, type FeatureFlagAttrs, FilteringSpanProcessor, type FilteringSpanProcessorOptions, type ForkLifecycle, type ForkOptions, type FunnelStatus, type GraphQLAttrs, HTTPAttributes, type HTTPClientAttrs, type HTTPConfig, type HTTPServerAttrs, type HrTime, type HttpDrainOptions, type HttpDrainRequest, type InstrumentOptions, type K8sAttrs, type LagMetricsConfig, type Link, type MaskFn, type MessagingAttrs, type MessagingConfig, type MessagingOperation, type MessagingSystem, Metric, type MetricsOptions, NORMALIZER_PATTERNS, NORMALIZER_PRESETS, type NetworkAttrs, NeverSampler, type OTelAttrs, type OperationContext, type TraceContext as OtelTraceContext, type OutcomeStatus, type ParsedError, type PeerAttrs, type PipelineDrainFn, type ProcessAttrs, type ProcessHandlersConfig, type ProducerConfig, type ProducerContext, REDACTOR_PATTERNS, REDACTOR_PRESETS, ROOT_CONTEXT, type RPCAttrs, RandomSampler, type RequestLogLevel, type RequestLogSnapshot, type RequestLogger, type RequestLoggerOptions, type SafeBaggageOptions, type SafeBaggageSchema, type Sampler, type SamplingContext, type SamplingPreset, type SchemaLike, type ServerAddressAttrs, ServiceAttributes, type ServiceAttrs, type SessionAttrs, type SingleInstrumentOptions, type Span, type SpanAttributes, type SpanContext, type SpanFilterPredicate, SpanKind, type SpanLink, type SpanNameNormalizerConfig, type SpanNameNormalizerFn, type SpanNameNormalizerPreset, SpanNameNormalizingProcessor, type SpanNameNormalizingProcessorOptions, type SpanOptions, SpanStatusCode, type StepConfig, type StepContext, type StepStatus, type StringRedactor, type StructuredError, type StructuredErrorInput, type TLSAttrs, type TextMapGetter, type TextMapSetter, type ThreadAttrs, type TimeInput, type TraceContext$1 as TraceContext, type TraceFlags, type TraceState, type Tracer, type TracerProvider, URLAttributes, type URLAttrs, type UserAttrs, UserIdSampler, type ValuePatternConfig, type WithBaggageOptions, type WithNewContextOptions, type WorkflowConfig, type WorkflowContext, type WorkflowStatus, attrs, autoRedactPII, builtinPatterns, context, createAttributeRedactor, createCounter, createDeterministicTraceId, createDrainPipeline, createHistogram, createLinkFromHeaders, createNoopRequestLogger, createObservableGauge, createRedactedSpan, createSafeBaggageSchema, createStringRedactor, createStructuredError, createUpDownCounter, ctx, dbClient, defineAuditCatalog, defineBaggageSchema, defineDrain, defineEnricher, defineErrorCatalog, defineEvent, defineHttpDrain, enrichWithTraceContext, extractLinksFromBatch, finalizeSpan, flattenMetadata, flattenToAttributes, flush, formatDuration, generateCorrelationId, getActiveContext, getActiveSpan, getActiveTraceContext, getAutotelTracer, getAutotelTracerProvider, getCatalogCode, getCorrelationId, getCurrentWorkflowContext, getEventQueue, getEvents, getMeter, getMetrics, getOperationContext, getOrCreateCorrelationId, getRequestLogger, getRequestLoggerSafe, getStructuredErrorAttributes, getTraceContext, getTracer, hasRequestContext, httpClient, httpRequestHeaderAttribute, httpResponseHeaderAttribute, httpServer, identify, init, instrument, isCatalogError, isInWorkflow, isInitialized, isLoggerLocked, isTracing, lockLogger, mergeAttrs, mergeServiceResource, normalizeAttributeRedactorConfig, otelTrace, parseError, propagation, recordStructuredError, request, resetEvents, resetMetrics, resolveSamplingPreset, resolveTraceUrl, runInOperationContext, runWithCorrelationId, runWithRequestContext, runWithSpan, safeSetAttributes, samplingPresets, setAutotelTracerProvider, setCorrelationId, setCorrelationIdInBaggage, setDevice, setError, setException, setSession, setUser, shutdown, span, structuredErrorToJSON, toAttributeValue, trace, traceConsumer, traceDB, traceHTTP, traceMessaging, traceProducer, traceStep, traceWorkflow, track, validateAttribute, withBaggage, withNewContext, withTracing };
638
641
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/baggage-span-processor.ts","../src/trace-hybrid.ts","../src/operation-context.ts","../src/rate-limiter.ts","../src/event-queue.ts","../src/track.ts","../src/shutdown.ts","../src/request-logger.ts","../src/structured-error.ts","../src/error-catalog.ts","../src/flatten-attributes.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts","../src/pretty-log-formatter.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;UAaiB;;;;;EAKf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAkCW,gCAAgC;mBAC1B;EAEL,YAAA,UAAS;EAIrB,QAAQ,MAAM,QAAM,eAAe;EA4BnC,MAAM,OAAO;EAIP,YAAY;EAIZ,cAAc;;;;cC9DT,cAAc,iBAAwB;;;;;;;;;;;;;;;UCjBlC;;;;;EAKf;;;;;;;;;;;;;;;iBAsBc,uBAAuB;;;;;;;;;;;;;;;;;;;;;iBAwBvB,sBAAsB,GAAG,cAAc,UAAU,IAAI;;;;;;;;;UC5DpD;;EAEf;;EAEA;;;;UCoBe;EACf;EACA,aAAa;EACb;;EAEA;;EAEA;;EAEA,UAAU;;EAEV,SAAS;;UAUM;EACf;EACA;EACA;EACA;EACA,YAAY;;;;;;;;;;;;cAmED;UACH;UACA;mBACS;mBACA;mBACA;UACT;UACA;UAGA;UAGA;UAGA;EAEI,YAAA,aAAa,mBAAmB,SAAS,QAAQ;;;;UAsBrD;;;;UA8GA;;;;;UA6CA;;;;;UA2BA;;;;UAOA;;;;;;;EAoBR,QAAQ,OAAO;;;;UAmCP;;;;;UAaM;;;;UA0BA;;;;;;UAUA;;;;;;UA+DA;;;;;;;UAiDA;;;;;EAoDR,SAAS;;;;;EAsBT,YAAY;;;;;EASlB;;;;EAeA;;;;EAOA,uBAAuB;;;;EAOvB,oBAAoB;;;;;EAQpB,oBAAoB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC1gB9B,MAAM,eAAe,sBAAsB,qBACzD,aAAa,iBACb,OAAO,cAAc,QACrB,UAAU;;;;;iBAmCI,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBCzHX,MAAM;EAC1B;EACA;IACE;;;;;;;;;;;;;;;;;;;;;;;;iBAoFkB,YAAY;;;iBC1HlB,sBAAsB,GAAG,KAAK,gBAAc,UAAU,IAAI;UAIzD;EACf,IAAI,QAAQ;EACZ,KAAK,iBAAiB,SAAS;EAC/B,KAAK,iBAAiB,SAAS;EAC/B,MAAM,OAAO,gBAAgB,SAAS;EACtC,cAAc;EACd,QAAQ,YAAY,0BAA0B;EAC9C,KACE,eACA,iBAAiB,eACjB,UAAU;;UAIG;EACf;EACA;EACA;EACA;EACA,SAAS;;UAGM;;EAEf,UAAU,UAAU,8BAA8B;;;;;;UAOnC;;EAEf,gBAAgB,OAAO;;EAEvB,eAAe,OAAO;;UAGP;EACf,YAAY;;iBAkBE,iBACd,MAAM,gBACN,UAAU,uBACT;;;;;;;;;iBAuLa,kBAAkB,MAAM;;;;;;iBAWxB,qBACd,MAAM,gBACN,UAAU,uBACT;;;;;;iBAUa,2BAA2B;;;UC1T1B;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU;EACV;;EAEA,WAAW;;UAGI,wBAAwB;EACvC;EACA;EACA;EACA;EACA;EACA,UAAU;;WAED,WAAW;;iBAGN,sBACd,OAAO,uBACN;iBAiDa,sBACd,OAAO,kBACN;iBAuBa,6BACd,OAAO,QACN,eAAe;iBA8BF,sBACd,KAAK,KAAK,gDACV,OAAO;;;;UCrGQ;;;;;EAKf,oBAAoB;;EAEpB;;EAEA;;EAEA,iBAAiB;;EAEjB;;EAEA;;EAEA;;;UAIe;EACf;EACA,UAAU;;EAEV,WAAW;;KAGR,SAAS,KAAK;EAAY,UAAU,cAAc;IACnD,IACA;EAAY,MAAM,cAAc;IAC9B;KAGD,YAAY,UAAU,qBACzB,SAAS,mBACJ,UAAU,sBACV,QAAQ,SAAS,IAAI,UAAU;;UAGrB,aAAa,UAAU;MAClC,MAAM,YAAY,KAAK;;WAElB;;EAET,MAAM;;KAGI,aAAa,UAAU,eAAe,kCACtC,WAAW,IAAI,aAAa,EAAE;;iBAU1B,eAAe,iBAAiB,SAAS;;iBAKzC,eAAe;;;;;;iBASf,yBACR,UAAU,eAAe,oBAC/B,mBAAmB,SAAS,IAAI,aAAa;;KAqEnC;;UAGK;;EAEf,qBAAqB;;EAErB;;EAEA,WAAW;;;UAII;WACN;WACA,UAAU;WACV;;KAGN,oBAAoB,UAAU,qBACjC,SAAS,wBAAwB,QAAQ,SAAS;;UAGnC,gBAAgB,UAAU;MACrC,MAAM,oBAAoB,KAAK;WAC1B;WACA,UAAU;;KAGT,aAAa,UAAU,eAAe,kCACtC,WAAW,IAAI,gBAAgB,EAAE;;;;;iBAO7B,yBACR,UAAU,eAAe,oBAC/B,mBAAmB,SAAS,IAAI,aAAa;;;;;;;iBCzN/B,iBAAiB,iBAAiB;;;;;iBAmClC,oBACd,QAAQ,yBACR,kBACC,eAAe;;;UC5CD,aAAa,UAAU,SAAS,WAAW;;EAE1D;;EAEA,eAAe,iBAAiB,QAAQ;;EAExC,aAAa,UAAU,eAAe;;EAEtC,OAAO,UAAU,YAAY,QAAQ,YAAY;;UAGlC;EACf;EACA,SAAS;EACT;;UAGe,iBACf,UACA,SACA,WAAW,kBACH,KAAK,aAAa,UAAU,SAAS;EAC7C,SAAS,UAAU,YAAY,QAAQ,YAAY;EACnD;EACA;EACA,oBAAoB,QAAQ;EAC5B,kBAAkB,QAAQ;;iBAqDZ,YAAY,UAAU,SAAS,WAAW,UACxD,SAAS,aAAa,UAAU,SAAS,aACvC,KAAK,WAAW,eAAe;iBAsBnB,gBAAgB,UAAU,SAAS,WAAW,UAC5D,SAAS,iBAAiB,UAAU,SAAS,aAC3C,KAAK,WAAW,eAAe;;;UCzGlB,cAAc,eAAe;EAC5C,OAAO;EACP;IACE;IACA;IACA;;EAEF;IACE;;EAEF,UAAU;;UAGK,mBACf,eAAe,yBACf;;EAGA;;EAEA,aAAa;;EAEb,UAAU,KAAK,cAAc,YAAY;;UAG1B;;EAEf;;iBAuBc,eACd,eAAe,yBACf,uBAEA,KAAK,mBAAmB,QAAQ,SAChC,UAAS,mBACP,KAAK,cAAc;;;;;;;;;;;iBCUP,eAAe"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/baggage-span-processor.ts","../src/trace-hybrid.ts","../src/operation-context.ts","../src/rate-limiter.ts","../src/event-queue.ts","../src/track.ts","../src/shutdown.ts","../src/request-logger.ts","../src/structured-error.ts","../src/error-catalog.ts","../src/flatten-attributes.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts","../src/pretty-log-formatter.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -105,6 +105,7 @@ function getRequestLogger(ctx, options) {
105
105
  const contextState = {};
106
106
  let emitted = false;
107
107
  let lastSnapshot = null;
108
+ let hasExplicitLevel = false;
108
109
  const addLogEvent = (level, message, fields) => {
109
110
  const attrs = fields ? flattenToAttributes(fields) : void 0;
110
111
  emitCorrelatedEvent(activeContext, `log.${level}`, {
@@ -122,6 +123,12 @@ function getRequestLogger(ctx, options) {
122
123
  mergeInto$1(contextState, fields);
123
124
  activeContext.setAttributes(flattenToAttributes(fields));
124
125
  },
126
+ setLevel(level) {
127
+ sealCheck("log.setLevel()", ["autotel.log.level"]);
128
+ if (emitted) return;
129
+ hasExplicitLevel = true;
130
+ activeContext.setAttribute("autotel.log.level", level);
131
+ },
125
132
  info(message, fields) {
126
133
  const keys = fields ? ["message", ...Object.keys(fields).filter((k) => k !== "requestLogs")] : ["message"];
127
134
  sealCheck("log.info()", keys);
@@ -137,7 +144,7 @@ function getRequestLogger(ctx, options) {
137
144
  sealCheck("log.warn()", keys);
138
145
  if (emitted) return;
139
146
  addLogEvent("warn", message, fields);
140
- activeContext.setAttribute("autotel.log.level", "warn");
147
+ if (!hasExplicitLevel) activeContext.setAttribute("autotel.log.level", "warn");
141
148
  if (fields) {
142
149
  mergeInto$1(contextState, fields);
143
150
  activeContext.setAttributes(flattenToAttributes(fields));
@@ -154,7 +161,7 @@ function getRequestLogger(ctx, options) {
154
161
  mergeInto$1(contextState, fields);
155
162
  activeContext.setAttributes(flattenToAttributes(fields));
156
163
  }
157
- activeContext.setAttribute("autotel.log.level", "error");
164
+ if (!hasExplicitLevel) activeContext.setAttribute("autotel.log.level", "error");
158
165
  },
159
166
  getContext() {
160
167
  return { ...contextState };
@@ -258,6 +265,7 @@ function createNoopRequestLogger() {
258
265
  });
259
266
  return {
260
267
  set() {},
268
+ setLevel() {},
261
269
  info() {},
262
270
  warn() {},
263
271
  error() {},
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["otelTraceApi","autotelTraceFn","mergeInto","otelTrace"],"sources":["../src/trace-hybrid.ts","../src/define-event.ts","../src/request-logger.ts","../src/error-catalog.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts"],"sourcesContent":["/**\n * Hybrid `trace` export: callable like autotel's `trace(fn)`, AND exposes the\n * full `@opentelemetry/api` `TraceAPI` surface (`trace.getActiveSpan()`,\n * `trace.getTracer()`, …) so existing OTel code \"just works\" when imported\n * from `autotel`.\n *\n * Implementation: `Object.assign` mutates the autotel `trace` function to\n * attach the OTel TraceAPI methods. Because every reference to `trace` across\n * autotel resolves to the same function instance, this is a one-time, global\n * augmentation.\n */\n\nimport { trace as otelTraceApi } from '@opentelemetry/api';\nimport { trace as autotelTraceFn } from './functional';\n\nconst otelMethods = {\n // Class methods on TraceAPI — bind to the singleton.\n setGlobalTracerProvider:\n otelTraceApi.setGlobalTracerProvider.bind(otelTraceApi),\n getTracerProvider: otelTraceApi.getTracerProvider.bind(otelTraceApi),\n getTracer: otelTraceApi.getTracer.bind(otelTraceApi),\n disable: otelTraceApi.disable.bind(otelTraceApi),\n // Instance fields on TraceAPI — already standalone, copy by reference.\n wrapSpanContext: otelTraceApi.wrapSpanContext,\n isSpanContextValid: otelTraceApi.isSpanContextValid,\n deleteSpan: otelTraceApi.deleteSpan,\n getSpan: otelTraceApi.getSpan,\n getActiveSpan: otelTraceApi.getActiveSpan,\n getSpanContext: otelTraceApi.getSpanContext,\n setSpan: otelTraceApi.setSpan,\n setSpanContext: otelTraceApi.setSpanContext,\n};\n\nexport const trace: typeof autotelTraceFn & typeof otelTraceApi = Object.assign(\n autotelTraceFn,\n otelMethods,\n) as typeof autotelTraceFn & typeof otelTraceApi;\n","import { track } from './track';\nimport { hashJson } from './stable-hash';\nimport type { EventSchemaMetadata } from './event-subscriber';\n\ntype SafeParseResult<T> =\n { success: true; data: T } | { success: false; error: unknown };\n\nexport interface SchemaLike<T> {\n safeParse(input: unknown): SafeParseResult<T>;\n}\n\nexport interface DefineEventOptions<S> {\n toJsonSchema?: (schema: S) => unknown;\n}\n\nexport interface DefinedEvent<Name extends string, Payload> {\n readonly name: Name;\n readonly schemaMetadata?: EventSchemaMetadata;\n track(payload: Payload): void;\n}\n\nexport function defineEvent<\n Name extends string,\n Payload,\n S extends SchemaLike<Payload>,\n>(\n name: Name,\n schema: S,\n options: DefineEventOptions<S> = {},\n): DefinedEvent<Name, Payload> {\n const jsonSchema = options.toJsonSchema?.(schema);\n const schemaMetadata = jsonSchema\n ? {\n source: 'zod' as const,\n jsonSchema,\n hash: hashJson(jsonSchema),\n }\n : undefined;\n\n return {\n name,\n schemaMetadata,\n track(payload: Payload) {\n const parsed = schema.safeParse(payload);\n if (!parsed.success) {\n throw new Error(\n `Invalid payload for event \"${name}\". Schema validation failed.`,\n );\n }\n track(\n name,\n parsed.data,\n schemaMetadata ? { schema: schemaMetadata } : undefined,\n );\n },\n };\n}\n","// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { trace as otelTrace } from '@opentelemetry/api';\nimport type { TraceContext } from './trace-context';\nimport { createTraceContext } from './trace-context';\nimport { recordStructuredError } from './structured-error';\nimport { flattenToAttributes } from './flatten-attributes';\nimport { emitCorrelatedEvent } from './correlated-events';\n\nconst POST_EMIT_FORK_HINT =\n \"For intentional background work tied to this request, use log.fork('label', fn) when available.\";\n\nfunction warnPostEmit(method: string, detail: string): void {\n console.warn(\n `[autotel] ${method} called after the wide event was emitted — ${detail} This data will not appear in observability. ${POST_EMIT_FORK_HINT}`,\n );\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n mergeInto(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n );\n } else if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {\n target[key] = [...targetVal, ...sourceVal];\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nconst requestContextStore =\n new nodeAsyncHooks.AsyncLocalStorage<TraceContext>();\n\nexport function runWithRequestContext<T>(ctx: TraceContext, fn: () => T): T {\n return requestContextStore.run(ctx, fn);\n}\n\nexport interface RequestLogger {\n set(fields: Record<string, unknown>): void;\n info(message: string, fields?: Record<string, unknown>): void;\n warn(message: string, fields?: Record<string, unknown>): void;\n error(error: Error | string, fields?: Record<string, unknown>): void;\n getContext(): Record<string, unknown>;\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;\n fork(\n label: string,\n fn: () => void | Promise<void>,\n options?: ForkOptions,\n ): void;\n}\n\nexport interface RequestLogSnapshot {\n timestamp: string;\n traceId: string;\n spanId: string;\n correlationId: string;\n context: Record<string, unknown>;\n}\n\nexport interface RequestLoggerOptions {\n /** Callback invoked by emitNow() for manual fan-out. */\n onEmit?: (snapshot: RequestLogSnapshot) => void | Promise<void>;\n}\n\n/**\n * Optional lifecycle hooks for adapters that need to track child loggers\n * spawned by `log.fork()` (e.g. active logger maps in framework integrations).\n */\nexport interface ForkLifecycle {\n /** Called after the child logger is created, before `fn` runs. */\n onChildEnter?: (child: RequestLogger) => void;\n /** Called after the child has finished (emit + drain), success or failure. */\n onChildExit?: (child: RequestLogger) => void;\n}\n\nexport interface ForkOptions {\n lifecycle?: ForkLifecycle;\n}\n\nfunction resolveContext(ctx?: TraceContext): TraceContext {\n if (ctx) return ctx;\n\n const stored = requestContextStore.getStore();\n if (stored) return stored;\n\n const span = otelTrace.getActiveSpan();\n if (!span) {\n throw new Error(\n '[autotel] getRequestLogger() requires an active span or runWithRequestContext(). Wrap your handler with trace() or use runWithRequestContext().',\n );\n }\n return createTraceContext(span);\n}\n\nexport function getRequestLogger(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger {\n const activeContext = resolveContext(ctx);\n const contextState: Record<string, unknown> = {};\n let emitted = false;\n let lastSnapshot: RequestLogSnapshot | null = null;\n\n const addLogEvent = (\n level: 'info' | 'warn' | 'error',\n message: string,\n fields?: Record<string, unknown>,\n ) => {\n const attrs = fields ? flattenToAttributes(fields) : undefined;\n emitCorrelatedEvent(activeContext, `log.${level}`, {\n message,\n ...attrs,\n });\n };\n\n const sealCheck = (method: string, keys: string[]): void => {\n if (emitted) {\n warnPostEmit(\n method,\n `Keys dropped: ${keys.length > 0 ? keys.join(', ') : '(empty)'}.`,\n );\n }\n };\n\n return {\n set(fields: Record<string, unknown>) {\n sealCheck('log.set()', Object.keys(fields));\n if (emitted) return;\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n },\n\n info(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.info()', keys);\n if (emitted) return;\n addLogEvent('info', message, fields);\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n warn(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.warn()', keys);\n if (emitted) return;\n addLogEvent('warn', message, fields);\n activeContext.setAttribute('autotel.log.level', 'warn');\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n error(error: Error | string, fields?: Record<string, unknown>) {\n const keys = fields ? [...Object.keys(fields), 'error'] : ['error'];\n sealCheck('log.error()', keys);\n if (emitted) return;\n const err = typeof error === 'string' ? new Error(error) : error;\n recordStructuredError(activeContext, err);\n addLogEvent('error', err.message, fields);\n\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n activeContext.setAttribute('autotel.log.level', 'error');\n },\n\n getContext() {\n return { ...contextState };\n },\n\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot {\n if (emitted) {\n warnPostEmit('log.emitNow()', 'Ignoring duplicate emit.');\n return lastSnapshot as RequestLogSnapshot;\n }\n\n const mergedContext = {\n ...contextState,\n ...overrides,\n };\n const flattened = flattenToAttributes(mergedContext);\n activeContext.setAttributes(flattened);\n\n const snapshot: RequestLogSnapshot = {\n timestamp: new Date().toISOString(),\n traceId: activeContext.traceId,\n spanId: activeContext.spanId,\n correlationId: activeContext.correlationId,\n context: mergedContext,\n };\n\n emitCorrelatedEvent(activeContext, 'log.emit.manual', {\n ...flattened,\n });\n\n if (options?.onEmit) {\n Promise.resolve(options.onEmit(snapshot)).catch((error) => {\n console.warn('[autotel] request logger onEmit failed:', error);\n });\n }\n\n emitted = true;\n lastSnapshot = snapshot;\n return snapshot;\n },\n\n fork(\n label: string,\n fn: () => void | Promise<void>,\n forkOptions?: ForkOptions,\n ): void {\n const parentRequestId = activeContext.correlationId;\n if (typeof parentRequestId !== 'string' || parentRequestId.length === 0) {\n throw new Error(\n '[autotel] log.fork() requires the parent logger to have a correlationId. ' +\n 'Ensure the request was created by autotel middleware.',\n );\n }\n\n const tracer = otelTrace.getTracer('autotel.request-logger');\n const lifecycle = forkOptions?.lifecycle;\n void tracer.startActiveSpan(`request.fork:${label}`, (childSpan) => {\n const childContext: TraceContext = {\n ...createTraceContext(childSpan),\n correlationId: crypto.randomUUID(),\n };\n\n requestContextStore.run(childContext, () => {\n const childLog = getRequestLogger(childContext);\n childLog.set({\n operation: label,\n _parentCorrelationId: parentRequestId,\n });\n\n lifecycle?.onChildEnter?.(childLog);\n\n void Promise.resolve()\n .then(() => fn())\n .then(() => {\n childLog.emitNow();\n })\n .catch((error_: unknown) => {\n const error =\n error_ instanceof Error ? error_ : new Error(String(error_));\n childLog.error(error);\n childLog.emitNow();\n })\n .finally(() => {\n try {\n lifecycle?.onChildExit?.(childLog);\n } catch (hookError) {\n console.warn(\n '[autotel] fork onChildExit hook threw:',\n hookError,\n );\n }\n childSpan.end();\n });\n });\n });\n },\n };\n}\n\n/**\n * Returns `true` when a request-logger context can be resolved without throwing —\n * i.e. an explicit `ctx` was provided, a `runWithRequestContext()` scope is active,\n * or there is an active OpenTelemetry span.\n *\n * Use this to branch on observability availability instead of wrapping\n * {@link getRequestLogger} in try/catch.\n */\nexport function hasRequestContext(ctx?: TraceContext): boolean {\n if (ctx) return true;\n if (requestContextStore.getStore()) return true;\n return otelTrace.getActiveSpan() != null;\n}\n\n/**\n * Like {@link getRequestLogger}, but returns `null` instead of throwing when no\n * request context is available. Intended for best-effort instrumentation where a\n * missing telemetry context must never crash business logic.\n */\nexport function getRequestLoggerSafe(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger | null {\n if (!hasRequestContext(ctx)) return null;\n return getRequestLogger(ctx, options);\n}\n\n/**\n * A no-op {@link RequestLogger} whose methods do nothing. Used as a fallback by\n * best-effort instrumentation so wrapped handlers can run un-instrumented without\n * branching on logger presence.\n */\nexport function createNoopRequestLogger(): RequestLogger {\n const snapshot = (): RequestLogSnapshot => ({\n timestamp: new Date().toISOString(),\n traceId: '',\n spanId: '',\n correlationId: '',\n context: {},\n });\n\n return {\n set() {},\n info() {},\n warn() {},\n error() {},\n getContext() {\n return {};\n },\n emitNow() {\n return snapshot();\n },\n fork(_label, fn) {\n void Promise.resolve().then(() => fn());\n },\n };\n}\n","/**\n * Typed error and audit catalogs.\n *\n * Group related errors into one catalog and get a refactor-safe builder per\n * code, with autocomplete at every call site and typed message parameters.\n *\n * @example\n * ```typescript\n * import { defineErrorCatalog } from 'autotel';\n *\n * export const billing = defineErrorCatalog('billing', {\n * PAYMENT_DECLINED: {\n * status: 402,\n * message: 'Card declined',\n * why: 'The issuer rejected the charge',\n * fix: 'Try a different payment method',\n * },\n * INSUFFICIENT_FUNDS: {\n * status: 402,\n * message: ({ available, required }: { available: number; required: number }) =>\n * `Insufficient funds: $${available} of $${required}`,\n * },\n * });\n *\n * throw billing.PAYMENT_DECLINED({ cause: stripeError });\n * throw billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });\n *\n * // In a catch block — refactor-safe, no magic strings:\n * if (billing.PAYMENT_DECLINED.match(err)) { ... }\n * ```\n */\n\nimport {\n createStructuredError,\n type StructuredError,\n} from './structured-error';\n\nconst catalogCodeKey = Symbol.for('autotel.catalog.code');\n\n/** Definition of a single error in a catalog. */\nexport interface ErrorCatalogEntry {\n /**\n * Human-readable message. Use a function to interpolate typed parameters;\n * the parameter type flows through to the call site.\n */\n message: string | ((params: never) => string);\n /** HTTP status to surface to clients. */\n status?: number;\n /** Stable error code. Defaults to `${namespace}.${KEY}`. */\n code?: string | number;\n /** Why it happened. A function receives the same params as `message`. */\n why?: string | ((params: never) => string);\n /** What the caller should do next. */\n fix?: string;\n /** Docs or runbook link. */\n link?: string;\n /** Error name. Defaults to the catalog key. */\n name?: string;\n}\n\n/** Per-call options passed alongside (or instead of) typed params. */\nexport interface ErrorBuildOptions {\n cause?: unknown;\n details?: Record<string, unknown>;\n /** Backend-only context. Never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\ntype ParamsOf<E> = E extends { message: (params: infer P) => string }\n ? P\n : E extends { why: (params: infer P) => string }\n ? P\n : void;\n\ntype BuilderArgs<E extends ErrorCatalogEntry> =\n ParamsOf<E> extends void\n ? [options?: ErrorBuildOptions]\n : [params: ParamsOf<E>, options?: ErrorBuildOptions];\n\n/** A callable error factory produced by {@link defineErrorCatalog}. */\nexport interface ErrorBuilder<E extends ErrorCatalogEntry> {\n (...args: BuilderArgs<E>): StructuredError;\n /** Stable code assigned to every error from this entry. */\n readonly code: string | number;\n /** True when `error` was produced by this catalog entry. */\n match(error: unknown): boolean;\n}\n\nexport type ErrorCatalog<T extends Record<string, ErrorCatalogEntry>> = {\n readonly [K in keyof T]: ErrorBuilder<T[K]>;\n};\n\nfunction readCatalogCode(error: unknown): string | number | undefined {\n if (error === null || typeof error !== 'object') return undefined;\n return (error as Record<symbol, unknown>)[catalogCodeKey] as\n string | number | undefined;\n}\n\n/** True when `error` was produced by any autotel error catalog. */\nexport function isCatalogError(error: unknown): error is StructuredError {\n return readCatalogCode(error) !== undefined;\n}\n\n/** Returns the catalog code of `error`, or `undefined` if it has none. */\nexport function getCatalogCode(error: unknown): string | number | undefined {\n return readCatalogCode(error);\n}\n\n/**\n * Define a typed error catalog. Returns an object whose keys are error\n * builders. Each builder produces a {@link StructuredError} carrying the\n * entry's message, status, code, why, fix, and link.\n */\nexport function defineErrorCatalog<\n const T extends Record<string, ErrorCatalogEntry>,\n>(namespace: string, entries: T): ErrorCatalog<T> {\n const catalog: Record<string, ErrorBuilder<ErrorCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n ErrorCatalogEntry,\n ][]) {\n const code = entry.code ?? `${namespace}.${key}`;\n const usesParams =\n typeof entry.message === 'function' || typeof entry.why === 'function';\n\n const builder = ((\n paramsOrOptions?: unknown,\n maybeOptions?: ErrorBuildOptions,\n ): StructuredError => {\n const params = usesParams ? paramsOrOptions : undefined;\n const options = (usesParams ? maybeOptions : paramsOrOptions) as\n ErrorBuildOptions | undefined;\n\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n const why =\n typeof entry.why === 'function'\n ? (entry.why as (p: unknown) => string)(params)\n : entry.why;\n\n const error = createStructuredError({\n message,\n name: entry.name ?? key,\n code,\n ...(entry.status === undefined ? {} : { status: entry.status }),\n ...(why === undefined ? {} : { why }),\n ...(entry.fix === undefined ? {} : { fix: entry.fix }),\n ...(entry.link === undefined ? {} : { link: entry.link }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n ...(options?.details === undefined ? {} : { details: options.details }),\n ...(options?.internal === undefined\n ? {}\n : { internal: options.internal }),\n });\n\n Object.defineProperty(error, catalogCodeKey, {\n value: code,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n return error;\n }) as ErrorBuilder<ErrorCatalogEntry>;\n\n Object.defineProperty(builder, 'code', {\n value: code,\n enumerable: true,\n });\n Object.defineProperty(builder, 'match', {\n value: (error: unknown): boolean => readCatalogCode(error) === code,\n enumerable: false,\n });\n\n catalog[key] = builder;\n }\n\n return Object.freeze(catalog) as ErrorCatalog<T>;\n}\n\n/** Severity of an audit action. */\nexport type AuditSeverity = 'info' | 'warn' | 'critical';\n\n/** Definition of a single action in an audit catalog. */\nexport interface AuditCatalogEntry {\n /** Human-readable description. Use a function for typed params. */\n message?: string | ((params: never) => string);\n /** Stable action name. Defaults to `${namespace}.${KEY}`. */\n action?: string;\n /** Severity of the action. Defaults to `'info'`. */\n severity?: AuditSeverity;\n}\n\n/** A resolved audit action descriptor produced by an audit catalog. */\nexport interface AuditAction {\n readonly action: string;\n readonly severity: AuditSeverity;\n readonly message?: string;\n}\n\ntype AuditDescriptorArgs<E extends AuditCatalogEntry> =\n ParamsOf<E> extends void ? [] : [params: ParamsOf<E>];\n\n/** A callable audit-action descriptor produced by {@link defineAuditCatalog}. */\nexport interface AuditDescriptor<E extends AuditCatalogEntry> {\n (...args: AuditDescriptorArgs<E>): AuditAction;\n readonly action: string;\n readonly severity: AuditSeverity;\n}\n\nexport type AuditCatalog<T extends Record<string, AuditCatalogEntry>> = {\n readonly [K in keyof T]: AuditDescriptor<T[K]>;\n};\n\n/**\n * Define a typed audit catalog. Returns typed action descriptors you can pass\n * to `track()` or audit helpers without scattering magic strings.\n */\nexport function defineAuditCatalog<\n const T extends Record<string, AuditCatalogEntry>,\n>(namespace: string, entries: T): AuditCatalog<T> {\n const catalog: Record<string, AuditDescriptor<AuditCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n AuditCatalogEntry,\n ][]) {\n const action = entry.action ?? `${namespace}.${key}`;\n const severity: AuditSeverity = entry.severity ?? 'info';\n\n const descriptor = ((params?: unknown): AuditAction => {\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n return Object.freeze({\n action,\n severity,\n ...(message === undefined ? {} : { message }),\n });\n }) as AuditDescriptor<AuditCatalogEntry>;\n\n Object.defineProperty(descriptor, 'action', {\n value: action,\n enumerable: true,\n });\n Object.defineProperty(descriptor, 'severity', {\n value: severity,\n enumerable: true,\n });\n\n catalog[key] = descriptor;\n }\n\n return Object.freeze(catalog) as AuditCatalog<T>;\n}\n","export interface DrainOptions<TContext, TConfig, TPayload = TContext> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Return null to skip draining (e.g. missing API key in dev). */\n resolve: () => TConfig | null | Promise<TConfig | null>;\n /** Transform contexts into payloads. Defaults to identity. */\n transform?: (contexts: TContext[]) => TPayload[];\n /** Transport implementation. */\n send: (payloads: TPayload[], config: TConfig) => Promise<void>;\n}\n\nexport interface HttpDrainRequest {\n url: string;\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface HttpDrainOptions<\n TContext,\n TConfig,\n TPayload = TContext,\n> extends Omit<DrainOptions<TContext, TConfig, TPayload>, 'send'> {\n encode: (payloads: TPayload[], config: TConfig) => HttpDrainRequest | null;\n timeoutMs?: number;\n retries?: number;\n resolveTimeoutMs?: (config: TConfig) => number | undefined;\n resolveRetries?: (config: TConfig) => number | undefined;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_RETRIES = 2;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n t.unref?.();\n });\n}\n\nasync function postWithRetry(options: {\n name: string;\n request: HttpDrainRequest;\n timeoutMs: number;\n retries: number;\n}): Promise<void> {\n const { name, request, timeoutMs, retries } = options;\n const attempts = Math.max(1, retries);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n timeout.unref?.();\n try {\n const response = await fetch(request.url, {\n method: 'POST',\n headers: request.headers,\n body: request.body,\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(\n `[autotel/${name}] HTTP ${response.status} draining ${request.url}`,\n );\n }\n return;\n } catch (error) {\n lastError = error;\n if (attempt < attempts) {\n await delay(100 * attempt);\n }\n } finally {\n clearTimeout(timeout);\n }\n }\n\n throw lastError;\n}\n\nexport function defineDrain<TContext, TConfig, TPayload = TContext>(\n options: DrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return async (ctx: TContext | TContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx];\n if (contexts.length === 0) return;\n\n const config = await options.resolve();\n if (!config) return;\n\n const payloads = options.transform\n ? options.transform(contexts)\n : (contexts as unknown as TPayload[]);\n\n if (payloads.length === 0) return;\n\n try {\n await options.send(payloads, config);\n } catch (error) {\n console.error(`[autotel/${options.name}] drain failed:`, error);\n }\n };\n}\n\nexport function defineHttpDrain<TContext, TConfig, TPayload = TContext>(\n options: HttpDrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return defineDrain<TContext, TConfig, TPayload>({\n name: options.name,\n resolve: options.resolve,\n transform: options.transform,\n send: async (payloads, config) => {\n const request = options.encode(payloads, config);\n if (!request) return;\n const timeoutMs =\n options.resolveTimeoutMs?.(config) ??\n options.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const retries =\n options.resolveRetries?.(config) ?? options.retries ?? DEFAULT_RETRIES;\n\n await postWithRetry({\n name: options.name,\n request,\n timeoutMs,\n retries,\n });\n },\n });\n}\n","export interface EnrichContext<TEvent extends Record<string, unknown>> {\n event: TEvent;\n request?: {\n method?: string;\n path?: string;\n requestId?: string;\n };\n response?: {\n status?: number;\n };\n headers?: Record<string, string>;\n}\n\nexport interface EnricherDefinition<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Top-level field to merge computed values into. */\n field: keyof TEvent & string;\n /** Return undefined to skip enrichment. */\n compute: (ctx: EnrichContext<TEvent>) => TValue | undefined;\n}\n\nexport interface EnricherOptions {\n /** Replace existing field value instead of merge. Default false. */\n overwrite?: boolean;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (isPlainObject(sourceVal) && isPlainObject(targetVal)) {\n mergeInto(targetVal, sourceVal);\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nexport function defineEnricher<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n>(\n def: EnricherDefinition<TEvent, TValue>,\n options: EnricherOptions = {},\n): (ctx: EnrichContext<TEvent>) => void {\n return (ctx: EnrichContext<TEvent>) => {\n let computed: TValue | undefined;\n try {\n computed = def.compute(ctx);\n } catch (error) {\n console.error(`[autotel/${def.name}] enrich failed:`, error);\n return;\n }\n\n if (!computed) return;\n\n if (options.overwrite || !isPlainObject(ctx.event[def.field])) {\n (ctx.event as Record<string, unknown>)[def.field] = computed;\n return;\n }\n\n mergeInto(\n ctx.event[def.field] as unknown as Record<string, unknown>,\n computed as unknown as Record<string, unknown>,\n );\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["otelTraceApi","autotelTraceFn","mergeInto","otelTrace"],"sources":["../src/trace-hybrid.ts","../src/define-event.ts","../src/request-logger.ts","../src/error-catalog.ts","../src/drain-toolkit.ts","../src/enricher-toolkit.ts"],"sourcesContent":["/**\n * Hybrid `trace` export: callable like autotel's `trace(fn)`, AND exposes the\n * full `@opentelemetry/api` `TraceAPI` surface (`trace.getActiveSpan()`,\n * `trace.getTracer()`, …) so existing OTel code \"just works\" when imported\n * from `autotel`.\n *\n * Implementation: `Object.assign` mutates the autotel `trace` function to\n * attach the OTel TraceAPI methods. Because every reference to `trace` across\n * autotel resolves to the same function instance, this is a one-time, global\n * augmentation.\n */\n\nimport { trace as otelTraceApi } from '@opentelemetry/api';\nimport { trace as autotelTraceFn } from './functional';\n\nconst otelMethods = {\n // Class methods on TraceAPI — bind to the singleton.\n setGlobalTracerProvider:\n otelTraceApi.setGlobalTracerProvider.bind(otelTraceApi),\n getTracerProvider: otelTraceApi.getTracerProvider.bind(otelTraceApi),\n getTracer: otelTraceApi.getTracer.bind(otelTraceApi),\n disable: otelTraceApi.disable.bind(otelTraceApi),\n // Instance fields on TraceAPI — already standalone, copy by reference.\n wrapSpanContext: otelTraceApi.wrapSpanContext,\n isSpanContextValid: otelTraceApi.isSpanContextValid,\n deleteSpan: otelTraceApi.deleteSpan,\n getSpan: otelTraceApi.getSpan,\n getActiveSpan: otelTraceApi.getActiveSpan,\n getSpanContext: otelTraceApi.getSpanContext,\n setSpan: otelTraceApi.setSpan,\n setSpanContext: otelTraceApi.setSpanContext,\n};\n\nexport const trace: typeof autotelTraceFn & typeof otelTraceApi = Object.assign(\n autotelTraceFn,\n otelMethods,\n) as typeof autotelTraceFn & typeof otelTraceApi;\n","import { track } from './track';\nimport { hashJson } from './stable-hash';\nimport type { EventSchemaMetadata } from './event-subscriber';\n\ntype SafeParseResult<T> =\n { success: true; data: T } | { success: false; error: unknown };\n\nexport interface SchemaLike<T> {\n safeParse(input: unknown): SafeParseResult<T>;\n}\n\nexport interface DefineEventOptions<S> {\n toJsonSchema?: (schema: S) => unknown;\n}\n\nexport interface DefinedEvent<Name extends string, Payload> {\n readonly name: Name;\n readonly schemaMetadata?: EventSchemaMetadata;\n track(payload: Payload): void;\n}\n\nexport function defineEvent<\n Name extends string,\n Payload,\n S extends SchemaLike<Payload>,\n>(\n name: Name,\n schema: S,\n options: DefineEventOptions<S> = {},\n): DefinedEvent<Name, Payload> {\n const jsonSchema = options.toJsonSchema?.(schema);\n const schemaMetadata = jsonSchema\n ? {\n source: 'zod' as const,\n jsonSchema,\n hash: hashJson(jsonSchema),\n }\n : undefined;\n\n return {\n name,\n schemaMetadata,\n track(payload: Payload) {\n const parsed = schema.safeParse(payload);\n if (!parsed.success) {\n throw new Error(\n `Invalid payload for event \"${name}\". Schema validation failed.`,\n );\n }\n track(\n name,\n parsed.data,\n schemaMetadata ? { schema: schemaMetadata } : undefined,\n );\n },\n };\n}\n","// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeAsyncHooks from 'node:async_hooks';\nimport { trace as otelTrace } from '@opentelemetry/api';\nimport type { TraceContext } from './trace-context';\nimport { createTraceContext } from './trace-context';\nimport { recordStructuredError } from './structured-error';\nimport { flattenToAttributes } from './flatten-attributes';\nimport { emitCorrelatedEvent } from './correlated-events';\n\nconst POST_EMIT_FORK_HINT =\n \"For intentional background work tied to this request, use log.fork('label', fn) when available.\";\n\nfunction warnPostEmit(method: string, detail: string): void {\n console.warn(\n `[autotel] ${method} called after the wide event was emitted — ${detail} This data will not appear in observability. ${POST_EMIT_FORK_HINT}`,\n );\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n mergeInto(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n );\n } else if (Array.isArray(targetVal) && Array.isArray(sourceVal)) {\n target[key] = [...targetVal, ...sourceVal];\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nconst requestContextStore =\n new nodeAsyncHooks.AsyncLocalStorage<TraceContext>();\n\nexport function runWithRequestContext<T>(ctx: TraceContext, fn: () => T): T {\n return requestContextStore.run(ctx, fn);\n}\n\nexport interface RequestLogger {\n set(fields: Record<string, unknown>): void;\n /** Set snapshot severity without adding a log event or exception. */\n setLevel(level: RequestLogLevel): void;\n info(message: string, fields?: Record<string, unknown>): void;\n warn(message: string, fields?: Record<string, unknown>): void;\n error(error: Error | string, fields?: Record<string, unknown>): void;\n getContext(): Record<string, unknown>;\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot;\n fork(\n label: string,\n fn: () => void | Promise<void>,\n options?: ForkOptions,\n ): void;\n}\n\nexport type RequestLogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport interface RequestLogSnapshot {\n timestamp: string;\n traceId: string;\n spanId: string;\n correlationId: string;\n context: Record<string, unknown>;\n}\n\nexport interface RequestLoggerOptions {\n /** Callback invoked by emitNow() for manual fan-out. */\n onEmit?: (snapshot: RequestLogSnapshot) => void | Promise<void>;\n}\n\n/**\n * Optional lifecycle hooks for adapters that need to track child loggers\n * spawned by `log.fork()` (e.g. active logger maps in framework integrations).\n */\nexport interface ForkLifecycle {\n /** Called after the child logger is created, before `fn` runs. */\n onChildEnter?: (child: RequestLogger) => void;\n /** Called after the child has finished (emit + drain), success or failure. */\n onChildExit?: (child: RequestLogger) => void;\n}\n\nexport interface ForkOptions {\n lifecycle?: ForkLifecycle;\n}\n\nfunction resolveContext(ctx?: TraceContext): TraceContext {\n if (ctx) return ctx;\n\n const stored = requestContextStore.getStore();\n if (stored) return stored;\n\n const span = otelTrace.getActiveSpan();\n if (!span) {\n throw new Error(\n '[autotel] getRequestLogger() requires an active span or runWithRequestContext(). Wrap your handler with trace() or use runWithRequestContext().',\n );\n }\n return createTraceContext(span);\n}\n\nexport function getRequestLogger(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger {\n const activeContext = resolveContext(ctx);\n const contextState: Record<string, unknown> = {};\n let emitted = false;\n let lastSnapshot: RequestLogSnapshot | null = null;\n let hasExplicitLevel = false;\n\n const addLogEvent = (\n level: 'info' | 'warn' | 'error',\n message: string,\n fields?: Record<string, unknown>,\n ) => {\n const attrs = fields ? flattenToAttributes(fields) : undefined;\n emitCorrelatedEvent(activeContext, `log.${level}`, {\n message,\n ...attrs,\n });\n };\n\n const sealCheck = (method: string, keys: string[]): void => {\n if (emitted) {\n warnPostEmit(\n method,\n `Keys dropped: ${keys.length > 0 ? keys.join(', ') : '(empty)'}.`,\n );\n }\n };\n\n return {\n set(fields: Record<string, unknown>) {\n sealCheck('log.set()', Object.keys(fields));\n if (emitted) return;\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n },\n\n setLevel(level: RequestLogLevel) {\n sealCheck('log.setLevel()', ['autotel.log.level']);\n if (emitted) return;\n hasExplicitLevel = true;\n activeContext.setAttribute('autotel.log.level', level);\n },\n\n info(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.info()', keys);\n if (emitted) return;\n addLogEvent('info', message, fields);\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n warn(message: string, fields?: Record<string, unknown>) {\n const keys = fields\n ? ['message', ...Object.keys(fields).filter((k) => k !== 'requestLogs')]\n : ['message'];\n sealCheck('log.warn()', keys);\n if (emitted) return;\n addLogEvent('warn', message, fields);\n if (!hasExplicitLevel) {\n activeContext.setAttribute('autotel.log.level', 'warn');\n }\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n },\n\n error(error: Error | string, fields?: Record<string, unknown>) {\n const keys = fields ? [...Object.keys(fields), 'error'] : ['error'];\n sealCheck('log.error()', keys);\n if (emitted) return;\n const err = typeof error === 'string' ? new Error(error) : error;\n recordStructuredError(activeContext, err);\n addLogEvent('error', err.message, fields);\n\n if (fields) {\n mergeInto(contextState, fields);\n activeContext.setAttributes(flattenToAttributes(fields));\n }\n if (!hasExplicitLevel) {\n activeContext.setAttribute('autotel.log.level', 'error');\n }\n },\n\n getContext() {\n return { ...contextState };\n },\n\n emitNow(overrides?: Record<string, unknown>): RequestLogSnapshot {\n if (emitted) {\n warnPostEmit('log.emitNow()', 'Ignoring duplicate emit.');\n return lastSnapshot as RequestLogSnapshot;\n }\n\n const mergedContext = {\n ...contextState,\n ...overrides,\n };\n const flattened = flattenToAttributes(mergedContext);\n activeContext.setAttributes(flattened);\n\n const snapshot: RequestLogSnapshot = {\n timestamp: new Date().toISOString(),\n traceId: activeContext.traceId,\n spanId: activeContext.spanId,\n correlationId: activeContext.correlationId,\n context: mergedContext,\n };\n\n emitCorrelatedEvent(activeContext, 'log.emit.manual', {\n ...flattened,\n });\n\n if (options?.onEmit) {\n Promise.resolve(options.onEmit(snapshot)).catch((error) => {\n console.warn('[autotel] request logger onEmit failed:', error);\n });\n }\n\n emitted = true;\n lastSnapshot = snapshot;\n return snapshot;\n },\n\n fork(\n label: string,\n fn: () => void | Promise<void>,\n forkOptions?: ForkOptions,\n ): void {\n const parentRequestId = activeContext.correlationId;\n if (typeof parentRequestId !== 'string' || parentRequestId.length === 0) {\n throw new Error(\n '[autotel] log.fork() requires the parent logger to have a correlationId. ' +\n 'Ensure the request was created by autotel middleware.',\n );\n }\n\n const tracer = otelTrace.getTracer('autotel.request-logger');\n const lifecycle = forkOptions?.lifecycle;\n void tracer.startActiveSpan(`request.fork:${label}`, (childSpan) => {\n const childContext: TraceContext = {\n ...createTraceContext(childSpan),\n correlationId: crypto.randomUUID(),\n };\n\n requestContextStore.run(childContext, () => {\n const childLog = getRequestLogger(childContext);\n childLog.set({\n operation: label,\n _parentCorrelationId: parentRequestId,\n });\n\n lifecycle?.onChildEnter?.(childLog);\n\n void Promise.resolve()\n .then(() => fn())\n .then(() => {\n childLog.emitNow();\n })\n .catch((error_: unknown) => {\n const error =\n error_ instanceof Error ? error_ : new Error(String(error_));\n childLog.error(error);\n childLog.emitNow();\n })\n .finally(() => {\n try {\n lifecycle?.onChildExit?.(childLog);\n } catch (hookError) {\n console.warn(\n '[autotel] fork onChildExit hook threw:',\n hookError,\n );\n }\n childSpan.end();\n });\n });\n });\n },\n };\n}\n\n/**\n * Returns `true` when a request-logger context can be resolved without throwing —\n * i.e. an explicit `ctx` was provided, a `runWithRequestContext()` scope is active,\n * or there is an active OpenTelemetry span.\n *\n * Use this to branch on observability availability instead of wrapping\n * {@link getRequestLogger} in try/catch.\n */\nexport function hasRequestContext(ctx?: TraceContext): boolean {\n if (ctx) return true;\n if (requestContextStore.getStore()) return true;\n return otelTrace.getActiveSpan() != null;\n}\n\n/**\n * Like {@link getRequestLogger}, but returns `null` instead of throwing when no\n * request context is available. Intended for best-effort instrumentation where a\n * missing telemetry context must never crash business logic.\n */\nexport function getRequestLoggerSafe(\n ctx?: TraceContext,\n options?: RequestLoggerOptions,\n): RequestLogger | null {\n if (!hasRequestContext(ctx)) return null;\n return getRequestLogger(ctx, options);\n}\n\n/**\n * A no-op {@link RequestLogger} whose methods do nothing. Used as a fallback by\n * best-effort instrumentation so wrapped handlers can run un-instrumented without\n * branching on logger presence.\n */\nexport function createNoopRequestLogger(): RequestLogger {\n const snapshot = (): RequestLogSnapshot => ({\n timestamp: new Date().toISOString(),\n traceId: '',\n spanId: '',\n correlationId: '',\n context: {},\n });\n\n return {\n set() {},\n setLevel() {},\n info() {},\n warn() {},\n error() {},\n getContext() {\n return {};\n },\n emitNow() {\n return snapshot();\n },\n fork(_label, fn) {\n void Promise.resolve().then(() => fn());\n },\n };\n}\n","/**\n * Typed error and audit catalogs.\n *\n * Group related errors into one catalog and get a refactor-safe builder per\n * code, with autocomplete at every call site and typed message parameters.\n *\n * @example\n * ```typescript\n * import { defineErrorCatalog } from 'autotel';\n *\n * export const billing = defineErrorCatalog('billing', {\n * PAYMENT_DECLINED: {\n * status: 402,\n * message: 'Card declined',\n * why: 'The issuer rejected the charge',\n * fix: 'Try a different payment method',\n * },\n * INSUFFICIENT_FUNDS: {\n * status: 402,\n * message: ({ available, required }: { available: number; required: number }) =>\n * `Insufficient funds: $${available} of $${required}`,\n * },\n * });\n *\n * throw billing.PAYMENT_DECLINED({ cause: stripeError });\n * throw billing.INSUFFICIENT_FUNDS({ available: 5, required: 100 });\n *\n * // In a catch block — refactor-safe, no magic strings:\n * if (billing.PAYMENT_DECLINED.match(err)) { ... }\n * ```\n */\n\nimport {\n createStructuredError,\n type StructuredError,\n} from './structured-error';\n\nconst catalogCodeKey = Symbol.for('autotel.catalog.code');\n\n/** Definition of a single error in a catalog. */\nexport interface ErrorCatalogEntry {\n /**\n * Human-readable message. Use a function to interpolate typed parameters;\n * the parameter type flows through to the call site.\n */\n message: string | ((params: never) => string);\n /** HTTP status to surface to clients. */\n status?: number;\n /** Stable error code. Defaults to `${namespace}.${KEY}`. */\n code?: string | number;\n /** Why it happened. A function receives the same params as `message`. */\n why?: string | ((params: never) => string);\n /** What the caller should do next. */\n fix?: string;\n /** Docs or runbook link. */\n link?: string;\n /** Error name. Defaults to the catalog key. */\n name?: string;\n}\n\n/** Per-call options passed alongside (or instead of) typed params. */\nexport interface ErrorBuildOptions {\n cause?: unknown;\n details?: Record<string, unknown>;\n /** Backend-only context. Never serialized to clients. */\n internal?: Record<string, unknown>;\n}\n\ntype ParamsOf<E> = E extends { message: (params: infer P) => string }\n ? P\n : E extends { why: (params: infer P) => string }\n ? P\n : void;\n\ntype BuilderArgs<E extends ErrorCatalogEntry> =\n ParamsOf<E> extends void\n ? [options?: ErrorBuildOptions]\n : [params: ParamsOf<E>, options?: ErrorBuildOptions];\n\n/** A callable error factory produced by {@link defineErrorCatalog}. */\nexport interface ErrorBuilder<E extends ErrorCatalogEntry> {\n (...args: BuilderArgs<E>): StructuredError;\n /** Stable code assigned to every error from this entry. */\n readonly code: string | number;\n /** True when `error` was produced by this catalog entry. */\n match(error: unknown): boolean;\n}\n\nexport type ErrorCatalog<T extends Record<string, ErrorCatalogEntry>> = {\n readonly [K in keyof T]: ErrorBuilder<T[K]>;\n};\n\nfunction readCatalogCode(error: unknown): string | number | undefined {\n if (error === null || typeof error !== 'object') return undefined;\n return (error as Record<symbol, unknown>)[catalogCodeKey] as\n string | number | undefined;\n}\n\n/** True when `error` was produced by any autotel error catalog. */\nexport function isCatalogError(error: unknown): error is StructuredError {\n return readCatalogCode(error) !== undefined;\n}\n\n/** Returns the catalog code of `error`, or `undefined` if it has none. */\nexport function getCatalogCode(error: unknown): string | number | undefined {\n return readCatalogCode(error);\n}\n\n/**\n * Define a typed error catalog. Returns an object whose keys are error\n * builders. Each builder produces a {@link StructuredError} carrying the\n * entry's message, status, code, why, fix, and link.\n */\nexport function defineErrorCatalog<\n const T extends Record<string, ErrorCatalogEntry>,\n>(namespace: string, entries: T): ErrorCatalog<T> {\n const catalog: Record<string, ErrorBuilder<ErrorCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n ErrorCatalogEntry,\n ][]) {\n const code = entry.code ?? `${namespace}.${key}`;\n const usesParams =\n typeof entry.message === 'function' || typeof entry.why === 'function';\n\n const builder = ((\n paramsOrOptions?: unknown,\n maybeOptions?: ErrorBuildOptions,\n ): StructuredError => {\n const params = usesParams ? paramsOrOptions : undefined;\n const options = (usesParams ? maybeOptions : paramsOrOptions) as\n ErrorBuildOptions | undefined;\n\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n const why =\n typeof entry.why === 'function'\n ? (entry.why as (p: unknown) => string)(params)\n : entry.why;\n\n const error = createStructuredError({\n message,\n name: entry.name ?? key,\n code,\n ...(entry.status === undefined ? {} : { status: entry.status }),\n ...(why === undefined ? {} : { why }),\n ...(entry.fix === undefined ? {} : { fix: entry.fix }),\n ...(entry.link === undefined ? {} : { link: entry.link }),\n ...(options?.cause === undefined ? {} : { cause: options.cause }),\n ...(options?.details === undefined ? {} : { details: options.details }),\n ...(options?.internal === undefined\n ? {}\n : { internal: options.internal }),\n });\n\n Object.defineProperty(error, catalogCodeKey, {\n value: code,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n return error;\n }) as ErrorBuilder<ErrorCatalogEntry>;\n\n Object.defineProperty(builder, 'code', {\n value: code,\n enumerable: true,\n });\n Object.defineProperty(builder, 'match', {\n value: (error: unknown): boolean => readCatalogCode(error) === code,\n enumerable: false,\n });\n\n catalog[key] = builder;\n }\n\n return Object.freeze(catalog) as ErrorCatalog<T>;\n}\n\n/** Severity of an audit action. */\nexport type AuditSeverity = 'info' | 'warn' | 'critical';\n\n/** Definition of a single action in an audit catalog. */\nexport interface AuditCatalogEntry {\n /** Human-readable description. Use a function for typed params. */\n message?: string | ((params: never) => string);\n /** Stable action name. Defaults to `${namespace}.${KEY}`. */\n action?: string;\n /** Severity of the action. Defaults to `'info'`. */\n severity?: AuditSeverity;\n}\n\n/** A resolved audit action descriptor produced by an audit catalog. */\nexport interface AuditAction {\n readonly action: string;\n readonly severity: AuditSeverity;\n readonly message?: string;\n}\n\ntype AuditDescriptorArgs<E extends AuditCatalogEntry> =\n ParamsOf<E> extends void ? [] : [params: ParamsOf<E>];\n\n/** A callable audit-action descriptor produced by {@link defineAuditCatalog}. */\nexport interface AuditDescriptor<E extends AuditCatalogEntry> {\n (...args: AuditDescriptorArgs<E>): AuditAction;\n readonly action: string;\n readonly severity: AuditSeverity;\n}\n\nexport type AuditCatalog<T extends Record<string, AuditCatalogEntry>> = {\n readonly [K in keyof T]: AuditDescriptor<T[K]>;\n};\n\n/**\n * Define a typed audit catalog. Returns typed action descriptors you can pass\n * to `track()` or audit helpers without scattering magic strings.\n */\nexport function defineAuditCatalog<\n const T extends Record<string, AuditCatalogEntry>,\n>(namespace: string, entries: T): AuditCatalog<T> {\n const catalog: Record<string, AuditDescriptor<AuditCatalogEntry>> = {};\n\n for (const [key, entry] of Object.entries(entries) as [\n string,\n AuditCatalogEntry,\n ][]) {\n const action = entry.action ?? `${namespace}.${key}`;\n const severity: AuditSeverity = entry.severity ?? 'info';\n\n const descriptor = ((params?: unknown): AuditAction => {\n const message =\n typeof entry.message === 'function'\n ? (entry.message as (p: unknown) => string)(params)\n : entry.message;\n return Object.freeze({\n action,\n severity,\n ...(message === undefined ? {} : { message }),\n });\n }) as AuditDescriptor<AuditCatalogEntry>;\n\n Object.defineProperty(descriptor, 'action', {\n value: action,\n enumerable: true,\n });\n Object.defineProperty(descriptor, 'severity', {\n value: severity,\n enumerable: true,\n });\n\n catalog[key] = descriptor;\n }\n\n return Object.freeze(catalog) as AuditCatalog<T>;\n}\n","export interface DrainOptions<TContext, TConfig, TPayload = TContext> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Return null to skip draining (e.g. missing API key in dev). */\n resolve: () => TConfig | null | Promise<TConfig | null>;\n /** Transform contexts into payloads. Defaults to identity. */\n transform?: (contexts: TContext[]) => TPayload[];\n /** Transport implementation. */\n send: (payloads: TPayload[], config: TConfig) => Promise<void>;\n}\n\nexport interface HttpDrainRequest {\n url: string;\n headers: Record<string, string>;\n body: string;\n}\n\nexport interface HttpDrainOptions<\n TContext,\n TConfig,\n TPayload = TContext,\n> extends Omit<DrainOptions<TContext, TConfig, TPayload>, 'send'> {\n encode: (payloads: TPayload[], config: TConfig) => HttpDrainRequest | null;\n timeoutMs?: number;\n retries?: number;\n resolveTimeoutMs?: (config: TConfig) => number | undefined;\n resolveRetries?: (config: TConfig) => number | undefined;\n}\n\nconst DEFAULT_TIMEOUT_MS = 5000;\nconst DEFAULT_RETRIES = 2;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n t.unref?.();\n });\n}\n\nasync function postWithRetry(options: {\n name: string;\n request: HttpDrainRequest;\n timeoutMs: number;\n retries: number;\n}): Promise<void> {\n const { name, request, timeoutMs, retries } = options;\n const attempts = Math.max(1, retries);\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n timeout.unref?.();\n try {\n const response = await fetch(request.url, {\n method: 'POST',\n headers: request.headers,\n body: request.body,\n signal: controller.signal,\n });\n if (!response.ok) {\n throw new Error(\n `[autotel/${name}] HTTP ${response.status} draining ${request.url}`,\n );\n }\n return;\n } catch (error) {\n lastError = error;\n if (attempt < attempts) {\n await delay(100 * attempt);\n }\n } finally {\n clearTimeout(timeout);\n }\n }\n\n throw lastError;\n}\n\nexport function defineDrain<TContext, TConfig, TPayload = TContext>(\n options: DrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return async (ctx: TContext | TContext[]) => {\n const contexts = Array.isArray(ctx) ? ctx : [ctx];\n if (contexts.length === 0) return;\n\n const config = await options.resolve();\n if (!config) return;\n\n const payloads = options.transform\n ? options.transform(contexts)\n : (contexts as unknown as TPayload[]);\n\n if (payloads.length === 0) return;\n\n try {\n await options.send(payloads, config);\n } catch (error) {\n console.error(`[autotel/${options.name}] drain failed:`, error);\n }\n };\n}\n\nexport function defineHttpDrain<TContext, TConfig, TPayload = TContext>(\n options: HttpDrainOptions<TContext, TConfig, TPayload>,\n): (ctx: TContext | TContext[]) => Promise<void> {\n return defineDrain<TContext, TConfig, TPayload>({\n name: options.name,\n resolve: options.resolve,\n transform: options.transform,\n send: async (payloads, config) => {\n const request = options.encode(payloads, config);\n if (!request) return;\n const timeoutMs =\n options.resolveTimeoutMs?.(config) ??\n options.timeoutMs ??\n DEFAULT_TIMEOUT_MS;\n const retries =\n options.resolveRetries?.(config) ?? options.retries ?? DEFAULT_RETRIES;\n\n await postWithRetry({\n name: options.name,\n request,\n timeoutMs,\n retries,\n });\n },\n });\n}\n","export interface EnrichContext<TEvent extends Record<string, unknown>> {\n event: TEvent;\n request?: {\n method?: string;\n path?: string;\n requestId?: string;\n };\n response?: {\n status?: number;\n };\n headers?: Record<string, string>;\n}\n\nexport interface EnricherDefinition<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n> {\n /** Stable identifier used in error logs. */\n name: string;\n /** Top-level field to merge computed values into. */\n field: keyof TEvent & string;\n /** Return undefined to skip enrichment. */\n compute: (ctx: EnrichContext<TEvent>) => TValue | undefined;\n}\n\nexport interface EnricherOptions {\n /** Replace existing field value instead of merge. Default false. */\n overwrite?: boolean;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction mergeInto(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): void {\n for (const key in source) {\n const sourceVal = source[key];\n if (sourceVal === undefined) continue;\n const targetVal = target[key];\n if (isPlainObject(sourceVal) && isPlainObject(targetVal)) {\n mergeInto(targetVal, sourceVal);\n } else {\n target[key] = sourceVal;\n }\n }\n}\n\nexport function defineEnricher<\n TEvent extends Record<string, unknown>,\n TValue extends object,\n>(\n def: EnricherDefinition<TEvent, TValue>,\n options: EnricherOptions = {},\n): (ctx: EnrichContext<TEvent>) => void {\n return (ctx: EnrichContext<TEvent>) => {\n let computed: TValue | undefined;\n try {\n computed = def.compute(ctx);\n } catch (error) {\n console.error(`[autotel/${def.name}] enrich failed:`, error);\n return;\n }\n\n if (!computed) return;\n\n if (options.overwrite || !isPlainObject(ctx.event[def.field])) {\n (ctx.event as Record<string, unknown>)[def.field] = computed;\n return;\n }\n\n mergeInto(\n ctx.event[def.field] as unknown as Record<string, unknown>,\n computed as unknown as Record<string, unknown>,\n );\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "Write Once, Observe Anywhere",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",