UNPKG

@brizz/sdk

Version:

OpenTelemetry-based observability SDK for AI applications

1 lines 135 kB
{"version":3,"sources":["../src/index.ts","../src/internal/instrumentation/auto-init.ts","../src/internal/logger.ts","../src/internal/sdk.ts","../src/internal/config.ts","../src/internal/instrumentation/registry.ts","../src/internal/log/logging.ts","../src/internal/log/processors/log-processor.ts","../src/internal/masking/patterns.ts","../src/internal/masking/utils.ts","../src/internal/semantic-conventions.ts","../src/internal/metric/metrics.ts","../src/internal/trace/tracing.ts","../src/internal/trace/processors/span-processor.ts","../src/internal/trace/transformations/vercel-ai.ts","../src/internal/trace/session.ts","../src/node/runtime.ts","../src/init.ts"],"sourcesContent":["// Auto-initialize instrumentations before anything else\nimport './internal/instrumentation/auto-init.js';\n\n// Re-export main SDK instance and types\nexport { Brizz, type IBrizzInitializeOptions } from './internal/sdk.js';\n\n// Re-export instrumentation types for manual configuration\nexport { type IInstrumentModules } from './internal/instrumentation/index.js';\n\n// Re-export utility functions from their respective modules\nexport { emitEvent } from './internal/log/index.js';\nexport { getSpanExporter, getSpanProcessor, withSessionId, callWithSessionId } from './internal/trace/index.js';\nexport { getMetricsExporter, getMetricsReader } from './internal/metric/index.js';\n// Re-export masking types and utilities\nexport type {\n IMaskingConfig,\n ISpanMaskingConfig,\n ILogMaskingConfig,\n IEventMaskingConfig,\n IEventMaskingRule,\n MaskingMode,\n IAttributesMaskingRule,\n} from './internal/masking/index.js';\n\n// Re-export masking patterns and utilities\nexport { DEFAULT_PII_PATTERNS } from './internal/masking/patterns.js';\nexport { maskValue, maskAttributes } from './internal/masking/utils.js';\n\n// Re-export logger types, enums, and functions\nexport { LogLevel, logger, setLogLevel, getLogLevel } from './internal/logger.js';\nexport type { ILogger } from './internal/logger.js';\n\n// Re-export OpenTelemetry types that users might need\nexport { SeverityNumber } from '@opentelemetry/api-logs';\n\n// Re-export runtime utilities\nexport * from './node/runtime.js';\n\n// Explicit init entrypoint (preload)\nexport * as init from './init.js';\n","import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';\nimport type { Instrumentation } from '@opentelemetry/instrumentation';\nimport { registerInstrumentations } from '@opentelemetry/instrumentation';\nimport { AnthropicInstrumentation } from '@traceloop/instrumentation-anthropic';\nimport { BedrockInstrumentation } from '@traceloop/instrumentation-bedrock';\nimport { ChromaDBInstrumentation } from '@traceloop/instrumentation-chromadb';\nimport { CohereInstrumentation } from '@traceloop/instrumentation-cohere';\nimport { LangChainInstrumentation } from '@traceloop/instrumentation-langchain';\nimport { LlamaIndexInstrumentation } from '@traceloop/instrumentation-llamaindex';\nimport { OpenAIInstrumentation } from '@traceloop/instrumentation-openai';\nimport { PineconeInstrumentation } from '@traceloop/instrumentation-pinecone';\nimport { QdrantInstrumentation } from '@traceloop/instrumentation-qdrant';\nimport { TogetherInstrumentation } from '@traceloop/instrumentation-together';\nimport { VertexAIInstrumentation } from '@traceloop/instrumentation-vertexai';\n\nimport { logger } from '../logger.js';\n\n/**\n * Auto-initialization that runs at import time.\n * This ensures all instrumentations are registered before any libraries are loaded.\n *\n * Flow:\n * 1. Load node auto-instrumentations (including Winston)\n * 2. Initialize logger (after Winston instrumentation is ready)\n * 3. Load GenAI instrumentations with error logging\n */\n\nlet autoInstrumentationsLoaded = false;\n\nconst exceptionLogger = (error: Error) => {\n logger.error(`Exception in instrumentation: ${String(error)}`);\n};\n\nfunction loadNodeAutoInstrumentations(): Instrumentation[] {\n try {\n const nodeInstrumentations = getNodeAutoInstrumentations();\n registerInstrumentations({ instrumentations: nodeInstrumentations });\n return nodeInstrumentations;\n } catch (error) {\n logger.error(`Failed to load Node.js auto-instrumentations: ${String(error)}`);\n return [];\n }\n}\n\nfunction loadGenAIInstrumentations(): Instrumentation[] {\n const instrumentations: Instrumentation[] = [];\n\n const genAIInstrumentationClasses = [\n { class: OpenAIInstrumentation, name: 'OpenAI' },\n { class: AnthropicInstrumentation, name: 'Anthropic' },\n { class: CohereInstrumentation, name: 'Cohere' },\n { class: VertexAIInstrumentation, name: 'Vertex AI' },\n { class: BedrockInstrumentation, name: 'Bedrock' },\n { class: PineconeInstrumentation, name: 'Pinecone' },\n { class: LangChainInstrumentation, name: 'LangChain' },\n { class: LlamaIndexInstrumentation, name: 'LlamaIndex' },\n { class: ChromaDBInstrumentation, name: 'ChromaDB' },\n { class: QdrantInstrumentation, name: 'Qdrant' },\n { class: TogetherInstrumentation, name: 'Together' },\n ];\n\n for (const config of genAIInstrumentationClasses) {\n try {\n const instrumentation = new config.class({ exceptionLogger });\n instrumentations.push(instrumentation);\n logger.debug(`Auto-loaded ${config.name} instrumentation`);\n } catch (error) {\n logger.error(`Failed to auto-load ${config.name} instrumentation: ${String(error)}`);\n }\n }\n\n try {\n registerInstrumentations({ instrumentations });\n logger.info(`Auto-registered ${instrumentations.length} GenAI instrumentations`);\n } catch (error) {\n logger.error(`Failed to register GenAI instrumentations: ${String(error)}`);\n }\n\n return instrumentations;\n}\n\n/**\n * Auto-initialize all instrumentations.\n * Called immediately when this module is imported.\n */\nfunction autoInitializeInstrumentations(): void {\n if (autoInstrumentationsLoaded) {\n return;\n }\n\n try {\n // Step 1: Load node auto-instrumentations first\n const nodeInstrumentations = loadNodeAutoInstrumentations();\n\n // Step 2: Load GenAI instrumentations with logger exception handling\n const genAIInstrumentations = loadGenAIInstrumentations();\n\n autoInstrumentationsLoaded = true;\n logger.info(\n `Auto-initialization complete: ${nodeInstrumentations.length} node + ${genAIInstrumentations.length} GenAI instrumentations`,\n );\n } catch (error) {\n logger.error(`Auto-initialization failed: ${String(error)}`);\n autoInstrumentationsLoaded = false;\n }\n}\n\n// Auto-initialize immediately when this module is imported\nautoInitializeInstrumentations();\n\nexport { autoInstrumentationsLoaded };\n","/**\n * Logger interface and implementations for the Brizz SDK.\n *\n * Simple Pino-based logging system that works everywhere in the SDK,\n * including during bootstrap and instrumentation loading.\n */\n\nimport { DiagLogLevel } from '@opentelemetry/api';\nimport pino from 'pino';\n\n/**\n * Available log levels for the Brizz SDK, ordered by severity.\n */\nexport enum LogLevel {\n NONE = 0,\n ERROR = 1,\n WARN = 2,\n INFO = 3,\n DEBUG = 4,\n}\n\n/**\n * Default log level for the SDK when no level is specified.\n */\nexport const DEFAULT_LOG_LEVEL = LogLevel.WARN;\n\n/**\n * Internal logger interface for the SDK.\n */\nexport interface ILogger {\n debug: (msg: string, ...meta: unknown[]) => void;\n info: (msg: string, ...meta: unknown[]) => void;\n warn: (msg: string, ...meta: unknown[]) => void;\n error: (msg: string, ...meta: unknown[]) => void;\n setLevel: (level: LogLevel) => void;\n getLevel: () => LogLevel;\n}\n\n/**\n * Pino-based logger implementation with dynamic level control.\n */\nclass PinoLogger implements ILogger {\n private _level: LogLevel = DEFAULT_LOG_LEVEL;\n private _pinoLogger: pino.Logger | null = null;\n\n constructor() {\n // Set initial level from environment\n const envLevel = this.getLogLevelFromEnv();\n this._level = envLevel;\n }\n\n /**\n * Lazy initialization of Pino logger to ensure it's created AFTER Jest spies\n * are set up during tests. This prevents the pino-pretty transport from\n * bypassing stdout/stderr spies.\n */\n private ensurePinoLogger(): pino.Logger {\n if (!this._pinoLogger) {\n this._pinoLogger = pino({\n name: 'Brizz',\n level: this.logLevelToPino(this._level),\n // Disable transport in test environment to allow proper spy capture\n transport: this.isProduction() || this.isTest()\n ? undefined\n : {\n target: 'pino-pretty',\n options: {\n singleLine: true,\n colorize: true,\n translateTime: 'HH:MM:ss',\n ignore: 'pid,hostname',\n messageFormat: '[{name}] {msg}',\n },\n },\n });\n }\n return this._pinoLogger;\n }\n\n private isProduction(): boolean {\n return process.env['NODE_ENV'] === 'production';\n }\n\n private isTest(): boolean {\n return process.env['NODE_ENV'] === 'test';\n }\n\n private getLogLevelFromEnv(): LogLevel {\n const envLevel = process.env['BRIZZ_LOG_LEVEL'];\n return envLevel ? parseLogLevel(envLevel) : DEFAULT_LOG_LEVEL;\n }\n\n private logLevelToPino(level: LogLevel): string {\n switch (level) {\n case LogLevel.DEBUG:\n return 'debug';\n case LogLevel.INFO:\n return 'info';\n case LogLevel.WARN:\n return 'warn';\n case LogLevel.ERROR:\n return 'error';\n default:\n return 'silent';\n }\n }\n\n private formatMeta(meta: unknown[]): object {\n if (meta.length === 0) {\n return {};\n }\n\n if (meta.length === 1 && typeof meta[0] === 'object' && meta[0] !== null) {\n return meta[0] as object;\n }\n\n return { metadata: meta };\n }\n\n setLevel(level: LogLevel): void {\n this._level = level;\n if (this._pinoLogger) {\n this._pinoLogger.level = this.logLevelToPino(level);\n }\n }\n\n getLevel(): LogLevel {\n return this._level;\n }\n\n debug = (msg: string, ...meta: unknown[]): void => {\n if (this._level >= LogLevel.DEBUG) {\n this.ensurePinoLogger().debug(this.formatMeta(meta), msg);\n }\n };\n\n info = (msg: string, ...meta: unknown[]): void => {\n if (this._level >= LogLevel.INFO) {\n this.ensurePinoLogger().info(this.formatMeta(meta), msg);\n }\n };\n\n warn = (msg: string, ...meta: unknown[]): void => {\n if (this._level >= LogLevel.WARN) {\n this.ensurePinoLogger().warn(this.formatMeta(meta), msg);\n }\n };\n\n error = (msg: string, ...meta: unknown[]): void => {\n if (this._level >= LogLevel.ERROR) {\n this.ensurePinoLogger().error(this.formatMeta(meta), msg);\n }\n };\n}\n\n/**\n * No-op logger that silences all logging output.\n */\nexport const noopLogger: ILogger = {\n debug: () => {},\n info: () => {},\n warn: () => {},\n error: () => {},\n setLevel: () => {},\n getLevel: () => LogLevel.NONE,\n};\n\n/**\n * Parses a log level string into a LogLevel enum value.\n * Supports both string names and numeric values.\n */\nexport function parseLogLevel(level?: string): LogLevel {\n if (!level) {\n return DEFAULT_LOG_LEVEL;\n }\n\n const normalizedLevel = level.toLowerCase().trim();\n\n switch (normalizedLevel) {\n case 'debug':\n return LogLevel.DEBUG;\n case 'info':\n return LogLevel.INFO;\n case 'warn':\n case 'warning':\n return LogLevel.WARN;\n case 'error':\n return LogLevel.ERROR;\n case 'none':\n case 'off':\n case 'silent':\n return LogLevel.NONE;\n default: {\n // Try to parse as number\n const numLevel = Number.parseInt(normalizedLevel, 10);\n if (!Number.isNaN(numLevel) && numLevel >= 0 && numLevel <= 4) {\n return numLevel as LogLevel;\n }\n return DEFAULT_LOG_LEVEL;\n }\n }\n}\n\n/**\n * Global logger instance - initialized once and used throughout the SDK.\n */\nexport const logger: ILogger = new PinoLogger();\n\n/**\n * Sets the global logger level.\n */\nexport function setLogLevel(level: LogLevel | string): void {\n const resolvedLevel = typeof level === 'string' ? parseLogLevel(level) : level;\n logger.setLevel(resolvedLevel);\n}\n\n/**\n * Gets the current global logger level.\n */\nexport function getLogLevel(): LogLevel {\n return logger.getLevel();\n}\n\n/**\n * Maps Brizz LogLevel to OpenTelemetry DiagLogLevel.\n */\nexport function mapLogLevelToDiagLevel(logLevel: LogLevel): DiagLogLevel {\n switch (logLevel) {\n case LogLevel.DEBUG:\n return DiagLogLevel.DEBUG;\n case LogLevel.INFO:\n return DiagLogLevel.INFO;\n case LogLevel.WARN:\n return DiagLogLevel.WARN;\n case LogLevel.ERROR:\n return DiagLogLevel.ERROR;\n default:\n return DiagLogLevel.NONE;\n }\n}\n","import { resourceFromAttributes } from '@opentelemetry/resources';\nimport type { LogRecordExporter } from '@opentelemetry/sdk-logs';\nimport type { MetricReader } from '@opentelemetry/sdk-metrics';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport type { SpanExporter } from '@opentelemetry/sdk-trace-base';\n\nimport type { IResolvedBrizzConfig } from './config';\nimport { resolveConfig } from './config';\nimport { InstrumentationRegistry, type IInstrumentModules } from './instrumentation';\nimport { LoggingModule } from './log';\nimport type { LogLevel } from './logger';\nimport { logger } from './logger';\nimport { type IMaskingConfig } from './masking';\nimport { getMetricsReader, MetricsModule } from './metric';\nimport { getSpanProcessor, TracingModule } from './trace';\n\n/**\n * Configuration options for initializing the Brizz SDK.\n *\n * @interface IBrizzInitializeOptions\n */\nexport interface IBrizzInitializeOptions {\n /** Application name for service identification */\n appName?: string;\n /** Base URL for telemetry endpoints (defaults to https://telemetry.brizz.dev) */\n baseUrl?: string;\n /** API key for authentication */\n apiKey?: string;\n /** Additional HTTP headers to include in telemetry requests */\n headers?: Record<string, string>;\n /** Disable batch processing for immediate event emission */\n disableBatch?: boolean;\n /**\n * Configuration for data masking and PII protection.\n * - `true`: Enable masking with default rules\n * - `false` or `undefined`: Disable masking\n * - `IMaskingConfig`: Enable masking with custom configuration\n */\n masking?: boolean | IMaskingConfig;\n /** Log level for SDK internal logging and OpenTelemetry diagnostic logging (debug, info, warn, error, none) */\n logLevel?: LogLevel | string;\n /**\n * Manual instrumentation modules for Next.js/Webpack environments.\n * Provide the actual imported modules to enable instrumentation in environments\n * where automatic instrumentation doesn't work properly.\n *\n * @example\n * ```typescript\n * import OpenAI from 'openai';\n * import * as LlamaIndex from 'llamaindex';\n *\n * Brizz.initialize({\n * appName: 'my-app',\n * instrumentModules: {\n * openAI: OpenAI,\n * llamaindex: LlamaIndex\n * }\n * });\n * ```\n */\n instrumentModules?: IInstrumentModules;\n /**\n * Disable the internal NodeSDK initialization and only setup telemetry modules.\n * This is useful when you want to create your own NodeSDK using Brizz utilities.\n * Brizz will automatically register its instrumentations with your SDK.\n *\n * @example\n * ```typescript\n * import { NodeSDK } from '@opentelemetry/sdk-node';\n * import { Brizz, getSpanProcessor } from 'brizz-sdk';\n *\n * // Initialize Brizz without internal NodeSDK\n * Brizz.initialize({\n * appName: 'my-app',\n * disableNodeSdk: true\n * });\n *\n * // Create your own NodeSDK\n * const sdk = new NodeSDK({\n * spanProcessors: [getSpanProcessor()],\n * // your custom configuration\n * });\n * sdk.start();\n * ```\n */\n disableNodeSdk?: boolean;\n /**\n * Custom span exporter for testing or alternative backends.\n * When provided, this exporter will be used instead of the default OTLP exporter.\n * Useful for integration testing with InMemorySpanExporter or custom exporters.\n *\n * @example\n * ```typescript\n * import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';\n *\n * const spanExporter = new InMemorySpanExporter();\n * Brizz.initialize({\n * appName: 'test-app',\n * customSpanExporter: spanExporter\n * });\n *\n * // Later in tests\n * const spans = spanExporter.getFinishedSpans();\n * ```\n */\n customSpanExporter?: SpanExporter;\n /**\n * Custom log exporter for testing or alternative backends.\n * When provided, this exporter will be used instead of the default OTLP exporter.\n * Useful for integration testing with InMemoryLogExporter or custom exporters.\n *\n * @example\n * ```typescript\n * import { InMemoryLogExporter } from 'brizz-sdk/test-utils';\n *\n * const logExporter = new InMemoryLogExporter();\n * Brizz.initialize({\n * appName: 'test-app',\n * customLogExporter: logExporter\n * });\n *\n * // Later in tests\n * const logs = logExporter.getFinishedLogs();\n * ```\n */\n customLogExporter?: LogRecordExporter;\n /**\n * Custom metric reader for testing or alternative backends.\n * When provided, this reader will be used instead of the default OTLP exporter.\n * Useful for integration testing with InMemoryMetricReader or custom readers.\n *\n * @example\n * ```typescript\n * import { InMemoryMetricReader } from 'brizz-sdk/test-utils';\n *\n * const metricReader = new InMemoryMetricReader();\n * Brizz.initialize({\n * appName: 'test-app',\n * customMetricReader: metricReader\n * });\n *\n * // Later in tests\n * const metrics = metricReader.getCollectedMetrics();\n * ```\n */\n customMetricReader?: MetricReader;\n}\n\n/**\n * Internal implementation of the Brizz SDK.\n *\n * This class is responsible only for initialization and shutdown of the SDK.\n * All functionality is exposed through standalone utility functions.\n *\n * @class _Brizz\n * @internal\n */\nexport class _Brizz {\n private static instance: _Brizz | null = null;\n /** Flag indicating if SDK initialization has completed successfully */\n private _initialized = false;\n\n /** OpenTelemetry sdk instance */\n private _sdk: NodeSDK | null = null;\n\n static getInstance(): _Brizz {\n if (!_Brizz.instance) {\n throw new Error('Brizz must be initialized before accessing TracingModule');\n }\n return _Brizz.instance;\n }\n\n /**\n * Initialize the Brizz SDK with the provided configuration.\n *\n * @param {IBrizzInitializeOptions} options - Configuration options for the SDK\n * @throws {Error} - If initialization fails\n *\n * @example\n * ```typescript\n * Brizz.initialize({\n * appName: 'my-app',\n * baseUrl: 'http://localhost:4318',\n * apiKey: 'your-api-key'\n * });\n * ```\n */\n initialize(options: IBrizzInitializeOptions): void {\n if (this._initialized) {\n logger.warn('Brizz SDK already initialized');\n return;\n }\n\n logger.info('Starting Brizz SDK initialization');\n\n try {\n const resolvedConfig = resolveConfig(options);\n this.initializeModules(resolvedConfig);\n this.setupInstrumentation(options);\n this.createAndStartNodeSDK(options, resolvedConfig);\n\n this._initialized = true;\n logger.info('Brizz SDK initialization completed successfully', {\n moduleSystem: this.detectModuleSystem(),\n });\n } catch (error) {\n logger.error('Failed to initialize Brizz SDK', { error });\n throw new Error(`Failed to initialize SDK: ${String(error)}`);\n }\n }\n\n /**\n * Set up instrumentation registry and configure manual modules.\n * @private\n */\n private setupInstrumentation(options: IBrizzInitializeOptions): void {\n const registry = InstrumentationRegistry.getInstance();\n\n if (options.instrumentModules && Object.keys(options.instrumentModules).length > 0) {\n registry.setManualModules(options.instrumentModules);\n }\n }\n\n /**\n * Create and start NodeSDK if not disabled.\n * Only handles manual instrumentations now - auto instrumentations are loaded at import time.\n * @private\n */\n private createAndStartNodeSDK(\n options: IBrizzInitializeOptions,\n resolvedConfig: IResolvedBrizzConfig,\n ): void {\n if (options.disableNodeSdk) {\n logger.info('NodeSDK disabled - only telemetry modules initialized');\n return;\n }\n\n const registry = InstrumentationRegistry.getInstance();\n const manualInstrumentations = registry.getManualInstrumentations();\n\n this._sdk = new NodeSDK({\n spanProcessors: [getSpanProcessor()],\n metricReader: getMetricsReader(),\n resource: resourceFromAttributes({\n 'service.name': resolvedConfig.appName,\n }),\n instrumentations: manualInstrumentations,\n });\n this._sdk.start();\n\n if (manualInstrumentations.length > 0) {\n logger.info(`NodeSDK started with ${manualInstrumentations.length} manual instrumentations`);\n } else {\n logger.info('NodeSDK started - using auto-instrumentations loaded at import time');\n }\n }\n\n /**\n * Initialize telemetry modules.\n *\n * Sets up the tracing, metrics, and logging modules with their\n * respective exporters and processors.\n *\n * @param {IResolvedBrizzConfig} config - Resolved configuration\n * @private\n */\n private initializeModules(config: IResolvedBrizzConfig): void {\n logger.info('Initializing telemetry modules');\n\n // Initialize tracing module\n logger.debug('Initializing tracing module');\n const tracingModule = new TracingModule();\n tracingModule.setup(config);\n\n // Initialize metrics module\n logger.debug('Initializing metrics module');\n const metricsModule = new MetricsModule();\n metricsModule.setup(config);\n\n // Initialize logging module\n logger.debug('Initializing logging module');\n const loggingModule = new LoggingModule();\n loggingModule.setup(config);\n\n logger.info('All telemetry modules initialized successfully');\n }\n\n /**\n * Detect the current module system (ESM or CJS) using reliable indicators\n *\n * @returns {string} - 'ESM' or 'CJS'\n * @private\n */\n private detectModuleSystem(): string {\n const inCJS =\n typeof module !== 'undefined' &&\n typeof exports !== 'undefined' &&\n typeof require === 'function' &&\n typeof __filename === 'string' &&\n typeof __dirname === 'string' &&\n\n this === (typeof exports !== 'undefined' ? exports : undefined); // top-level `this` is exports in CJS, undefined in ESM\n\n return inCJS ? 'CJS' : 'ESM';\n }\n\n /**\n * Gracefully shutdown the Brizz SDK.\n *\n * This method stops all telemetry collection, flushes any pending data,\n * and releases resources. Should be called before application termination.\n *\n * @returns {Promise<void>} - Resolves when shutdown is complete\n * @throws {Error} - If shutdown fails\n *\n * @example\n * ```typescript\n * // Shutdown before app exit\n * await Brizz.shutdown();\n * ```\n */\n public async shutdown(): Promise<void> {\n if (!this._initialized) {\n logger.warn('Brizz SDK not initialized');\n return;\n }\n\n try {\n // Shutdown all modules\n await this.shutdownModules();\n await this._sdk?.shutdown();\n\n // Clear all references\n this._initialized = false;\n this._sdk = null;\n _Brizz.instance = null;\n\n logger.info('Brizz SDK shut down successfully');\n } catch (error) {\n // Suppress network errors during shutdown in tests\n if (error instanceof Error && error.message.includes('ENOTFOUND')) {\n logger.debug(`Network error during shutdown (expected in tests): ${error.message}`);\n } else {\n logger.error(`Failed to shutdown Brizz SDK: ${String(error)}`);\n throw error;\n }\n }\n }\n\n /**\n * Shutdown all telemetry modules.\n * @private\n */\n private async shutdownModules(): Promise<void> {\n logger.info('Shutting down telemetry modules');\n\n try {\n // Shutdown tracing module\n try {\n const tracingModule = TracingModule.getInstance();\n await tracingModule.shutdown();\n } catch {\n logger.debug('Tracing module already shut down or not initialized');\n }\n\n // Shutdown metrics module\n try {\n const metricsModule = MetricsModule.getInstance();\n metricsModule.shutdown();\n } catch {\n logger.debug('Metrics module already shut down or not initialized');\n }\n\n // Shutdown logging module\n try {\n const loggingModule = LoggingModule.getInstance();\n await loggingModule.shutdown();\n } catch {\n logger.debug('Logging module already shut down or not initialized');\n }\n\n logger.info('All telemetry modules shut down successfully');\n } catch (error) {\n logger.error('Error shutting down modules', { error });\n throw error;\n }\n }\n}\n\nexport const Brizz = new _Brizz();\n","import type { NodeSDK } from '@opentelemetry/sdk-node';\n\nimport type { IBrizzInitializeOptions } from '../index';\n\nimport { logger, DEFAULT_LOG_LEVEL, parseLogLevel, setLogLevel } from './logger';\nimport type { LogLevel } from './logger';\nimport type { IMaskingConfig } from './masking';\n\nexport interface IResolvedBrizzConfig extends Omit<IBrizzInitializeOptions, 'masking'> {\n appName: string;\n baseUrl: string;\n headers: Record<string, string>;\n disableBatch: boolean;\n apiKey?: string;\n logLevel: LogLevel;\n nodeSDK?: NodeSDK | null;\n masking?: IMaskingConfig;\n}\n\nexport function resolveConfig(options: IBrizzInitializeOptions): IResolvedBrizzConfig {\n // Resolve log level first so we can set it before other logging\n const envLogLevel = process.env['BRIZZ_LOG_LEVEL'] || options.logLevel?.toString() || DEFAULT_LOG_LEVEL.toString();\n let resolvedLogLevel: LogLevel;\n if (envLogLevel) {\n resolvedLogLevel = parseLogLevel(envLogLevel);\n } else if (typeof options.logLevel === 'string') {\n resolvedLogLevel = parseLogLevel(options.logLevel);\n } else {\n resolvedLogLevel = options.logLevel || DEFAULT_LOG_LEVEL;\n }\n\n setLogLevel(resolvedLogLevel);\n\n // Determine masking status for logging\n let maskingStatus: string;\n if (options.masking === true) {\n maskingStatus = 'enabled';\n } else if (options.masking === false) {\n maskingStatus = 'disabled';\n } else if (typeof options.masking === 'object') {\n maskingStatus = 'custom';\n } else {\n maskingStatus = 'default-disabled';\n }\n\n logger.debug('Starting configuration resolution', {\n appName: options.appName,\n baseUrl: options.baseUrl,\n hasApiKey: !!options.apiKey,\n disableBatch: options.disableBatch,\n logLevel: resolvedLogLevel,\n headersCount: Object.keys(options.headers || {}).length,\n masking: maskingStatus,\n });\n\n // Resolve masking configuration\n let resolvedMasking: IMaskingConfig | undefined;\n if (options.masking === true) {\n // Enable masking with all defaults\n resolvedMasking = {\n spanMasking: {},\n eventMasking: {},\n };\n } else if (options.masking && typeof options.masking === 'object') {\n // Use provided masking config\n resolvedMasking = options.masking;\n }\n // If options.masking is false or undefined, resolvedMasking remains undefined\n\n // Resolve all configuration with environment variables taking precedence\n const resolvedConfig: IResolvedBrizzConfig = {\n ...options,\n appName: process.env['BRIZZ_APP_NAME'] || options.appName || 'unknown-app',\n baseUrl: process.env['BRIZZ_BASE_URL'] || options.baseUrl || 'https://telemetry.brizz.dev',\n headers: { ...options.headers },\n apiKey: process.env['BRIZZ_API_KEY'] || options.apiKey,\n disableBatch: process.env['BRIZZ_DISABLE_BATCH'] === 'true' || !!options.disableBatch,\n logLevel: resolvedLogLevel,\n masking: resolvedMasking,\n };\n // Add Authorization header if API key is present\n if (resolvedConfig.apiKey) {\n resolvedConfig.headers['Authorization'] = `Bearer ${resolvedConfig.apiKey}`;\n }\n\n // Process additional environment variables\n if (process.env['BRIZZ_HEADERS']) {\n try {\n const envHeaders = JSON.parse(process.env['BRIZZ_HEADERS']) as Record<string, string>;\n Object.assign(resolvedConfig.headers, envHeaders);\n logger.debug('Added headers from environment variable', {\n headers: Object.keys(envHeaders),\n });\n } catch (error) {\n logger.error('Failed to parse BRIZZ_HEADERS environment variable', { error });\n throw new Error('Invalid JSON in BRIZZ_HEADERS environment variable');\n }\n }\n\n logger.debug('Configuration resolved with environment variables', {\n appName: resolvedConfig.appName,\n baseUrl: resolvedConfig.baseUrl,\n hasApiKey: !!resolvedConfig.apiKey,\n disableBatch: resolvedConfig.disableBatch,\n envOverrides: {\n hasEnvApiKey: !!process.env['BRIZZ_API_KEY'],\n hasEnvBaseUrl: !!process.env['BRIZZ_BASE_URL'],\n hasEnvBatch: !!process.env['BRIZZ_DISABLE_BATCH'],\n hasEnvHeaders: !!process.env['BRIZZ_HEADERS'],\n },\n });\n\n return resolvedConfig;\n}\n\n// Legacy function for backward compatibility\nexport function processEnvironmentConfig(options: IBrizzInitializeOptions): IResolvedBrizzConfig {\n return resolveConfig(options);\n}\n","import type { Instrumentation } from '@opentelemetry/instrumentation';\nimport { AnthropicInstrumentation } from '@traceloop/instrumentation-anthropic';\nimport { BedrockInstrumentation } from '@traceloop/instrumentation-bedrock';\nimport { ChromaDBInstrumentation } from '@traceloop/instrumentation-chromadb';\nimport { CohereInstrumentation } from '@traceloop/instrumentation-cohere';\nimport { LangChainInstrumentation } from '@traceloop/instrumentation-langchain';\nimport { LlamaIndexInstrumentation } from '@traceloop/instrumentation-llamaindex';\nimport { OpenAIInstrumentation } from '@traceloop/instrumentation-openai';\nimport { PineconeInstrumentation } from '@traceloop/instrumentation-pinecone';\nimport { QdrantInstrumentation } from '@traceloop/instrumentation-qdrant';\nimport { TogetherInstrumentation } from '@traceloop/instrumentation-together';\nimport { VertexAIInstrumentation } from '@traceloop/instrumentation-vertexai';\n\nimport { logger } from '../logger';\n\n/**\n * Module types for manual instrumentation (for Next.js/Webpack compatibility)\n */\nexport interface IInstrumentModules {\n openAI?: unknown;\n anthropic?: unknown;\n cohere?: unknown;\n google_vertexai?: unknown;\n google_aiplatform?: unknown;\n bedrock?: unknown;\n pinecone?: unknown;\n langchain?: {\n chains?: unknown;\n agents?: unknown;\n tools?: unknown;\n };\n llamaindex?: unknown;\n chromadb?: unknown;\n qdrant?: unknown;\n together?: unknown;\n vercelAI?: unknown;\n}\n\ninterface IInstrumentationConfig {\n class: new (config: { exceptionLogger: (error: Error) => void }) => Instrumentation;\n name: string;\n module?: unknown;\n}\n\nexport class InstrumentationRegistry {\n private static instance: InstrumentationRegistry;\n private manualModules: IInstrumentModules | null = null;\n\n public static getInstance(): InstrumentationRegistry {\n if (!InstrumentationRegistry.instance) {\n InstrumentationRegistry.instance = new InstrumentationRegistry();\n }\n return InstrumentationRegistry.instance;\n }\n\n /**\n * Set manual instrumentation modules for Next.js/Webpack environments\n */\n public setManualModules(modules: IInstrumentModules): void {\n this.manualModules = modules;\n logger.info('Manual instrumentation modules configured for Next.js/Webpack compatibility');\n }\n\n /**\n * Helper to load instrumentation with optional manual module\n */\n private loadInstrumentation<T extends Instrumentation>(\n InstrumentationClass: new (config: { exceptionLogger: (error: Error) => void }) => T,\n name: string,\n manualModule?: unknown,\n exceptionLogger?: (error: Error) => void,\n ): T | null {\n try {\n const inst = new InstrumentationClass({\n exceptionLogger:\n exceptionLogger ||\n ((error) => logger.error(`Exception in instrumentation: ${String(error)}`)),\n });\n if (manualModule) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (inst as any).manuallyInstrument(manualModule);\n logger.debug(`Manual instrumentation enabled for ${name}`);\n }\n return inst;\n } catch (error) {\n logger.error(`Failed to load ${name} instrumentation: ${String(error)}`);\n return null;\n }\n }\n\n /**\n * Get manual instrumentations only.\n * Auto-instrumentations are handled at import time.\n */\n public getManualInstrumentations(): Instrumentation[] {\n if (!this.manualModules || Object.keys(this.manualModules).length === 0) {\n logger.debug('No manual instrumentation modules configured');\n return [];\n }\n\n const instrumentations: Instrumentation[] = [];\n const exceptionLogger = (error: Error) => {\n logger.error(`Exception in manual instrumentation: ${String(error)}`);\n };\n\n this.loadManualInstrumentations(instrumentations, exceptionLogger);\n\n logger.info(`Loaded ${instrumentations.length} manual instrumentations`);\n return instrumentations;\n }\n\n /**\n * Load manual instrumentations only (with modules provided by user).\n * @private\n */\n private loadManualInstrumentations(\n instrumentations: Instrumentation[],\n exceptionLogger: (error: Error) => void,\n ): void {\n const instrumentationConfigs: IInstrumentationConfig[] = [\n { class: OpenAIInstrumentation, name: 'OpenAI', module: this.manualModules?.openAI },\n { class: AnthropicInstrumentation, name: 'Anthropic', module: this.manualModules?.anthropic },\n { class: CohereInstrumentation, name: 'Cohere', module: this.manualModules?.cohere },\n {\n class: VertexAIInstrumentation,\n name: 'Vertex AI',\n module: this.manualModules?.google_vertexai,\n },\n { class: BedrockInstrumentation, name: 'Bedrock', module: this.manualModules?.bedrock },\n { class: PineconeInstrumentation, name: 'Pinecone', module: this.manualModules?.pinecone },\n { class: LangChainInstrumentation, name: 'LangChain', module: this.manualModules?.langchain },\n {\n class: LlamaIndexInstrumentation,\n name: 'LlamaIndex',\n module: this.manualModules?.llamaindex,\n },\n { class: ChromaDBInstrumentation, name: 'ChromaDB', module: this.manualModules?.chromadb },\n { class: QdrantInstrumentation, name: 'Qdrant', module: this.manualModules?.qdrant },\n { class: TogetherInstrumentation, name: 'Together', module: this.manualModules?.together },\n ];\n\n // Only load instrumentations that have manual modules configured\n for (const config of instrumentationConfigs) {\n if (config.module) {\n const instrumentation = this.loadInstrumentation(\n config.class,\n config.name,\n config.module,\n exceptionLogger,\n );\n if (instrumentation) {\n instrumentations.push(instrumentation);\n }\n }\n }\n }\n}\n","import type { LogBody } from '@opentelemetry/api-logs';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport {\n LoggerProvider,\n type LogRecordExporter,\n type LogRecordProcessor,\n} from '@opentelemetry/sdk-logs';\n\nimport type { IResolvedBrizzConfig } from '../config';\nimport { logger } from '../logger';\n\nimport {\n BrizzBatchLogRecordProcessor,\n BrizzSimpleLogRecordProcessor,\n} from './processors/log-processor';\n\nexport class LoggingModule {\n private static instance: LoggingModule | null = null;\n private logExporter: LogRecordExporter | null = null;\n private logProcessor: LogRecordProcessor | null = null;\n private loggerProvider: LoggerProvider | null = null;\n\n static getInstance(): LoggingModule {\n if (!LoggingModule.instance) {\n throw new Error('Brizz must be initialized before accessing LoggingModule');\n }\n return LoggingModule.instance;\n }\n\n /**\n * Initialize the logging module with the provided configuration\n */\n setup(config: IResolvedBrizzConfig): void {\n logger.info('Setting up logging module');\n\n // Initialize exporter\n this.initLogExporter(config);\n\n // Initialize processor with masking support\n this.initLogProcessor(config);\n\n // Initialize logger provider\n this.initLoggerProvider(config);\n\n // Set as module instance for standalone functions\n LoggingModule.instance = this;\n\n logger.info('Logging module setup completed');\n }\n\n /**\n * Initialize the log exporter\n */\n private initLogExporter(config: IResolvedBrizzConfig): void {\n if (this.logExporter) {\n logger.debug('Log exporter already initialized, skipping re-initialization');\n return;\n }\n\n // Use custom log exporter if provided\n if (config.customLogExporter) {\n logger.debug('Using custom log exporter');\n this.logExporter = config.customLogExporter;\n logger.debug('Custom log exporter initialized successfully');\n return;\n }\n\n // Use default OTLP exporter\n const logsUrl = config.baseUrl.replace(/\\/$/, '') + '/v1/logs';\n logger.debug('Initializing default OTLP log exporter', { url: logsUrl });\n\n const headers = { ...config.headers };\n\n this.logExporter = new OTLPLogExporter({\n url: logsUrl,\n headers,\n });\n\n logger.debug('OTLP log exporter initialized successfully');\n }\n\n /**\n * Initialize the log processor with masking support\n */\n private initLogProcessor(config: IResolvedBrizzConfig): void {\n if (this.logProcessor) {\n logger.debug('Log processor already initialized, skipping re-initialization');\n return;\n }\n\n if (!this.logExporter) {\n throw new Error('Log exporter must be initialized before processor');\n }\n\n logger.debug('Initializing log processor', {\n disableBatch: config.disableBatch,\n hasMasking: !!config.masking?.eventMasking,\n });\n\n this.logProcessor = config.disableBatch\n ? new BrizzSimpleLogRecordProcessor(this.logExporter, config)\n : new BrizzBatchLogRecordProcessor(this.logExporter, config);\n\n logger.debug('Log processor initialized successfully');\n }\n\n /**\n * Initialize the logger provider\n */\n private initLoggerProvider(config: IResolvedBrizzConfig): void {\n if (this.loggerProvider) {\n logger.debug('Logger provider already initialized, skipping re-initialization');\n return;\n }\n\n if (!this.logProcessor) {\n throw new Error('Log processor must be initialized before logger provider');\n }\n\n logger.debug('Creating resource with service name', {\n serviceName: config.appName,\n });\n\n const resource = resourceFromAttributes({\n 'service.name': config.appName,\n });\n\n logger.debug('Creating logger provider with resource');\n this.loggerProvider = new LoggerProvider({\n resource,\n processors: [this.logProcessor],\n });\n\n logger.debug('Logger provider initialization completed');\n }\n\n /**\n * Emit a custom event to the telemetry pipeline\n */\n emitEvent(\n name: string,\n attributes?: Record<string, string | number | boolean>,\n body?: LogBody,\n severityNumber: SeverityNumber = SeverityNumber.INFO,\n ): void {\n logger.debug('Attempting to emit event', {\n name,\n hasAttributes: !!attributes,\n attributesCount: attributes ? Object.keys(attributes).length : 0,\n hasBody: !!body,\n severityNumber,\n });\n\n if (!this.loggerProvider) {\n logger.error('Cannot emit event: Logger provider not initialized');\n throw new Error('Logging module not initialized');\n }\n\n // Prepare log attributes with event name as required field\n const logAttributes: Record<string, string | number | boolean> = { 'event.name': name };\n if (attributes) {\n Object.assign(logAttributes, attributes);\n logger.debug('Combined log attributes', { attributes: Object.keys(logAttributes) });\n }\n\n // Get logger instance for event emission\n logger.debug('Getting logger instance for brizz_events');\n const eventLogger = this.loggerProvider.getLogger('brizz.events');\n\n // Emit the event\n logger.debug('Emitting log record with eventName', { eventName: name });\n try {\n eventLogger.emit({\n eventName: name,\n attributes: logAttributes,\n severityNumber: severityNumber,\n body: body,\n });\n logger.debug('Event successfully emitted', { eventName: name });\n } catch (error) {\n // Log detailed error information for debugging\n logger.error(`Failed to emit event '${name}'`, { error, eventName: name });\n logger.error('Log record that failed', {\n eventName: name,\n hasAttributes: logAttributes,\n severityNumber: severityNumber,\n hasBody: body,\n });\n throw error instanceof Error ? error : new Error(String(error));\n }\n }\n\n /**\n * Check if the module is initialized\n */\n isInitialized(): boolean {\n return this.loggerProvider !== null;\n }\n\n /**\n * Get the logger provider\n */\n getLoggerProvider(): LoggerProvider | null {\n return this.loggerProvider;\n }\n\n /**\n * Shutdown the logging module\n */\n async shutdown(): Promise<void> {\n logger.debug('Shutting down logging module');\n\n if (this.loggerProvider) {\n await this.loggerProvider.shutdown();\n this.loggerProvider = null;\n }\n\n this.logProcessor = null;\n this.logExporter = null;\n\n // Clear module instance\n if (LoggingModule.instance === this) {\n LoggingModule.instance = null;\n }\n\n logger.debug('Logging module shutdown completed');\n }\n}\n\n/**\n * Emit a custom event to the telemetry pipeline.\n * @throws {Error} - If SDK is not initialized\n */\nexport function emitEvent(\n name: string,\n attributes?: Record<string, string | number | boolean>,\n body?: LogBody,\n severityNumber: SeverityNumber = SeverityNumber.INFO,\n): void {\n return LoggingModule.getInstance().emitEvent(name, attributes, body, severityNumber);\n}\n","import { context } from '@opentelemetry/api';\nimport type { AnyValue, LogAttributes } from '@opentelemetry/api-logs';\nimport type { LogRecordExporter, SdkLogRecord } from '@opentelemetry/sdk-logs';\nimport { BatchLogRecordProcessor, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';\n\nimport type { IResolvedBrizzConfig } from '../../config';\nimport { logger } from '../../logger';\nimport { DEFAULT_PII_PATTERNS } from '../../masking/patterns';\nimport type { IAttributesMaskingRule, ILogMaskingConfig, MaskableValue } from '../../masking/types';\nimport { maskAttributes, maskValue } from '../../masking/utils';\nimport { BRIZZ, PROPERTIES_CONTEXT_KEY } from '../../semantic-conventions';\n\nexport const DEFAULT_LOG_MASKING_RULES: IAttributesMaskingRule[] = [\n {\n mode: 'partial',\n attributePattern: 'event.name',\n patterns: DEFAULT_PII_PATTERNS,\n },\n];\n\nexport class BrizzSimpleLogRecordProcessor extends SimpleLogRecordProcessor {\n public readonly config: IResolvedBrizzConfig;\n\n constructor(exporter: LogRecordExporter, config: IResolvedBrizzConfig) {\n super(exporter);\n this.config = config;\n }\n\n override onEmit(logRecord: SdkLogRecord): void {\n const maskingConfig = this.config.masking?.eventMasking;\n if (maskingConfig) {\n maskLog(logRecord, maskingConfig);\n }\n const associationProperties = context.active().getValue(PROPERTIES_CONTEXT_KEY);\n if (associationProperties) {\n for (const [key, value] of Object.entries(associationProperties)) {\n logRecord.setAttribute(`${BRIZZ}.${key}`, value as AnyValue);\n }\n }\n super.onEmit(logRecord);\n }\n}\n\nexport class BrizzBatchLogRecordProcessor extends BatchLogRecordProcessor {\n public readonly config: IResolvedBrizzConfig;\n\n constructor(exporter: LogRecordExporter, config: IResolvedBrizzConfig) {\n super(exporter);\n this.config = config;\n }\n\n override onEmit(logRecord: SdkLogRecord): void {\n const maskingConfig = this.config.masking?.eventMasking;\n if (maskingConfig) {\n maskLog(logRecord, maskingConfig);\n }\n const associationProperties = context.active().getValue(PROPERTIES_CONTEXT_KEY);\n if (associationProperties) {\n for (const [key, value] of Object.entries(associationProperties)) {\n logRecord.setAttribute(`${BRIZZ}.${key}`, value as AnyValue);\n }\n }\n super.onEmit(logRecord);\n }\n}\n\nexport function maskLog(logRecord: SdkLogRecord, config: ILogMaskingConfig): SdkLogRecord {\n if (!logRecord.attributes) {\n return logRecord;\n }\n\n let rules = config.rules || [];\n if (!config.disableDefaultRules) {\n rules = [...DEFAULT_LOG_MASKING_RULES, ...rules];\n }\n\n try {\n if (logRecord.attributes && Object.keys(logRecord.attributes).length > 0) {\n const attributesRecord: Record<string, MaskableValue> = {};\n\n for (const [key, value] of Object.entries(logRecord.attributes)) {\n attributesRecord[key] = value as MaskableValue;\n }\n\n const maskedAttributes = maskAttributes(attributesRecord, rules);\n if (maskedAttributes) {\n // Clear existing attributes and set masked ones\n const newAttributes: LogAttributes = {};\n for (const [key, value] of Object.entries(maskedAttributes)) {\n newAttributes[key] = value as AnyValue;\n }\n logRecord.setAttributes(newAttributes);\n }\n }\n\n if (config.maskBody && logRecord.body !== undefined && logRecord.body !== null) {\n let maskedBody: unknown = logRecord.body;\n for (const rule of rules) {\n maskedBody = maskValue(maskedBody, rule);\n }\n logRecord.setBody(maskedBody as AnyValue);\n }\n\n return logRecord;\n } catch (error) {\n logger.error('Error masking log record:', error);\n return logRecord;\n }\n}\n","/**\n * Default PII patterns for data masking.\n *\n * This module contains 100+ built-in patterns for detecting and masking\n * common types of personally identifiable information (PII) and sensitive data.\n * Patterns are organized by category and mirror the Python SDK implementation.\n */\n\nimport type { IPatternEntry } from './types';\n\n/**\n * All built-in PII patterns combined - mirrors Python SDK patterns.py\n */\nexport const DEFAULT_PII_PATTERNS: IPatternEntry[] = [\n // Email addresses\n {\n name: 'email_addresses',\n pattern: String.raw`\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b`,\n },\n // Phone numbers (US format)\n {\n name: 'us_phone_numbers',\n pattern: String.raw`(?:^|[\\s])(?:\\+?1[-.\\s]*)?(?:\\([0-9]{3}\\)\\s?[0-9]{3}[-.\\s]?[0-9]{4}|[0-9]{3}[-.\\s]?[0-9]{3}[-.\\s]?[0-9]{4}|[0-9]{10})(?=[\\s]|$)`,\n },\n // Social Security Numbers\n {\n name: 'ssn',\n pattern: String.raw`\\b(?!000|666|9\\d{2})\\d{3}[-\\s]?(?!00)\\d{2}[-\\s]?(?!0000)\\d{4}\\b`,\n },\n // Credit card numbers\n {\n name: 'credit_cards',\n pattern: String.raw`\\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\\\\d{3})\\\\d{11})\\b`,\n },\n {\n name: 'credit_cards_with_separators',\n pattern: String.raw`\\b(?:4\\\\d{3}|5[1-5]\\\\d{2}|3[47]\\\\d{2})[-\\s]?\\\\d{4}[-\\s]?\\\\d{4}[-\\s]?\\\\d{4}\\b`,\n },\n // IP addresses (IPv4)\n {\n name: 'ipv4_addresses',\n pattern: String.raw`\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?!\\.[0-9])\\b`,\n },\n // API keys/tokens\n {\n name: 'generic_api_keys',\n pattern: String.raw`\\b(?:[Aa][Pp][Ii][_-]?[Kk][Ee][Yy]|[Tt][Oo][Kk][Ee][Nn]|[Ss][Ee][Cc][Rr][Ee][Tt])[_-]?[=:]?\\s*[\"']?(?:[a-zA-Z0-9\\-_.]{20,})[\"']?\\b`,\n },\n {\n name: 'openai_keys',\n pattern: String.raw`\\bsk[-_][a-zA-Z0-9]{20,}\\b`,\n },\n {\n name: 'base64_secrets',\n pattern: String.raw`\\b[A-Za-z0-9+/]{64,}={0,2}\\b`,\n },\n // AWS Keys\n {\n name: 'aws_access_keys',\n pattern: String.raw`\\b(?:AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\\b`,\n },\n {\n name: 'aws_secret_keys',\n pattern: String.raw`\\b[A-Za-z0-9/+=]*[A-Z][A-Za-z0-9/+=]*[a-z][A-Za-z0-9/+=]*[/+=][A-Za-z0-9/+=]{30,}\\b`,\n },\n // GitHub tokens\n {\n name: 'github_tokens',\n pattern: String.raw`\\bghp_[a-zA-Z0-9]{36}\\b`,\n },\n // Slack tokens\n {\n name: 'slack_tokens',\n pattern: String.raw`\\bxox[baprs]-[0-9]{10,13}-[0-9]{10,13}-[a-zA-Z0-9]{24,34}\\b`,\n },\n // Stripe keys\n {\n name: 'stripe_keys',\n pattern: String.raw`\\b(?:sk|pk)_(?:test|live)_[a-zA-Z0-9]{24,}\\b`,\n },\n // JWT tokens\n {\n name: 'jwt_tokens',\n pattern: String.raw`\\beyJ[A-Za-z0-9_-]{2,}\\\\.[A-Za-z0-9_-]{2,}\\\\.[A-Za-z0-9_-]{2,}\\b`,\n },\n // MAC addresses\n {\n name: 'mac_addresses',\n pattern: St