@omote/core 0.9.7 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +77 -35
  2. package/dist/{chunk-X5OTUOE6.mjs → chunk-3FILA2CD.mjs} +63 -205
  3. package/dist/chunk-3FILA2CD.mjs.map +1 -0
  4. package/dist/{chunk-CYBTTLG7.mjs → chunk-5WIOGMJA.mjs} +77 -219
  5. package/dist/chunk-5WIOGMJA.mjs.map +1 -0
  6. package/dist/{chunk-3NDJA3I4.mjs → chunk-NWZMIQK4.mjs} +135 -206
  7. package/dist/chunk-NWZMIQK4.mjs.map +1 -0
  8. package/dist/{chunk-Y3DTP5P3.mjs → chunk-VSYYT4HO.mjs} +1 -1
  9. package/dist/{chunk-X5OTUOE6.mjs.map → chunk-VSYYT4HO.mjs.map} +1 -1
  10. package/dist/chunk-WW4XAUJ3.mjs +208 -0
  11. package/dist/chunk-WW4XAUJ3.mjs.map +1 -0
  12. package/dist/index.d.mts +336 -1375
  13. package/dist/index.d.ts +336 -1375
  14. package/dist/index.js +6738 -11284
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +6099 -10719
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/logging/index.js +5 -0
  19. package/dist/logging/index.js.map +1 -1
  20. package/dist/logging/index.mjs +1 -1
  21. package/dist/otlp-2BML6FIK.mjs +7 -0
  22. package/dist/otlp-2BML6FIK.mjs.map +1 -0
  23. package/package.json +1 -2
  24. package/dist/Logger-BeUI6jG7.d.mts +0 -145
  25. package/dist/Logger-BeUI6jG7.d.ts +0 -145
  26. package/dist/Logger-DSoGAYJu.d.mts +0 -141
  27. package/dist/Logger-DSoGAYJu.d.ts +0 -141
  28. package/dist/chunk-3NDJA3I4.mjs.map +0 -1
  29. package/dist/chunk-CYBTTLG7.mjs.map +0 -1
  30. package/dist/chunk-ESU52TDS.mjs +0 -287
  31. package/dist/chunk-ESU52TDS.mjs.map +0 -1
  32. package/dist/chunk-MXKJOF4I.mjs +0 -38
  33. package/dist/chunk-MXKJOF4I.mjs.map +0 -1
  34. package/dist/chunk-XK22BRG4.mjs +0 -38
  35. package/dist/chunk-XK22BRG4.mjs.map +0 -1
  36. package/dist/chunk-Y3DTP5P3.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logging/types.ts","../src/logging/formatters.ts","../src/telemetry/exporters/console.ts","../src/logging/Clock.ts","../src/telemetry/OmoteTelemetry.ts","../src/logging/Logger.ts","../src/logging/ErrorCodes.ts"],"sourcesContent":["/**\n * Logging types for Omote SDK\n *\n * 6-level logging system with structured output:\n * - error: Critical failures that prevent operation\n * - warn: Recoverable issues or degraded performance\n * - info: Key lifecycle events (model loaded, inference complete)\n * - debug: Detailed operational info for development\n * - trace: Fine-grained tracing for performance analysis\n * - verbose: Extremely detailed output (tensor shapes, intermediate values)\n */\n\nexport type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'verbose';\n\n/**\n * Numeric priority for log levels (lower = more severe)\n */\nexport const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n error: 0,\n warn: 1,\n info: 2,\n debug: 3,\n trace: 4,\n verbose: 5,\n};\n\n/**\n * Structured log entry\n */\nexport interface LogEntry {\n /** Unix timestamp in milliseconds */\n timestamp: number;\n /** Log level */\n level: LogLevel;\n /** Module name (e.g., 'LocalInference', 'ModelCache') */\n module: string;\n /** Human-readable message */\n message: string;\n /** Optional structured error code from ErrorCodes (e.g., 'OMOTE_INF_001') */\n code?: string;\n /** Optional structured data */\n data?: Record<string, unknown>;\n /** Optional error object */\n error?: Error;\n /** Trace ID from active telemetry span (log-to-span correlation) */\n traceId?: string;\n /** Span ID from active telemetry span (log-to-span correlation) */\n spanId?: string;\n}\n\n/**\n * Log output sink interface.\n *\n * **Portability contract**: Replace the default console sink to route logs to\n * any backend — Node.js winston/pino, Unreal UE_LOG, Unity Debug.Log, or a\n * remote telemetry service. Register via `configureLogging({ sink: mySink })`.\n */\nexport interface LogSink {\n (entry: LogEntry): void;\n}\n\n/**\n * Log formatter interface\n */\nexport interface LogFormatter {\n (entry: LogEntry): string;\n}\n\n/**\n * Global logging configuration\n */\nexport interface LoggingConfig {\n /** Minimum log level to output (default: 'info') */\n level: LogLevel;\n /** Enable/disable logging globally (default: true) */\n enabled: boolean;\n /** Output format: 'json' for structured, 'pretty' for human-readable */\n format: 'json' | 'pretty';\n /** Custom output sink (default: console) */\n sink?: LogSink;\n /** Include timestamps in output (default: true) */\n timestamps?: boolean;\n /** Include module name in output (default: true) */\n includeModule?: boolean;\n}\n\n/**\n * Logger interface for module-specific logging.\n *\n * **Portability contract**: All SDK modules depend only on this interface.\n * To integrate with a non-browser runtime (Node.js, Unreal, Unity), either:\n * 1. Use the built-in Logger with a custom `LogSink`, or\n * 2. Provide your own `ILogger` implementation that bridges to the host\n * runtime's logging system (e.g., `console.log`, `UE_LOG`, `Debug.Log`).\n */\nexport interface ILogger {\n error(message: string, data?: Record<string, unknown>): void;\n warn(message: string, data?: Record<string, unknown>): void;\n info(message: string, data?: Record<string, unknown>): void;\n debug(message: string, data?: Record<string, unknown>): void;\n trace(message: string, data?: Record<string, unknown>): void;\n verbose(message: string, data?: Record<string, unknown>): void;\n\n /** Create a child logger with a sub-module name */\n child(subModule: string): ILogger;\n\n /** Get the module name for this logger */\n readonly module: string;\n}\n\n/**\n * Default configuration\n */\nexport const DEFAULT_LOGGING_CONFIG: LoggingConfig = {\n level: 'info',\n enabled: true,\n format: 'pretty',\n timestamps: true,\n includeModule: true,\n};\n","/**\n * Log formatters for different output formats\n */\n\nimport type { LogEntry, LogFormatter, LogLevel } from './types';\n\n/**\n * ANSI color codes for terminal output\n */\nconst COLORS = {\n reset: '\\x1b[0m',\n red: '\\x1b[31m',\n yellow: '\\x1b[33m',\n blue: '\\x1b[34m',\n cyan: '\\x1b[36m',\n gray: '\\x1b[90m',\n white: '\\x1b[37m',\n magenta: '\\x1b[35m',\n};\n\n/**\n * Level-specific colors\n */\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n error: COLORS.red,\n warn: COLORS.yellow,\n info: COLORS.blue,\n debug: COLORS.cyan,\n trace: COLORS.magenta,\n verbose: COLORS.gray,\n};\n\n/**\n * Level display names (padded for alignment)\n */\nconst LEVEL_NAMES: Record<LogLevel, string> = {\n error: 'ERROR ',\n warn: 'WARN ',\n info: 'INFO ',\n debug: 'DEBUG ',\n trace: 'TRACE ',\n verbose: 'VERBOSE',\n};\n\n/**\n * Check if we're in a browser environment\n */\nconst isBrowser = typeof window !== 'undefined';\n\n/**\n * Format timestamp as ISO string or relative time\n */\nfunction formatTimestamp(timestamp: number): string {\n const date = new Date(timestamp);\n return date.toISOString().substring(11, 23); // HH:mm:ss.SSS\n}\n\n/**\n * Safely serialize data to JSON, handling circular references\n */\nfunction safeStringify(data: unknown): string {\n const seen = new WeakSet();\n return JSON.stringify(data, (key, value) => {\n if (typeof value === 'object' && value !== null) {\n if (seen.has(value)) {\n return '[Circular]';\n }\n seen.add(value);\n }\n // Handle special types\n if (value instanceof Error) {\n return {\n name: value.name,\n message: value.message,\n stack: value.stack,\n };\n }\n if (value instanceof Float32Array || value instanceof Int16Array) {\n return `${value.constructor.name}(${value.length})`;\n }\n if (ArrayBuffer.isView(value)) {\n return `${value.constructor.name}(${value.byteLength})`;\n }\n return value;\n });\n}\n\n/**\n * JSON formatter - structured output for machine parsing\n */\nexport const jsonFormatter: LogFormatter = (entry: LogEntry): string => {\n const output: Record<string, unknown> = {\n timestamp: entry.timestamp,\n level: entry.level,\n module: entry.module,\n message: entry.message,\n };\n\n if (entry.data && Object.keys(entry.data).length > 0) {\n output.data = entry.data;\n }\n\n if (entry.traceId) output.traceId = entry.traceId;\n if (entry.spanId) output.spanId = entry.spanId;\n\n if (entry.error) {\n output.error = {\n name: entry.error.name,\n message: entry.error.message,\n stack: entry.error.stack,\n };\n }\n\n return safeStringify(output);\n};\n\n/**\n * Pretty formatter - human-readable output with colors\n */\nexport const prettyFormatter: LogFormatter = (entry: LogEntry): string => {\n const time = formatTimestamp(entry.timestamp);\n const level = LEVEL_NAMES[entry.level];\n const module = entry.module;\n const message = entry.message;\n\n // Build the base message\n let output: string;\n\n if (isBrowser) {\n // Browser: no ANSI colors, use plain text\n output = `${time} ${level} [${module}] ${message}`;\n } else {\n // Terminal: use ANSI colors\n const color = LEVEL_COLORS[entry.level];\n output = `${COLORS.gray}${time}${COLORS.reset} ${color}${level}${COLORS.reset} ${COLORS.cyan}[${module}]${COLORS.reset} ${message}`;\n }\n\n // Append trace correlation if present\n if (entry.traceId) {\n output += ` [trace:${entry.traceId.slice(0, 8)}]`;\n }\n\n // Append data if present\n if (entry.data && Object.keys(entry.data).length > 0) {\n const dataStr = safeStringify(entry.data);\n // For pretty format, indent multi-line data\n if (dataStr.length > 80) {\n output += '\\n ' + JSON.stringify(entry.data, null, 2).replace(/\\n/g, '\\n ');\n } else {\n output += ' ' + dataStr;\n }\n }\n\n // Append error if present\n if (entry.error) {\n output += `\\n ${entry.error.name}: ${entry.error.message}`;\n if (entry.error.stack) {\n const stackLines = entry.error.stack.split('\\n').slice(1, 4);\n output += '\\n ' + stackLines.join('\\n ');\n }\n }\n\n return output;\n};\n\n/**\n * Get formatter by name\n */\nexport function getFormatter(format: 'json' | 'pretty'): LogFormatter {\n return format === 'json' ? jsonFormatter : prettyFormatter;\n}\n\n/**\n * Create browser console arguments for styled output\n * Returns [formatString, ...styleArgs] for console.log\n */\nexport function createBrowserConsoleArgs(entry: LogEntry): [string, ...string[]] {\n const time = formatTimestamp(entry.timestamp);\n const level = entry.level.toUpperCase().padEnd(7);\n const module = entry.module;\n const message = entry.message;\n\n // CSS styles for browser console\n const styles = {\n time: 'color: gray;',\n error: 'color: red; font-weight: bold;',\n warn: 'color: orange; font-weight: bold;',\n info: 'color: blue;',\n debug: 'color: cyan;',\n trace: 'color: magenta;',\n verbose: 'color: gray;',\n module: 'color: teal; font-weight: bold;',\n message: 'color: inherit;',\n };\n\n let formatStr = '%c%s %c%s %c[%s]%c %s';\n const args: string[] = [\n styles.time,\n time,\n styles[entry.level],\n level,\n styles.module,\n module,\n styles.message,\n message,\n ];\n\n // Append data\n if (entry.data && Object.keys(entry.data).length > 0) {\n formatStr += ' %o';\n args.push(entry.data as unknown as string);\n }\n\n return [formatStr, ...args];\n}\n","/**\n * Console Exporter\n *\n * Exports telemetry data to the browser console for development/debugging.\n *\n * @category Telemetry\n */\n\nimport type { SpanAttributes } from '../types';\n\n/**\n * Span data structure for export\n */\nexport interface SpanData {\n name: string;\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n startTime: number;\n endTime: number;\n durationMs: number;\n /** Epoch timestamp in ms for OTLP export (start) */\n epochMs: number;\n /** Epoch timestamp in ms for OTLP export (end) */\n endEpochMs: number;\n status: 'ok' | 'error';\n attributes: SpanAttributes;\n error?: Error;\n}\n\n/**\n * Metric data structure for export\n */\nexport interface MetricData {\n name: string;\n type: 'counter' | 'histogram';\n value: number;\n attributes: Record<string, string | number | boolean>;\n timestamp: number;\n /** Histogram bucket data for OTLP export */\n histogramData?: {\n count: number;\n sum: number;\n min: number;\n max: number;\n bucketBoundaries: number[];\n bucketCounts: number[];\n };\n}\n\n/**\n * Exporter interface that all exporters must implement\n */\nexport interface TelemetryExporterInterface {\n /** Export a completed span */\n exportSpan(span: SpanData): void;\n /** Export a metric */\n exportMetric(metric: MetricData): void;\n /** Flush any buffered data */\n flush(): Promise<void>;\n /** Shutdown the exporter */\n shutdown(): Promise<void>;\n}\n\n/**\n * Console exporter for development/debugging\n *\n * Outputs spans and metrics to the browser console with formatting.\n */\nexport class ConsoleExporter implements TelemetryExporterInterface {\n private enabled: boolean;\n private prefix: string;\n\n constructor(options: { enabled?: boolean; prefix?: string } = {}) {\n this.enabled = options.enabled ?? true;\n this.prefix = options.prefix ?? '[Omote Telemetry]';\n }\n\n exportSpan(span: SpanData): void {\n if (!this.enabled) return;\n\n const statusIcon = span.status === 'ok' ? '✓' : '✗';\n const statusColor = span.status === 'ok' ? 'color: green' : 'color: red';\n\n console.groupCollapsed(\n `%c${this.prefix} %c${statusIcon} ${span.name} %c(${span.durationMs.toFixed(2)}ms)`,\n 'color: gray',\n statusColor,\n 'color: gray'\n );\n\n console.log('Trace ID:', span.traceId);\n console.log('Span ID:', span.spanId);\n if (span.parentSpanId) {\n console.log('Parent Span ID:', span.parentSpanId);\n }\n console.log('Duration:', `${span.durationMs.toFixed(2)}ms`);\n console.log('Status:', span.status);\n\n if (Object.keys(span.attributes).length > 0) {\n console.log('Attributes:', span.attributes);\n }\n\n if (span.error) {\n console.error('Error:', span.error);\n }\n\n console.groupEnd();\n }\n\n exportMetric(metric: MetricData): void {\n if (!this.enabled) return;\n\n const typeIcon = metric.type === 'counter' ? '↑' : '📊';\n\n console.log(\n `%c${this.prefix} %c${typeIcon} ${metric.name}: %c${metric.value}`,\n 'color: gray',\n 'color: blue',\n 'color: black; font-weight: bold',\n metric.attributes\n );\n }\n\n async flush(): Promise<void> {\n // Console exporter doesn't buffer, nothing to flush\n }\n\n async shutdown(): Promise<void> {\n this.enabled = false;\n }\n}\n","/**\n * Abstract monotonic clock for runtime-agnostic timing.\n *\n * **Portability contract**: To port the SDK to Node.js, Python, Unreal, or Unity,\n * implement this interface with your runtime's high-resolution timer and call\n * `configureClock()` at startup.\n *\n * @example Node.js\n * ```typescript\n * import { configureClock } from '@omote/core';\n * configureClock({\n * now: () => Number(process.hrtime.bigint() / 1_000_000n),\n * timestamp: () => Date.now(),\n * });\n * ```\n *\n * @example Unreal Engine (via wasm-bindgen)\n * ```typescript\n * configureClock({\n * now: () => FPlatformTime.Seconds() * 1000,\n * timestamp: () => FDateTime.UtcNow().ToUnixTimestamp() * 1000,\n * });\n * ```\n *\n * @example Unity (via jslib bridge)\n * ```typescript\n * configureClock({\n * now: () => Time.realtimeSinceStartup * 1000,\n * timestamp: () => Date.now(),\n * });\n * ```\n */\nexport interface Clock {\n /** Monotonic high-resolution milliseconds (for durations, telemetry spans) */\n now(): number;\n /** Wall-clock milliseconds since epoch (for log timestamps) */\n timestamp(): number;\n}\n\nexport const defaultClock: Clock = {\n now: () => performance.now(),\n timestamp: () => Date.now(),\n};\n\nlet activeClock: Clock = defaultClock;\n\n/** Replace the default browser clock with a custom implementation. */\nexport function configureClock(clock: Clock): void {\n activeClock = clock;\n}\n\n/** Get the active clock instance. */\nexport function getClock(): Clock {\n return activeClock;\n}\n","/**\n * Muse Telemetry\n *\n * Main orchestrator for SDK telemetry. Manages spans, metrics, and exporters.\n *\n * @category Telemetry\n */\n\nimport type { TelemetryConfig, SpanAttributes, SamplingConfig } from './types';\nimport type { SpanData, MetricData, TelemetryExporterInterface } from './exporters/console';\nimport { ConsoleExporter } from './exporters/console';\nimport { getClock } from '../logging/Clock';\n\n/** Default histogram bucket boundaries (union of inference + model load buckets) */\nconst DEFAULT_HISTOGRAM_BUCKETS = [1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, 60000];\n\n/**\n * Generate a random hex ID\n */\nfunction generateId(length: number = 16): string {\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Span context for tracing\n */\ninterface SpanContext {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n}\n\n/**\n * Active span handle returned by startSpan\n */\nexport interface ActiveSpan {\n /** End the span with success status */\n end(): void;\n /** End the span with error status */\n endWithError(error: Error): void;\n /** Add attributes to the span */\n setAttributes(attrs: Partial<SpanAttributes>): void;\n /** Get the span context */\n getContext(): SpanContext;\n}\n\n/**\n * Global telemetry instance\n */\nlet globalTelemetry: OmoteTelemetry | null = null;\n\n/**\n * Configure global telemetry\n *\n * @example\n * ```typescript\n * // Development\n * configureTelemetry({\n * enabled: true,\n * serviceName: 'omote-dev',\n * exporter: 'console',\n * });\n *\n * // Production\n * configureTelemetry({\n * enabled: true,\n * serviceName: 'omote-prod',\n * exporter: 'otlp',\n * exporterConfig: {\n * endpoint: 'https://tempo.example.com',\n * },\n * sampling: { ratio: 0.1 },\n * });\n * ```\n */\nexport function configureTelemetry(config: TelemetryConfig): OmoteTelemetry {\n if (globalTelemetry) {\n globalTelemetry.shutdown();\n }\n globalTelemetry = new OmoteTelemetry(config);\n return globalTelemetry;\n}\n\n/**\n * Get the global telemetry instance\n */\nexport function getTelemetry(): OmoteTelemetry | null {\n return globalTelemetry;\n}\n\n/**\n * Main telemetry class\n *\n * Manages spans, metrics, and exports to configured backends.\n */\nexport class OmoteTelemetry {\n private config: Required<Omit<TelemetryConfig, 'exporterConfig' | 'customExporter'>> & { exporterConfig?: TelemetryConfig['exporterConfig'] };\n private exporter: TelemetryExporterInterface | null = null;\n private exporterReady: Promise<void> | null = null;\n private activeTraceId: string | null = null;\n private metricsIntervalId: ReturnType<typeof setInterval> | null = null;\n\n // Span stack for log-to-span correlation\n private spanStack: Array<{ traceId: string; spanId: string }> = [];\n\n // Metric accumulators\n private counters: Map<string, { value: number; attributes: Record<string, string | number | boolean> }> = new Map();\n private histograms: Map<string, { count: number; sum: number; min: number; max: number; bucketBoundaries: number[]; bucketCounts: number[]; attributes: Record<string, string | number | boolean> }> = new Map();\n\n constructor(config: TelemetryConfig) {\n this.config = {\n enabled: config.enabled ?? false,\n serviceName: config.serviceName ?? 'omote-sdk',\n serviceVersion: config.serviceVersion ?? '0.1.0',\n exporter: config.exporter ?? 'none',\n exporterConfig: config.exporterConfig,\n sampling: config.sampling ?? { ratio: 1.0, alwaysSampleErrors: true },\n metricsEnabled: config.metricsEnabled ?? true,\n metricsIntervalMs: config.metricsIntervalMs ?? 60000,\n };\n\n if (this.config.enabled) {\n // Custom exporter: set synchronously (no async import needed)\n if ((config as TelemetryConfig).customExporter) {\n this.exporter = (config as TelemetryConfig).customExporter!;\n } else {\n this.exporterReady = this.initExporter();\n }\n this.startMetricsCollection();\n }\n }\n\n /**\n * Initialize the configured exporter\n */\n private async initExporter(): Promise<void> {\n // Custom exporter takes priority\n if ((this.config as TelemetryConfig).customExporter) {\n this.exporter = (this.config as TelemetryConfig).customExporter!;\n return;\n }\n\n switch (this.config.exporter) {\n case 'console':\n this.exporter = new ConsoleExporter({ enabled: true });\n break;\n case 'otlp':\n if (!this.config.exporterConfig) {\n console.warn('[Telemetry] OTLP exporter requires exporterConfig with endpoint');\n return;\n }\n {\n const { OTLPExporter } = await import('./exporters/otlp');\n this.exporter = new OTLPExporter(\n this.config.exporterConfig,\n this.config.serviceName,\n this.config.serviceVersion\n );\n }\n break;\n case 'none':\n default:\n this.exporter = null;\n }\n }\n\n /**\n * Start periodic metrics collection\n */\n private startMetricsCollection(): void {\n if (!this.config.metricsEnabled || !this.exporter) return;\n\n this.metricsIntervalId = setInterval(() => {\n this.flushMetrics();\n }, this.config.metricsIntervalMs);\n }\n\n /**\n * Check if this operation should be sampled\n */\n private shouldSample(isError: boolean = false): boolean {\n if (!this.config.enabled) return false;\n\n const sampling = this.config.sampling as SamplingConfig;\n if (isError && sampling.alwaysSampleErrors) return true;\n\n const ratio = sampling.ratio ?? 1.0;\n return Math.random() < ratio;\n }\n\n /**\n * Start a new span\n *\n * @example\n * ```typescript\n * const span = telemetry.startSpan('Wav2Vec2.infer', {\n * 'inference.input_samples': samples.length,\n * 'model.backend': 'webgpu',\n * });\n *\n * try {\n * const result = await doInference();\n * span.setAttributes({ 'inference.output_frames': result.frames });\n * span.end();\n * } catch (error) {\n * span.endWithError(error);\n * }\n * ```\n */\n startSpan(name: string, attributes: Partial<SpanAttributes> = {}, parentContext?: SpanContext): ActiveSpan {\n const traceId = parentContext?.traceId ?? this.activeTraceId ?? generateId(16);\n const spanId = generateId(8);\n const parentSpanId = parentContext?.spanId;\n const startTime = getClock().now();\n const epochMs = Date.now();\n\n // Set active trace if this is a root span\n if (!parentContext && !this.activeTraceId) {\n this.activeTraceId = traceId;\n }\n\n let spanAttributes = { ...attributes };\n let ended = false;\n let sampled = this.shouldSample();\n\n const context: SpanContext = { traceId, spanId, parentSpanId };\n\n // Push to span stack for log-to-span correlation\n this.spanStack.push({ traceId, spanId });\n\n const endSpan = (status: 'ok' | 'error', error?: Error): void => {\n if (ended) return;\n ended = true;\n\n // Pop from span stack\n const idx = this.spanStack.findIndex(s => s.spanId === spanId);\n if (idx !== -1) this.spanStack.splice(idx, 1);\n\n const endTime = getClock().now();\n const durationMs = endTime - startTime;\n const endEpochMs = epochMs + (endTime - startTime);\n\n // Re-check sampling for errors\n if (status === 'error' && !sampled) {\n sampled = this.shouldSample(true);\n }\n\n if (!sampled || !this.exporter) return;\n\n const spanData: SpanData = {\n name,\n traceId,\n spanId,\n parentSpanId,\n startTime,\n endTime,\n durationMs,\n epochMs,\n endEpochMs,\n status,\n attributes: spanAttributes as SpanAttributes,\n error,\n };\n\n this.exporter.exportSpan(spanData);\n\n // Clear active trace if this was the root span\n if (!parentSpanId && this.activeTraceId === traceId) {\n this.activeTraceId = null;\n }\n };\n\n return {\n end: () => endSpan('ok'),\n endWithError: (error: Error) => endSpan('error', error),\n setAttributes: (attrs: Partial<SpanAttributes>) => {\n spanAttributes = { ...spanAttributes, ...attrs };\n },\n getContext: () => context,\n };\n }\n\n /**\n * Wrap an async function with a span\n *\n * @example\n * ```typescript\n * const result = await telemetry.withSpan('Model.load', async (span) => {\n * const model = await loadModel();\n * span.setAttributes({ 'model.size_bytes': model.size });\n * return model;\n * });\n * ```\n */\n async withSpan<T>(\n name: string,\n fn: (span: ActiveSpan) => Promise<T>,\n attributes: Partial<SpanAttributes> = {},\n parentContext?: SpanContext\n ): Promise<T> {\n const span = this.startSpan(name, attributes, parentContext);\n\n try {\n const result = await fn(span);\n span.end();\n return result;\n } catch (error) {\n span.endWithError(error as Error);\n throw error;\n }\n }\n\n /**\n * Increment a counter metric\n *\n * @example\n * ```typescript\n * telemetry.incrementCounter('omote.inference.total', 1, {\n * model: 'wav2vec2',\n * backend: 'webgpu',\n * status: 'success',\n * });\n * ```\n */\n incrementCounter(\n name: string,\n value: number = 1,\n attributes: Record<string, string | number | boolean> = {}\n ): void {\n if (!this.config.enabled || !this.config.metricsEnabled) return;\n\n const key = this.getMetricKey(name, attributes);\n const existing = this.counters.get(key);\n\n if (existing) {\n existing.value += value;\n } else {\n this.counters.set(key, { value, attributes });\n }\n }\n\n /**\n * Record a histogram value\n *\n * @example\n * ```typescript\n * telemetry.recordHistogram('omote.inference.latency', durationMs, {\n * model: 'wav2vec2',\n * backend: 'webgpu',\n * });\n * ```\n */\n recordHistogram(\n name: string,\n value: number,\n attributes: Record<string, string | number | boolean> = {},\n bucketBoundaries: number[] = DEFAULT_HISTOGRAM_BUCKETS\n ): void {\n if (!this.config.enabled || !this.config.metricsEnabled) return;\n\n const key = this.getMetricKey(name, attributes);\n const existing = this.histograms.get(key);\n\n if (existing) {\n existing.count++;\n existing.sum += value;\n if (value < existing.min) existing.min = value;\n if (value > existing.max) existing.max = value;\n // Increment bucket count\n let placed = false;\n for (let i = 0; i < existing.bucketBoundaries.length; i++) {\n if (value <= existing.bucketBoundaries[i]) {\n existing.bucketCounts[i]++;\n placed = true;\n break;\n }\n }\n if (!placed) existing.bucketCounts[existing.bucketCounts.length - 1]++;\n } else {\n // Initialize bucket counts (boundaries.length + 1 for overflow bucket)\n const bucketCounts = new Array(bucketBoundaries.length + 1).fill(0);\n let placed = false;\n for (let i = 0; i < bucketBoundaries.length; i++) {\n if (value <= bucketBoundaries[i]) {\n bucketCounts[i]++;\n placed = true;\n break;\n }\n }\n if (!placed) bucketCounts[bucketCounts.length - 1]++;\n this.histograms.set(key, { count: 1, sum: value, min: value, max: value, bucketBoundaries, bucketCounts, attributes });\n }\n }\n\n /**\n * Generate unique key for metric with attributes\n */\n private getMetricKey(name: string, attributes: Record<string, string | number | boolean>): string {\n // Fast path: no attributes → return name directly (avoids Object.entries + sort + join)\n const keys = Object.keys(attributes);\n if (keys.length === 0) return name;\n\n const sortedAttrs = Object.entries(attributes)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => `${k}=${v}`)\n .join(',');\n return `${name}|${sortedAttrs}`;\n }\n\n /**\n * Flush accumulated metrics to exporter\n */\n private flushMetrics(): void {\n if (!this.exporter) return;\n\n const timestamp = Date.now();\n\n // Export counters (delta temporality — reset after export)\n for (const [key, data] of this.counters) {\n if (data.value === 0) continue;\n const name = key.split('|')[0];\n const metric: MetricData = {\n name,\n type: 'counter',\n value: data.value,\n attributes: data.attributes,\n timestamp,\n };\n this.exporter.exportMetric(metric);\n data.value = 0;\n }\n\n // Export histogram aggregates\n for (const [key, data] of this.histograms) {\n const name = key.split('|')[0];\n if (data.count === 0) continue;\n\n const avg = data.sum / data.count;\n\n const metric: MetricData = {\n name,\n type: 'histogram',\n value: avg,\n attributes: {\n ...data.attributes,\n count: data.count,\n sum: data.sum,\n min: data.min,\n max: data.max,\n },\n timestamp,\n histogramData: {\n count: data.count,\n sum: data.sum,\n min: data.min,\n max: data.max,\n bucketBoundaries: [...data.bucketBoundaries],\n bucketCounts: [...data.bucketCounts],\n },\n };\n this.exporter.exportMetric(metric);\n\n // Reset accumulators after export\n data.count = 0;\n data.sum = 0;\n data.min = Infinity;\n data.max = -Infinity;\n data.bucketCounts.fill(0);\n }\n }\n\n /**\n * Force flush all pending data\n */\n async flush(): Promise<void> {\n if (this.exporterReady) await this.exporterReady;\n this.flushMetrics();\n await this.exporter?.flush();\n }\n\n /**\n * Shutdown telemetry\n */\n async shutdown(): Promise<void> {\n if (this.metricsIntervalId) {\n clearInterval(this.metricsIntervalId);\n this.metricsIntervalId = null;\n }\n\n await this.flush();\n await this.exporter?.shutdown();\n this.exporter = null;\n }\n\n /**\n * Check if telemetry is enabled\n */\n isEnabled(): boolean {\n return this.config.enabled;\n }\n\n /**\n * Get current configuration\n */\n getConfig(): TelemetryConfig {\n return { ...this.config };\n }\n\n /**\n * Get the active span context for log-to-span correlation.\n * Returns the most recent (top of stack) active span, or null if none.\n */\n getActiveContext(): { traceId: string; spanId: string } | null {\n return this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1] : null;\n }\n}\n","/**\n * Omote SDK Logger\n *\n * Unified logging system with:\n * - 6 log levels (error, warn, info, debug, trace, verbose)\n * - Structured JSON output for machine parsing\n * - Pretty output for human readability\n * - Module-based child loggers\n * - Runtime configuration\n * - Browser and Node.js compatible\n */\n\nimport type {\n LogLevel,\n LogEntry,\n LoggingConfig,\n LogSink,\n ILogger,\n} from './types';\nimport { LOG_LEVEL_PRIORITY, DEFAULT_LOGGING_CONFIG } from './types';\nimport { getFormatter, createBrowserConsoleArgs } from './formatters';\nimport { getTelemetry } from '../telemetry/OmoteTelemetry';\nimport { getClock } from './Clock';\n\n/**\n * Check if running in browser\n */\nconst isBrowser = typeof window !== 'undefined';\n\n/**\n * Global logging configuration\n */\nlet globalConfig: LoggingConfig = { ...DEFAULT_LOGGING_CONFIG };\n\n/**\n * Configure global logging settings\n */\nexport function configureLogging(config: Partial<LoggingConfig>): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\n/**\n * Get current logging configuration\n */\nexport function getLoggingConfig(): LoggingConfig {\n return { ...globalConfig };\n}\n\n/**\n * Reset logging configuration to defaults\n */\nexport function resetLoggingConfig(): void {\n globalConfig = { ...DEFAULT_LOGGING_CONFIG };\n}\n\n/**\n * Set log level at runtime\n */\nexport function setLogLevel(level: LogLevel): void {\n globalConfig.level = level;\n}\n\n/**\n * Enable or disable logging\n */\nexport function setLoggingEnabled(enabled: boolean): void {\n globalConfig.enabled = enabled;\n}\n\n/**\n * Default console sink with browser-optimized output\n */\nconst consoleSink: LogSink = (entry: LogEntry): void => {\n const consoleMethod = entry.level === 'error' ? 'error'\n : entry.level === 'warn' ? 'warn'\n : 'log';\n\n if (globalConfig.format === 'pretty' && isBrowser) {\n // Use styled console output in browser\n const args = createBrowserConsoleArgs(entry);\n (console as unknown as Record<string, (...args: unknown[]) => void>)[consoleMethod](...args);\n } else {\n // Use formatter for terminal or JSON output\n const formatter = getFormatter(globalConfig.format);\n const formatted = formatter(entry);\n (console as unknown as Record<string, (...args: unknown[]) => void>)[consoleMethod](formatted);\n }\n};\n\n/**\n * Get the active sink (custom or default console)\n */\nfunction getActiveSink(): LogSink {\n return globalConfig.sink || consoleSink;\n}\n\n/**\n * Check if a log level should be output given current config\n */\nfunction shouldLog(level: LogLevel): boolean {\n if (!globalConfig.enabled) return false;\n return LOG_LEVEL_PRIORITY[level] <= LOG_LEVEL_PRIORITY[globalConfig.level];\n}\n\n/**\n * Logger implementation\n */\nclass Logger implements ILogger {\n readonly module: string;\n\n constructor(module: string) {\n this.module = module;\n }\n\n private log(level: LogLevel, message: string, data?: Record<string, unknown>): void {\n if (!shouldLog(level)) return;\n\n const entry: LogEntry = {\n timestamp: getClock().timestamp(),\n level,\n module: this.module,\n message,\n data,\n };\n\n // Attach active span context for log-to-span correlation\n const ctx = getTelemetry()?.getActiveContext();\n if (ctx) {\n entry.traceId = ctx.traceId;\n entry.spanId = ctx.spanId;\n }\n\n // Extract error from data if present\n if (data?.error instanceof Error) {\n entry.error = data.error;\n // Remove from data to avoid duplication\n const { error, ...rest } = data;\n entry.data = Object.keys(rest).length > 0 ? rest : undefined;\n }\n\n getActiveSink()(entry);\n }\n\n error(message: string, data?: Record<string, unknown>): void {\n this.log('error', message, data);\n }\n\n warn(message: string, data?: Record<string, unknown>): void {\n this.log('warn', message, data);\n }\n\n info(message: string, data?: Record<string, unknown>): void {\n this.log('info', message, data);\n }\n\n debug(message: string, data?: Record<string, unknown>): void {\n this.log('debug', message, data);\n }\n\n trace(message: string, data?: Record<string, unknown>): void {\n this.log('trace', message, data);\n }\n\n verbose(message: string, data?: Record<string, unknown>): void {\n this.log('verbose', message, data);\n }\n\n child(subModule: string): ILogger {\n return new Logger(`${this.module}.${subModule}`);\n }\n}\n\n/**\n * Logger cache for reusing instances\n */\nconst loggerCache = new Map<string, Logger>();\n\n/**\n * Create a logger for a specific module\n *\n * @param module - Module name (e.g., 'LocalInference', 'ModelCache')\n * @returns Logger instance\n *\n * @example\n * ```typescript\n * const logger = createLogger('LocalInference');\n * logger.info('Model loaded', { backend: 'webgpu', loadTimeMs: 1234 });\n * ```\n */\nexport function createLogger(module: string): ILogger {\n let logger = loggerCache.get(module);\n if (!logger) {\n logger = new Logger(module);\n loggerCache.set(module, logger);\n }\n return logger;\n}\n\n/**\n * Clear logger cache (useful for testing)\n */\nexport function clearLoggerCache(): void {\n loggerCache.clear();\n}\n\n/**\n * No-op logger for when logging is completely disabled\n */\nexport const noopLogger: ILogger = {\n module: 'noop',\n error: () => {},\n warn: () => {},\n info: () => {},\n debug: () => {},\n trace: () => {},\n verbose: () => {},\n child: () => noopLogger,\n};\n\n/**\n * Get a no-op logger (for production builds that tree-shake logging)\n */\nexport function getNoopLogger(): ILogger {\n return noopLogger;\n}\n","/**\r\n * Structured error codes for the Omote SDK.\r\n *\r\n * Codes follow the pattern `OMOTE_{CATEGORY}_{SEQ}` where:\r\n * - **INF** = Inference (model loading, session, OOM)\r\n * - **AUD** = Audio (AudioContext, scheduling, decoding)\r\n * - **SPH** = Speech (VAD, ASR, microphone)\r\n * - **TTS** = Text-to-Speech (synthesis, streaming, phonemizer)\r\n * - **PIP** = Pipeline (state machine, abort)\r\n * - **CAC** = Cache (IndexedDB quota, eviction, staleness)\r\n * - **NET** = Network (fetch, timeout, WebSocket)\r\n *\r\n * Exported as a const object (not enum) for tree-shaking.\r\n */\r\nexport const ErrorCodes = {\r\n // ── Inference ──────────────────────────────────────────────────────────\r\n /** Model failed to load (file not found, corrupted, unsupported format) */\r\n INF_LOAD_FAILED: 'OMOTE_INF_001',\r\n /** ORT session poisoned after WebGPU device loss — must reload tab */\r\n INF_SESSION_POISON: 'OMOTE_INF_002',\r\n /** Inference exceeded timeout threshold */\r\n INF_TIMEOUT: 'OMOTE_INF_003',\r\n /** Out-of-memory during inference or model loading */\r\n INF_OOM: 'OMOTE_INF_004',\r\n /** WebGPU unavailable, fell back to WASM */\r\n INF_WEBGPU_FALLBACK: 'OMOTE_INF_005',\r\n /** Input tensor shape does not match model expectations */\r\n INF_SHAPE_MISMATCH: 'OMOTE_INF_006',\r\n\r\n // ── Audio ──────────────────────────────────────────────────────────────\r\n /** AudioContext creation or resume failed */\r\n AUD_CONTEXT_FAILED: 'OMOTE_AUD_001',\r\n /** Gap detected in audio scheduling (buffer underrun) */\r\n AUD_SCHEDULE_GAP: 'OMOTE_AUD_002',\r\n /** Audio buffer decoding failed */\r\n AUD_DECODE_FAILED: 'OMOTE_AUD_003',\r\n\r\n // ── Speech ─────────────────────────────────────────────────────────────\r\n /** Voice activity detection error */\r\n SPH_VAD_ERROR: 'OMOTE_SPH_001',\r\n /** Automatic speech recognition error */\r\n SPH_ASR_ERROR: 'OMOTE_SPH_002',\r\n /** Microphone access denied or unavailable */\r\n SPH_MIC_DENIED: 'OMOTE_SPH_003',\r\n\r\n // ── TTS ────────────────────────────────────────────────────────────────\r\n /** TTS synthesis failed */\r\n TTS_SYNTH_FAILED: 'OMOTE_TTS_001',\r\n /** TTS streaming error (chunk delivery failure) */\r\n TTS_STREAM_ERROR: 'OMOTE_TTS_002',\r\n /** Phonemizer (eSpeak-NG WASM) ran out of memory */\r\n TTS_PHONEMIZER_OOM: 'OMOTE_TTS_003',\r\n\r\n // ── Pipeline ───────────────────────────────────────────────────────────\r\n /** Invalid state transition in pipeline state machine */\r\n PIP_STATE_ERROR: 'OMOTE_PIP_001',\r\n /** Pipeline operation aborted (user interrupt or signal) */\r\n PIP_ABORT: 'OMOTE_PIP_002',\r\n\r\n // ── Cache ──────────────────────────────────────────────────────────────\r\n /** IndexedDB storage quota exceeded */\r\n CAC_QUOTA_EXCEEDED: 'OMOTE_CAC_001',\r\n /** Cache entry evicted (LRU or manual) */\r\n CAC_EVICTION: 'OMOTE_CAC_002',\r\n /** Cached model is stale (version mismatch) */\r\n CAC_STALE: 'OMOTE_CAC_003',\r\n\r\n // ── Network ────────────────────────────────────────────────────────────\r\n /** HTTP fetch failed (model download, CDN) */\r\n NET_FETCH_FAILED: 'OMOTE_NET_001',\r\n /** Network request timed out */\r\n NET_TIMEOUT: 'OMOTE_NET_002',\r\n /** WebSocket connection error */\r\n NET_WEBSOCKET_ERROR: 'OMOTE_NET_003',\r\n} as const;\r\n\r\n/** Union type of all error code string values. */\r\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\r\n"],"mappings":"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","names":["isBrowser"]}
@@ -82,6 +82,8 @@ var jsonFormatter = (entry) => {
82
82
  if (entry.data && Object.keys(entry.data).length > 0) {
83
83
  output.data = entry.data;
84
84
  }
85
+ if (entry.traceId) output.traceId = entry.traceId;
86
+ if (entry.spanId) output.spanId = entry.spanId;
85
87
  if (entry.error) {
86
88
  output.error = {
87
89
  name: entry.error.name,
@@ -103,6 +105,9 @@ var prettyFormatter = (entry) => {
103
105
  const color = LEVEL_COLORS[entry.level];
104
106
  output = `${COLORS.gray}${time}${COLORS.reset} ${color}${level}${COLORS.reset} ${COLORS.cyan}[${module}]${COLORS.reset} ${message}`;
105
107
  }
108
+ if (entry.traceId) {
109
+ output += ` [trace:${entry.traceId.slice(0, 8)}]`;
110
+ }
106
111
  if (entry.data && Object.keys(entry.data).length > 0) {
107
112
  const dataStr = safeStringify(entry.data);
108
113
  if (dataStr.length > 80) {
@@ -207,199 +212,21 @@ var ConsoleExporter = class {
207
212
  }
208
213
  };
209
214
 
210
- // src/telemetry/exporters/otlp.ts
211
- var StatusCode = {
212
- UNSET: 0,
213
- OK: 1,
214
- ERROR: 2
215
+ // src/logging/Clock.ts
216
+ var defaultClock = {
217
+ now: () => performance.now(),
218
+ timestamp: () => Date.now()
215
219
  };
216
- function spanToOTLP(span, serviceName, serviceVersion) {
217
- const attributes = Object.entries(span.attributes).filter(([, v]) => v !== void 0).map(([key, value]) => ({
218
- key,
219
- value: typeof value === "string" ? { stringValue: value } : typeof value === "number" ? Number.isInteger(value) ? { intValue: value } : { doubleValue: value } : { boolValue: value }
220
- }));
221
- return {
222
- resourceSpans: [{
223
- resource: {
224
- attributes: [
225
- { key: "service.name", value: { stringValue: serviceName } },
226
- { key: "service.version", value: { stringValue: serviceVersion } },
227
- { key: "telemetry.sdk.name", value: { stringValue: "omote-sdk" } },
228
- { key: "telemetry.sdk.language", value: { stringValue: "javascript" } }
229
- ]
230
- },
231
- scopeSpans: [{
232
- scope: {
233
- name: "omote-sdk",
234
- version: serviceVersion
235
- },
236
- spans: [{
237
- traceId: span.traceId,
238
- spanId: span.spanId,
239
- parentSpanId: span.parentSpanId || "",
240
- name: span.name,
241
- kind: 1,
242
- // INTERNAL
243
- startTimeUnixNano: String(span.startTime * 1e6),
244
- endTimeUnixNano: String(span.endTime * 1e6),
245
- attributes,
246
- status: {
247
- code: span.status === "ok" ? StatusCode.OK : StatusCode.ERROR,
248
- message: span.error?.message || ""
249
- }
250
- }]
251
- }]
252
- }]
253
- };
220
+ var activeClock = defaultClock;
221
+ function configureClock(clock) {
222
+ activeClock = clock;
254
223
  }
255
- function metricToOTLP(metric, serviceName, serviceVersion) {
256
- const attributes = Object.entries(metric.attributes).filter(([, v]) => v !== void 0).map(([key, value]) => ({
257
- key,
258
- value: typeof value === "string" ? { stringValue: value } : typeof value === "number" ? Number.isInteger(value) ? { intValue: value } : { doubleValue: value } : { boolValue: value }
259
- }));
260
- const dataPoint = {
261
- attributes,
262
- timeUnixNano: String(metric.timestamp * 1e6),
263
- ...metric.type === "counter" ? { asInt: metric.value } : { asDouble: metric.value }
264
- };
265
- return {
266
- resourceMetrics: [{
267
- resource: {
268
- attributes: [
269
- { key: "service.name", value: { stringValue: serviceName } },
270
- { key: "service.version", value: { stringValue: serviceVersion } }
271
- ]
272
- },
273
- scopeMetrics: [{
274
- scope: {
275
- name: "omote-sdk",
276
- version: serviceVersion
277
- },
278
- metrics: [{
279
- name: metric.name,
280
- ...metric.type === "counter" ? {
281
- sum: {
282
- dataPoints: [dataPoint],
283
- aggregationTemporality: 2,
284
- // CUMULATIVE
285
- isMonotonic: true
286
- }
287
- } : {
288
- gauge: {
289
- dataPoints: [dataPoint]
290
- }
291
- }
292
- }]
293
- }]
294
- }]
295
- };
224
+ function getClock() {
225
+ return activeClock;
296
226
  }
297
- var OTLPExporter = class {
298
- constructor(config, serviceName = "omote-sdk", serviceVersion = "0.1.0") {
299
- this.spanBuffer = [];
300
- this.metricBuffer = [];
301
- this.flushIntervalId = null;
302
- this.BUFFER_SIZE = 100;
303
- this.FLUSH_INTERVAL_MS = 5e3;
304
- this.isShutdown = false;
305
- const parsed = new URL(config.endpoint);
306
- if (parsed.protocol !== "https:") {
307
- const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]";
308
- if (!isLocalhost) {
309
- throw new Error("OTLP endpoint must use HTTPS (or localhost for development)");
310
- }
311
- }
312
- this.config = {
313
- endpoint: config.endpoint,
314
- timeoutMs: config.timeoutMs ?? 1e4,
315
- headers: config.headers ? { ...config.headers } : {}
316
- };
317
- this.serviceName = serviceName;
318
- this.serviceVersion = serviceVersion;
319
- this.flushIntervalId = setInterval(() => {
320
- this.flush().catch(console.error);
321
- }, this.FLUSH_INTERVAL_MS);
322
- }
323
- exportSpan(span) {
324
- if (this.isShutdown) return;
325
- this.spanBuffer.push(span);
326
- if (this.spanBuffer.length >= this.BUFFER_SIZE) {
327
- this.flush().catch(console.error);
328
- }
329
- }
330
- exportMetric(metric) {
331
- if (this.isShutdown) return;
332
- this.metricBuffer.push(metric);
333
- if (this.metricBuffer.length >= this.BUFFER_SIZE) {
334
- this.flush().catch(console.error);
335
- }
336
- }
337
- async flush() {
338
- if (this.isShutdown) return;
339
- const spans = this.spanBuffer.splice(0);
340
- const metrics = this.metricBuffer.splice(0);
341
- const promises = [];
342
- if (spans.length > 0) {
343
- promises.push(this.exportSpans(spans));
344
- }
345
- if (metrics.length > 0) {
346
- promises.push(this.exportMetrics(metrics));
347
- }
348
- await Promise.all(promises);
349
- }
350
- async shutdown() {
351
- if (this.flushIntervalId) {
352
- clearInterval(this.flushIntervalId);
353
- this.flushIntervalId = null;
354
- }
355
- await this.flush();
356
- this.isShutdown = true;
357
- }
358
- async exportSpans(spans) {
359
- const resourceSpans = spans.map(
360
- (span) => spanToOTLP(span, this.serviceName, this.serviceVersion).resourceSpans[0]
361
- );
362
- const body = { resourceSpans };
363
- const endpoint = this.config.endpoint.replace(/\/$/, "") + "/v1/traces";
364
- await this.sendRequest(endpoint, body);
365
- }
366
- async exportMetrics(metrics) {
367
- const resourceMetrics = metrics.map(
368
- (metric) => metricToOTLP(metric, this.serviceName, this.serviceVersion).resourceMetrics[0]
369
- );
370
- const body = { resourceMetrics };
371
- const endpoint = this.config.endpoint.replace(/\/$/, "") + "/v1/metrics";
372
- await this.sendRequest(endpoint, body);
373
- }
374
- async sendRequest(endpoint, body) {
375
- const controller = new AbortController();
376
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeoutMs);
377
- try {
378
- const response = await fetch(endpoint, {
379
- method: "POST",
380
- headers: {
381
- "Content-Type": "application/json",
382
- ...this.config.headers
383
- },
384
- body: JSON.stringify(body),
385
- signal: controller.signal
386
- });
387
- if (!response.ok) {
388
- console.warn(`[OTLP] Export failed: ${response.status} ${response.statusText}`);
389
- }
390
- } catch (error) {
391
- if (error.name === "AbortError") {
392
- console.warn("[OTLP] Export timed out");
393
- } else {
394
- console.warn("[OTLP] Export error:", error);
395
- }
396
- } finally {
397
- clearTimeout(timeoutId);
398
- }
399
- }
400
- };
401
227
 
402
228
  // src/telemetry/OmoteTelemetry.ts
229
+ var DEFAULT_HISTOGRAM_BUCKETS = [1, 5, 10, 25, 50, 100, 250, 500, 1e3, 2500, 5e3, 1e4, 3e4, 6e4];
403
230
  function generateId(length = 16) {
404
231
  const bytes = new Uint8Array(length);
405
232
  crypto.getRandomValues(bytes);
@@ -419,6 +246,7 @@ function getTelemetry() {
419
246
  var OmoteTelemetry = class {
420
247
  constructor(config) {
421
248
  this.exporter = null;
249
+ this.exporterReady = null;
422
250
  this.activeTraceId = null;
423
251
  this.metricsIntervalId = null;
424
252
  // Span stack for log-to-span correlation
@@ -437,14 +265,18 @@ var OmoteTelemetry = class {
437
265
  metricsIntervalMs: config.metricsIntervalMs ?? 6e4
438
266
  };
439
267
  if (this.config.enabled) {
440
- this.initExporter();
441
- this.startMetricsCollection();
268
+ if (config.customExporter) {
269
+ this.exporter = config.customExporter;
270
+ this.startMetricsCollection();
271
+ } else {
272
+ this.exporterReady = this.initExporter();
273
+ }
442
274
  }
443
275
  }
444
276
  /**
445
277
  * Initialize the configured exporter
446
278
  */
447
- initExporter() {
279
+ async initExporter() {
448
280
  switch (this.config.exporter) {
449
281
  case "console":
450
282
  this.exporter = new ConsoleExporter({ enabled: true });
@@ -454,16 +286,20 @@ var OmoteTelemetry = class {
454
286
  console.warn("[Telemetry] OTLP exporter requires exporterConfig with endpoint");
455
287
  return;
456
288
  }
457
- this.exporter = new OTLPExporter(
458
- this.config.exporterConfig,
459
- this.config.serviceName,
460
- this.config.serviceVersion
461
- );
289
+ {
290
+ const { OTLPExporter } = await import("./otlp-2BML6FIK.mjs");
291
+ this.exporter = new OTLPExporter(
292
+ this.config.exporterConfig,
293
+ this.config.serviceName,
294
+ this.config.serviceVersion
295
+ );
296
+ }
462
297
  break;
463
298
  case "none":
464
299
  default:
465
300
  this.exporter = null;
466
301
  }
302
+ this.startMetricsCollection();
467
303
  }
468
304
  /**
469
305
  * Start periodic metrics collection
@@ -507,7 +343,8 @@ var OmoteTelemetry = class {
507
343
  const traceId = parentContext?.traceId ?? this.activeTraceId ?? generateId(16);
508
344
  const spanId = generateId(8);
509
345
  const parentSpanId = parentContext?.spanId;
510
- const startTime = performance.now();
346
+ const startTime = getClock().now();
347
+ const epochMs = Date.now();
511
348
  if (!parentContext && !this.activeTraceId) {
512
349
  this.activeTraceId = traceId;
513
350
  }
@@ -521,8 +358,9 @@ var OmoteTelemetry = class {
521
358
  ended = true;
522
359
  const idx = this.spanStack.findIndex((s) => s.spanId === spanId);
523
360
  if (idx !== -1) this.spanStack.splice(idx, 1);
524
- const endTime = performance.now();
361
+ const endTime = getClock().now();
525
362
  const durationMs = endTime - startTime;
363
+ const endEpochMs = epochMs + (endTime - startTime);
526
364
  if (status === "error" && !sampled) {
527
365
  sampled = this.shouldSample(true);
528
366
  }
@@ -535,6 +373,8 @@ var OmoteTelemetry = class {
535
373
  startTime,
536
374
  endTime,
537
375
  durationMs,
376
+ epochMs,
377
+ endEpochMs,
538
378
  status,
539
379
  attributes: spanAttributes,
540
380
  error
@@ -609,7 +449,7 @@ var OmoteTelemetry = class {
609
449
  * });
610
450
  * ```
611
451
  */
612
- recordHistogram(name, value, attributes = {}) {
452
+ recordHistogram(name, value, attributes = {}, bucketBoundaries = DEFAULT_HISTOGRAM_BUCKETS) {
613
453
  if (!this.config.enabled || !this.config.metricsEnabled) return;
614
454
  const key = this.getMetricKey(name, attributes);
615
455
  const existing = this.histograms.get(key);
@@ -618,8 +458,27 @@ var OmoteTelemetry = class {
618
458
  existing.sum += value;
619
459
  if (value < existing.min) existing.min = value;
620
460
  if (value > existing.max) existing.max = value;
461
+ let placed = false;
462
+ for (let i = 0; i < existing.bucketBoundaries.length; i++) {
463
+ if (value <= existing.bucketBoundaries[i]) {
464
+ existing.bucketCounts[i]++;
465
+ placed = true;
466
+ break;
467
+ }
468
+ }
469
+ if (!placed) existing.bucketCounts[existing.bucketCounts.length - 1]++;
621
470
  } else {
622
- this.histograms.set(key, { count: 1, sum: value, min: value, max: value, attributes });
471
+ const bucketCounts = new Array(bucketBoundaries.length + 1).fill(0);
472
+ let placed = false;
473
+ for (let i = 0; i < bucketBoundaries.length; i++) {
474
+ if (value <= bucketBoundaries[i]) {
475
+ bucketCounts[i]++;
476
+ placed = true;
477
+ break;
478
+ }
479
+ }
480
+ if (!placed) bucketCounts[bucketCounts.length - 1]++;
481
+ this.histograms.set(key, { count: 1, sum: value, min: value, max: value, bucketBoundaries, bucketCounts, attributes });
623
482
  }
624
483
  }
625
484
  /**
@@ -636,7 +495,7 @@ var OmoteTelemetry = class {
636
495
  */
637
496
  flushMetrics() {
638
497
  if (!this.exporter) return;
639
- const timestamp = performance.now();
498
+ const timestamp = Date.now();
640
499
  for (const [key, data] of this.counters) {
641
500
  if (data.value === 0) continue;
642
501
  const name = key.split("|")[0];
@@ -665,19 +524,29 @@ var OmoteTelemetry = class {
665
524
  min: data.min,
666
525
  max: data.max
667
526
  },
668
- timestamp
527
+ timestamp,
528
+ histogramData: {
529
+ count: data.count,
530
+ sum: data.sum,
531
+ min: data.min,
532
+ max: data.max,
533
+ bucketBoundaries: [...data.bucketBoundaries],
534
+ bucketCounts: [...data.bucketCounts]
535
+ }
669
536
  };
670
537
  this.exporter.exportMetric(metric);
671
538
  data.count = 0;
672
539
  data.sum = 0;
673
540
  data.min = Infinity;
674
541
  data.max = -Infinity;
542
+ data.bucketCounts.fill(0);
675
543
  }
676
544
  }
677
545
  /**
678
546
  * Force flush all pending data
679
547
  */
680
548
  async flush() {
549
+ if (this.exporterReady) await this.exporterReady;
681
550
  this.flushMetrics();
682
551
  await this.exporter?.flush();
683
552
  }
@@ -757,7 +626,7 @@ var Logger = class _Logger {
757
626
  log(level, message, data) {
758
627
  if (!shouldLog(level)) return;
759
628
  const entry = {
760
- timestamp: Date.now(),
629
+ timestamp: getClock().timestamp(),
761
630
  level,
762
631
  module: this.module,
763
632
  message,
@@ -829,6 +698,63 @@ function getNoopLogger() {
829
698
  return noopLogger;
830
699
  }
831
700
 
701
+ // src/logging/ErrorCodes.ts
702
+ var ErrorCodes = {
703
+ // ── Inference ──────────────────────────────────────────────────────────
704
+ /** Model failed to load (file not found, corrupted, unsupported format) */
705
+ INF_LOAD_FAILED: "OMOTE_INF_001",
706
+ /** ORT session poisoned after WebGPU device loss — must reload tab */
707
+ INF_SESSION_POISON: "OMOTE_INF_002",
708
+ /** Inference exceeded timeout threshold */
709
+ INF_TIMEOUT: "OMOTE_INF_003",
710
+ /** Out-of-memory during inference or model loading */
711
+ INF_OOM: "OMOTE_INF_004",
712
+ /** WebGPU unavailable, fell back to WASM */
713
+ INF_WEBGPU_FALLBACK: "OMOTE_INF_005",
714
+ /** Input tensor shape does not match model expectations */
715
+ INF_SHAPE_MISMATCH: "OMOTE_INF_006",
716
+ // ── Audio ──────────────────────────────────────────────────────────────
717
+ /** AudioContext creation or resume failed */
718
+ AUD_CONTEXT_FAILED: "OMOTE_AUD_001",
719
+ /** Gap detected in audio scheduling (buffer underrun) */
720
+ AUD_SCHEDULE_GAP: "OMOTE_AUD_002",
721
+ /** Audio buffer decoding failed */
722
+ AUD_DECODE_FAILED: "OMOTE_AUD_003",
723
+ // ── Speech ─────────────────────────────────────────────────────────────
724
+ /** Voice activity detection error */
725
+ SPH_VAD_ERROR: "OMOTE_SPH_001",
726
+ /** Automatic speech recognition error */
727
+ SPH_ASR_ERROR: "OMOTE_SPH_002",
728
+ /** Microphone access denied or unavailable */
729
+ SPH_MIC_DENIED: "OMOTE_SPH_003",
730
+ // ── TTS ────────────────────────────────────────────────────────────────
731
+ /** TTS synthesis failed */
732
+ TTS_SYNTH_FAILED: "OMOTE_TTS_001",
733
+ /** TTS streaming error (chunk delivery failure) */
734
+ TTS_STREAM_ERROR: "OMOTE_TTS_002",
735
+ /** Phonemizer (eSpeak-NG WASM) ran out of memory */
736
+ TTS_PHONEMIZER_OOM: "OMOTE_TTS_003",
737
+ // ── Pipeline ───────────────────────────────────────────────────────────
738
+ /** Invalid state transition in pipeline state machine */
739
+ PIP_STATE_ERROR: "OMOTE_PIP_001",
740
+ /** Pipeline operation aborted (user interrupt or signal) */
741
+ PIP_ABORT: "OMOTE_PIP_002",
742
+ // ── Cache ──────────────────────────────────────────────────────────────
743
+ /** IndexedDB storage quota exceeded */
744
+ CAC_QUOTA_EXCEEDED: "OMOTE_CAC_001",
745
+ /** Cache entry evicted (LRU or manual) */
746
+ CAC_EVICTION: "OMOTE_CAC_002",
747
+ /** Cached model is stale (version mismatch) */
748
+ CAC_STALE: "OMOTE_CAC_003",
749
+ // ── Network ────────────────────────────────────────────────────────────
750
+ /** HTTP fetch failed (model download, CDN) */
751
+ NET_FETCH_FAILED: "OMOTE_NET_001",
752
+ /** Network request timed out */
753
+ NET_TIMEOUT: "OMOTE_NET_002",
754
+ /** WebSocket connection error */
755
+ NET_WEBSOCKET_ERROR: "OMOTE_NET_003"
756
+ };
757
+
832
758
  export {
833
759
  LOG_LEVEL_PRIORITY,
834
760
  DEFAULT_LOGGING_CONFIG,
@@ -836,7 +762,9 @@ export {
836
762
  prettyFormatter,
837
763
  getFormatter,
838
764
  ConsoleExporter,
839
- OTLPExporter,
765
+ defaultClock,
766
+ configureClock,
767
+ getClock,
840
768
  configureTelemetry,
841
769
  getTelemetry,
842
770
  OmoteTelemetry,
@@ -848,6 +776,7 @@ export {
848
776
  createLogger,
849
777
  clearLoggerCache,
850
778
  noopLogger,
851
- getNoopLogger
779
+ getNoopLogger,
780
+ ErrorCodes
852
781
  };
853
- //# sourceMappingURL=chunk-3NDJA3I4.mjs.map
782
+ //# sourceMappingURL=chunk-NWZMIQK4.mjs.map