diff --git a/src/lib/telemetry.ts b/src/lib/telemetry.ts new file mode 100644 index 0000000..0a23757 --- /dev/null +++ b/src/lib/telemetry.ts @@ -0,0 +1,40 @@ +import { TelemetryEvent, TelemetrySink, ErrorContext } from './telemetry.types'; + +class ConsoleSink implements TelemetrySink { + capture(event: TelemetryEvent): void { + if (process.env.NODE_ENV !== 'production') { + console.warn('[Telemetry]', event.context.source, event.context.operation, event.message); + } + } +} + +let activeSink: TelemetrySink = new ConsoleSink(); +let telemetryEnabled = true; + +export function registerSink(sink: TelemetrySink): void { + activeSink = sink; +} + +export function setTelemetryEnabled(enabled: boolean): void { + telemetryEnabled = enabled; +} + +export function reportError(error: unknown, context: ErrorContext): void { + if (!telemetryEnabled || context.userRejected) { + return; + } + + try { + const errObj = error instanceof Error ? error : new Error(String(error)); + const event: TelemetryEvent = { + name: errObj.name, + message: errObj.message, + stack: errObj.stack, + context, + timestamp: Date.now() + }; + activeSink.capture(event); + } catch (sinkErr) { + console.error('Telemetry dispatch failed:', sinkErr); + } +} \ No newline at end of file diff --git a/src/lib/telemetry.types.ts b/src/lib/telemetry.types.ts new file mode 100644 index 0000000..eaca327 --- /dev/null +++ b/src/lib/telemetry.types.ts @@ -0,0 +1,23 @@ +export type ErrorSource = 'transaction' | 'auth' | 'mutation' | 'render' | 'global'; + +export interface ErrorContext { + source: ErrorSource; + operation?: string; + statusCode?: number; + code?: string; + userRejected?: boolean; + metadata?: Record; +} + +export interface TelemetryEvent { + name: string; + message: string; + stack?: string; + context: ErrorContext; + timestamp: number; +} + +export interface TelemetrySink { + capture(event: TelemetryEvent): void; + flush?(): Promise; +} \ No newline at end of file