UNPKG

@brizz/sdk

Version:

OpenTelemetry-based observability SDK for AI applications

1 lines 133 kB
{"version":3,"sources":["../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/loader.ts","../src/node/runtime.ts","../src/preload.ts"],"sourcesContent":["/**\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: String.raw`\\b(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}\\b`,\n },\n // IPv6 addresses\n {\n name: 'ipv6_addresses',\n pattern: String.raw`\\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\b`,\n },\n // Medical records\n {\n name: 'medical_record_numbers',\n pattern: String.raw`\\b(?:[Mm][Rr][Nn])[-\\s]?\\d{6,10}\\b`,\n },\n // Bitcoin addresses\n {\n name: 'bitcoin_addresses',\n pattern: String.raw`\\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\\b`,\n },\n // Ethereum addresses\n {\n name: 'ethereum_addresses',\n pattern: String.raw`\\b0x[a-fA-F0-9]{40}(?![a-fA-F0-9])\\b`,\n },\n // UUIDs\n {\n name: 'uuids',\n pattern: String.raw`\\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?![0-9a-fA-F-])\\b`,\n },\n // Database connection strings\n {\n name: 'database_connections',\n pattern: String.raw`\\b(?:[Mm][Oo][Nn][Gg][Oo][Dd][Bb]|[Pp][Oo][Ss][Tt][Gg][Rr][Ee][Ss]|[Mm][Yy][Ss][Qq][Ll]|[Rr][Ee][Dd][Ii][Ss]|[Mm][Ss][Ss][Qq][Ll]|[Oo][Rr][Aa][Cc][Ll][Ee]):\\\\/\\\\/[^\\\\s]+\\b`,\n },\n // Private keys\n {\n name: 'rsa_private_keys',\n pattern: '-----BEGIN (?:RSA )?PRIVATE KEY-----',\n },\n {\n name: 'pgp_private_keys',\n pattern: '-----BEGIN PGP PRIVATE KEY BLOCK-----',\n },\n {\n name: 'certificates',\n pattern: '-----BEGIN CERTIFICATE-----',\n },\n // Date of birth patterns\n {\n name: 'date_of_birth_us',\n pattern: String.raw`\\b(?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\\\d|3[01])[-/](?:19|20)\\\\d{2}\\b`,\n },\n {\n name: 'date_of_birth_iso',\n pattern: String.raw`\\b(?:19|20)\\\\d{2}[-/](?:0[1-9]|1[0-2])[-/](?:0[1-9]|[12]\\\\d|3[01])\\b`,\n },\n // US Identification Numbers\n {\n name: 'us_passport_numbers',\n pattern: String.raw`\\b[A-Z]?\\\\d{6,9}\\b`,\n },\n {\n name: 'drivers_license',\n pattern: String.raw`\\b[A-Z]{1,2}\\\\d{3,8}[-\\s]?\\\\d{2,5}[-\\s]?\\\\d{2,5}[-\\s]?\\\\d{1,5}[-\\s]?\\\\d?\\b`,\n },\n {\n name: 'bank_account_numbers',\n pattern: String.raw`\\b\\\\d{10,17}\\b`,\n },\n {\n name: 'aba_routing_numbers',\n pattern: String.raw`\\b((0[0-9])|(1[0-2])|(2[1-9])|(3[0-2])|(6[1-9])|(7[0-2])|80)([0-9]{7})\\b`,\n },\n {\n name: 'health_insurance_numbers',\n pattern: String.raw`\\b\\\\d{10}[A-Z]\\b`,\n },\n {\n name: 'employee_ids',\n pattern: String.raw`\\b(?:[Ee][Mm][Pp]|[Ee][Mm][Pp][Ll][Oo][Yy][Ee][Ee]|[Ee])[-\\s]?\\\\d{5,8}\\b`,\n },\n {\n name: 'tax_ein',\n pattern: String.raw`\\b\\\\d{2}-\\\\d{7}\\b`,\n },\n {\n name: 'medicare_beneficiary_id',\n pattern: String.raw`\\b[1-9][A-Z][A-Z0-9]\\\\d-[A-Z][A-Z0-9]\\\\d-[A-Z][A-Z0-9]\\\\d{2}\\b`,\n },\n {\n name: 'national_provider_id',\n pattern: String.raw`\\b1\\\\d{9}\\b`,\n },\n {\n name: 'dea_numbers',\n pattern: String.raw`\\b[A-Z]{2}\\\\d{7}\\b`,\n },\n {\n name: 'itin',\n pattern: String.raw`\\b9\\\\d{2}(?:[ \\\\-]?)[7,8]\\\\d(?:[ \\\\-]?)\\\\d{4}\\b`,\n },\n // Vehicle and Location\n {\n name: 'vin_numbers',\n pattern: String.raw`\\b[A-HJ-NPR-Z0-9]{17}\\b`,\n },\n {\n name: 'coordinates',\n pattern: String.raw`\\b[-+]?(?:[0-8]?\\\\d(?:\\\\.\\\\d+)?|90(?:\\\\.0+)?),\\\\s*[-+]?(?:1[0-7]\\\\d(?:\\\\.\\\\d+)?|180(?:\\\\.0+)?|[0-9]?\\\\d(?:\\\\.\\\\d+)?)\\b`,\n },\n {\n name: 'us_license_plates',\n pattern: String.raw`\\b[A-Z]{1,3}[-\\s]\\\\d{1,4}[A-Z]?\\b|\\b\\\\d{1,2}[A-Z]{1,3}\\\\d{1,4}\\b`,\n },\n {\n name: 'us_zip_codes',\n pattern: String.raw`\\b(\\\\d{5}-\\\\d{4}|\\\\d{5})\\b`,\n },\n {\n name: 'us_street_addresses',\n pattern: String.raw`\\b\\\\d{1,8}\\b[\\\\s\\\\S]{10,100}?\\b(AK|AL|AR|AZ|CA|CO|CT|DC|DE|FL|GA|HI|IA|ID|IL|IN|KS|KY|LA|MA|MD|ME|MI|MN|MO|MS|MT|NC|ND|NE|NH|NJ|NM|NV|NY|OH|OK|OR|PA|RI|SC|SD|TN|TX|UT|VA|VT|WA|WI|WV|WY)\\b\\\\s\\\\d{5}\\b`,\n },\n // International Banking\n {\n name: 'iban',\n pattern: String.raw`\\b[A-Z]{2}\\d{2}[A-Z0-9]{4}\\d{7}([A-Z0-9]?){0,16}\\b`,\n },\n {\n name: 'swift_bic',\n pattern: String.raw`\\b[A-Z]{4}[A-Z]{2}[A-Z0-9]{2}([A-Z0-9]{3})?\\b`,\n },\n // Additional API Keys and Tokens\n {\n name: 'google_oauth',\n pattern: String.raw`\\bya29\\\\.[a-zA-Z0-9_-]{60,}\\b`,\n },\n {\n name: 'firebase_tokens',\n pattern: String.raw`\\bAAAA[A-Za-z0-9_-]{100,}\\b`,\n },\n {\n name: 'google_app_ids',\n pattern: String.raw`\\b[0-9]+-\\w+\\.apps\\.googleusercontent\\.com\\b`,\n },\n {\n name: 'facebook_secrets',\n pattern: String.raw`\\b(facebook_secret|FACEBOOK_SECRET|facebook_app_secret|FACEBOOK_APP_SECRET)[a-z_ =\\\\s\"'\\\\:]{0,5}[^a-zA-Z0-9][a-f0-9]{32}[^a-zA-Z0-9]`,\n },\n {\n name: 'github_keys',\n pattern: String.raw`\\b(GITHUB_SECRET|GITHUB_KEY|github_secret|github_key|github_token|GITHUB_TOKEN|github_api_key|GITHUB_API_KEY)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-zA-Z0-9][a-zA-Z0-9]{40}[^a-zA-Z0-9]`,\n },\n {\n name: 'heroku_keys',\n pattern: String.raw`\\b(heroku_api_key|HEROKU_API_KEY|heroku_secret|HEROKU_SECRET)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-zA-Z0-9-]\\\\w{8}(?:-\\\\w{4}){3}-\\\\w{12}[^a-zA-Z0-9\\\\-]`,\n },\n {\n name: 'slack_api_keys',\n pattern: String.raw`\\b(slack_api_key|SLACK_API_KEY|slack_key|SLACK_KEY)[a-z_ =\\\\s\\\"'\\\\:]{0,10}[^a-f0-9][a-f0-9]{32}[^a-f0-9]`,\n },\n {\n name: 'slack_api_tokens',\n pattern: String.raw`\\b(xox[pb](?:-[a-zA-Z0-9]+){4,})\\b`,\n },\n // Extended Private Keys and Certificates\n {\n name: 'dsa_private_keys',\n pattern: String.raw`-----BEGIN DSA PRIVATE KEY-----(?:[a-zA-Z0-9\\+\\=\\/\"']|\\s)+?-----END DSA PRIVATE KEY-----`,\n },\n {\n name: 'ec_private_keys',\n pattern: String.raw`-----BEGIN (?:EC|ECDSA) PRIVATE KEY-----(?:[a-zA-Z0-9\\+\\=\\/\"']|\\s)+?-----END (?:EC|ECDSA) PRIVATE KEY-----`,\n },\n {\n name: 'encrypted_private_keys',\n pattern: String.raw`-----BEGIN ENCRYPTED PRIVATE KEY-----(?:.|\\s)+?-----END ENCRYPTED PRIVATE KEY-----`,\n },\n {\n name: 'ssl_certificates',\n pattern: String.raw`-----BEGIN CERTIFICATE-----(?:.|\\n)+?\\s-----END CERTIFICATE-----`,\n },\n {\n name: 'ssh_dss_public',\n p