From 64c7712da7de35ae338150038bbf2e2a9f3ac64d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Jan 2026 01:48:23 +0000 Subject: [PATCH] refactor(utils): translate all comments to English and enhance circuit breaker - Translate all Portuguese comments to English in utility modules - Enhance circuit-breaker.ts with sliding window failure tracking - Add factory functions for WhatsApp-specific circuit breakers (createPreKeyCircuitBreaker, createConnectionCircuitBreaker, createMessageCircuitBreaker) - Improve volume threshold support for circuit breaker decisions - Standardize documentation across all utility files Files updated: - structured-logger.ts: English comments - baileys-logger.ts: English comments - trace-context.ts: English comments - prometheus-metrics.ts: English comments - cache-utils.ts: English comments - circuit-breaker.ts: Enhanced with sliding window + English comments - retry-utils.ts: English comments - baileys-event-stream.ts: English comments - index.ts: English comments --- src/Utils/baileys-event-stream.ts | 171 ++++++------ src/Utils/baileys-logger.ts | 134 +++++---- src/Utils/cache-utils.ts | 97 +++---- src/Utils/circuit-breaker.ts | 443 ++++++++++++++++++++++-------- src/Utils/index.ts | 8 +- src/Utils/prometheus-metrics.ts | 179 ++++++------ src/Utils/retry-utils.ts | 149 +++++----- src/Utils/structured-logger.ts | 127 +++++---- src/Utils/trace-context.ts | 171 ++++++------ 9 files changed, 865 insertions(+), 614 deletions(-) diff --git a/src/Utils/baileys-event-stream.ts b/src/Utils/baileys-event-stream.ts index 85734206..2a62d18b 100644 --- a/src/Utils/baileys-event-stream.ts +++ b/src/Utils/baileys-event-stream.ts @@ -1,15 +1,16 @@ /** - * @fileoverview Gerenciamento de eventos do Baileys - * @module Utils/baileys-event-stream + * Baileys Event Stream Management * - * Fornece: - * - Event buffering com backpressure - * - Event transformation e filtering - * - Priority queues para eventos + * Provides: + * - Event buffering with backpressure + * - Event transformation and filtering + * - Priority queues for events * - Batch processing - * - Dead letter queue para eventos falhos - * - Replay de eventos - * - Integração com logging e métricas + * - Dead letter queue for failed events + * - Event replay + * - Logging and metrics integration + * + * @module Utils/baileys-event-stream */ import { EventEmitter } from 'events' @@ -17,7 +18,7 @@ import { metrics } from './prometheus-metrics.js' import type { BaileysLogCategory } from './baileys-logger.js' /** - * Tipos de eventos do Baileys + * Baileys event types */ export type BaileysEventType = | 'connection.update' @@ -41,15 +42,15 @@ export type BaileysEventType = | 'call' | 'blocklist.set' | 'blocklist.update' - | string // Para eventos customizados + | string // For custom events /** - * Prioridade de eventos + * Event priority */ export type EventPriority = 'critical' | 'high' | 'normal' | 'low' /** - * Valores numéricos de prioridade + * Numeric priority values */ const PRIORITY_VALUES: Record = { critical: 0, @@ -59,7 +60,7 @@ const PRIORITY_VALUES: Record = { } /** - * Evento do stream + * Stream event */ export interface StreamEvent { id: string @@ -74,48 +75,48 @@ export interface StreamEvent { } /** - * Opções do Event Stream + * Event Stream options */ export interface EventStreamOptions { - /** Tamanho máximo do buffer (default: 10000) */ + /** Maximum buffer size (default: 10000) */ maxBufferSize?: number - /** Se deve aplicar backpressure quando buffer cheio */ + /** Whether to apply backpressure when buffer is full */ enableBackpressure?: boolean - /** Limite de highWaterMark para backpressure */ + /** High water mark limit for backpressure */ highWaterMark?: number - /** Limite de lowWaterMark para retomar */ + /** Low water mark limit to resume */ lowWaterMark?: number - /** Tamanho do batch para processamento */ + /** Batch size for processing */ batchSize?: number - /** Intervalo de flush em ms (0 = desabilitado) */ + /** Flush interval in ms (0 = disabled) */ flushInterval?: number - /** Máximo de retries para eventos falhos */ + /** Maximum retries for failed events */ maxRetries?: number - /** Tamanho da dead letter queue */ + /** Dead letter queue size */ deadLetterQueueSize?: number - /** Se deve coletar métricas */ + /** Whether to collect metrics */ collectMetrics?: boolean - /** Nome do stream para métricas */ + /** Stream name for metrics */ streamName?: string } /** - * Handler de evento + * Event handler */ export type EventHandler = (event: StreamEvent) => void | Promise /** - * Filtro de evento + * Event filter */ export type EventFilter = (event: StreamEvent) => boolean /** - * Transformador de evento + * Event transformer */ export type EventTransformer = (event: StreamEvent) => StreamEvent /** - * Resultado de processamento de batch + * Batch processing result */ export interface BatchResult { processed: number @@ -124,7 +125,7 @@ export interface BatchResult { } /** - * Estatísticas do stream + * Stream statistics */ export interface EventStreamStats { bufferSize: number @@ -140,7 +141,7 @@ export interface EventStreamStats { } /** - * Mapeamento de tipo de evento para categoria + * Event type to category mapping */ const EVENT_CATEGORY_MAP: Record = { 'connection.update': 'connection', @@ -165,7 +166,7 @@ const EVENT_CATEGORY_MAP: Record = { } /** - * Prioridade padrão por tipo de evento + * Default priority by event type */ const EVENT_PRIORITY_MAP: Partial> = { 'connection.update': 'critical', @@ -178,14 +179,14 @@ const EVENT_PRIORITY_MAP: Partial> = { } /** - * Gera ID único para evento + * Generate unique event ID */ function generateEventId(): string { return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}` } /** - * Classe principal do Event Stream + * Main Event Stream class */ export class BaileysEventStream extends EventEmitter { private buffer: StreamEvent[] = [] @@ -217,7 +218,7 @@ export class BaileysEventStream extends EventEmitter { this.stats = this.createInitialStats() - // Iniciar flush periódico se configurado + // Start periodic flush if configured if (this.options.flushInterval > 0) { this.flushTimer = setInterval(() => this.flush(), this.options.flushInterval) } @@ -243,10 +244,10 @@ export class BaileysEventStream extends EventEmitter { } /** - * Adiciona evento ao stream + * Add event to stream */ push(type: BaileysEventType, data: T, options?: { priority?: EventPriority; metadata?: Record }): boolean { - // Verificar backpressure + // Check backpressure if (this.options.enableBackpressure && this.buffer.length >= this.options.highWaterMark) { this.stats.isBackpressured = true this.emit('backpressure', { bufferSize: this.buffer.length }) @@ -274,23 +275,23 @@ export class BaileysEventStream extends EventEmitter { retryCount: 0, } - // Aplicar transformadores + // Apply transformers let transformedEvent: StreamEvent = event for (const transformer of this.transformers) { transformedEvent = transformer(transformedEvent) } - // Aplicar filtros + // Apply filters for (const filter of this.filters) { if (!filter(transformedEvent)) { return false } } - // Adicionar ao buffer na posição correta (por prioridade) + // Add to buffer at correct position (by priority) this.insertByPriority(transformedEvent) - // Atualizar estatísticas + // Update statistics this.stats.totalReceived++ this.stats.bufferSize = this.buffer.length this.stats.lastEventTimestamp = Date.now() @@ -303,7 +304,7 @@ export class BaileysEventStream extends EventEmitter { this.emit('event', transformedEvent) - // Processar se não estiver pausado + // Process if not paused if (!this.paused && !this.isProcessing) { this.processNext() } @@ -312,12 +313,12 @@ export class BaileysEventStream extends EventEmitter { } /** - * Insere evento no buffer por prioridade + * Insert event in buffer by priority */ private insertByPriority(event: StreamEvent): void { const eventPriorityValue = PRIORITY_VALUES[event.priority] - // Encontrar posição correta + // Find correct position let insertIndex = this.buffer.length for (let i = 0; i < this.buffer.length; i++) { if (PRIORITY_VALUES[this.buffer[i].priority] > eventPriorityValue) { @@ -330,7 +331,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Registra handler para tipo de evento + * Register handler for event type */ on(event: BaileysEventType | '*', handler: EventHandler): this { if (!this.handlers.has(event)) { @@ -352,7 +353,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Registra handler único + * Register single-use handler */ once(event: BaileysEventType, handler: EventHandler): this { const wrappedHandler: EventHandler = (e) => { @@ -363,7 +364,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Adiciona filtro + * Add filter */ addFilter(filter: EventFilter): this { this.filters.push(filter) @@ -371,7 +372,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Remove filtro + * Remove filter */ removeFilter(filter: EventFilter): this { const index = this.filters.indexOf(filter) @@ -382,7 +383,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Adiciona transformador + * Add transformer */ addTransformer(transformer: EventTransformer): this { this.transformers.push(transformer) @@ -390,7 +391,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Processa próximos eventos + * Process next events */ private async processNext(): Promise { if (this.isProcessing || this.paused || this.buffer.length === 0) { @@ -400,17 +401,17 @@ export class BaileysEventStream extends EventEmitter { this.isProcessing = true try { - // Pegar batch de eventos + // Get batch of events const batch = this.buffer.splice(0, this.options.batchSize) this.stats.bufferSize = this.buffer.length - // Verificar se saiu de backpressure + // Check if exited backpressure if (this.stats.isBackpressured && this.buffer.length <= this.options.lowWaterMark) { this.stats.isBackpressured = false this.emit('drain') } - // Processar batch + // Process batch const startTime = Date.now() let processed = 0 let failed = 0 @@ -430,7 +431,7 @@ export class BaileysEventStream extends EventEmitter { const duration = Date.now() - startTime this.emit('batch-processed', { processed, failed, duration } as BatchResult) - // Continuar processando se houver mais + // Continue processing if there are more if (this.buffer.length > 0) { setImmediate(() => this.processNext()) } @@ -440,10 +441,10 @@ export class BaileysEventStream extends EventEmitter { } /** - * Processa um evento + * Process a single event */ private async processEvent(event: StreamEvent): Promise { - // Handlers específicos do tipo + // Type-specific handlers const typeHandlers = this.handlers.get(event.type) if (typeHandlers) { for (const handler of typeHandlers) { @@ -451,7 +452,7 @@ export class BaileysEventStream extends EventEmitter { } } - // Handlers globais + // Global handlers const globalHandlers = this.handlers.get('*') if (globalHandlers) { for (const handler of globalHandlers) { @@ -461,13 +462,13 @@ export class BaileysEventStream extends EventEmitter { } /** - * Trata evento que falhou + * Handle failed event */ private async handleFailedEvent(event: StreamEvent, error: Error): Promise { event.retryCount = (event.retryCount || 0) + 1 if (event.retryCount <= this.options.maxRetries) { - // Re-adicionar ao buffer para retry + // Re-add to buffer for retry event.originalTimestamp = event.originalTimestamp || event.timestamp event.timestamp = Date.now() this.buffer.push(event) @@ -475,7 +476,7 @@ export class BaileysEventStream extends EventEmitter { this.emit('retry', { event, error, attempt: event.retryCount }) } else { - // Enviar para dead letter queue + // Send to dead letter queue this.addToDeadLetterQueue(event, error) } @@ -485,7 +486,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Adiciona evento à dead letter queue + * Add event to dead letter queue */ private addToDeadLetterQueue(event: StreamEvent, error: Error): void { const dlqEvent = { @@ -500,7 +501,7 @@ export class BaileysEventStream extends EventEmitter { this.deadLetterQueue.push(dlqEvent) - // Limitar tamanho da DLQ + // Limit DLQ size while (this.deadLetterQueue.length > this.options.deadLetterQueueSize) { this.deadLetterQueue.shift() } @@ -510,7 +511,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Força flush do buffer + * Force flush the buffer */ async flush(): Promise { const startTime = Date.now() @@ -543,7 +544,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Pausa o processamento + * Pause processing */ pause(): void { this.paused = true @@ -551,7 +552,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Resume o processamento + * Resume processing */ resume(): void { this.paused = false @@ -560,14 +561,14 @@ export class BaileysEventStream extends EventEmitter { } /** - * Verifica se está pausado + * Check if paused */ isPaused(): boolean { return this.paused } /** - * Limpa o buffer + * Clear the buffer */ clear(): void { this.buffer = [] @@ -576,14 +577,14 @@ export class BaileysEventStream extends EventEmitter { } /** - * Retorna eventos da dead letter queue + * Return dead letter queue events */ getDeadLetterQueue(): StreamEvent[] { return [...this.deadLetterQueue] } /** - * Limpa dead letter queue + * Clear dead letter queue */ clearDeadLetterQueue(): void { this.deadLetterQueue = [] @@ -591,7 +592,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Replay eventos da dead letter queue + * Replay dead letter queue events */ async replayDeadLetterQueue(): Promise { const events = this.deadLetterQueue.splice(0) @@ -625,14 +626,14 @@ export class BaileysEventStream extends EventEmitter { } /** - * Retorna estatísticas + * Return statistics */ getStats(): EventStreamStats { return { ...this.stats } } /** - * Reseta estatísticas + * Reset statistics */ resetStats(): void { this.stats = this.createInitialStats() @@ -641,7 +642,7 @@ export class BaileysEventStream extends EventEmitter { } /** - * Destroy e limpa recursos + * Destroy and clean up resources */ destroy(): void { if (this.flushTimer) { @@ -657,47 +658,47 @@ export class BaileysEventStream extends EventEmitter { } /** - * Factory para criar event stream + * Factory to create event stream */ export function createEventStream(options?: EventStreamOptions): BaileysEventStream { return new BaileysEventStream(options) } /** - * Filtros pré-definidos + * Pre-defined filters */ export const eventFilters = { - /** Filtra por tipo de evento */ + /** Filter by event type */ byType: (...types: BaileysEventType[]): EventFilter => (event) => types.includes(event.type), - /** Filtra por categoria */ + /** Filter by category */ byCategory: (...categories: BaileysLogCategory[]): EventFilter => (event) => categories.includes(event.category), - /** Filtra por prioridade mínima */ + /** Filter by minimum priority */ byMinPriority: (minPriority: EventPriority): EventFilter => (event) => PRIORITY_VALUES[event.priority] <= PRIORITY_VALUES[minPriority], - /** Filtra eventos recentes (dentro de ms) */ + /** Filter recent events (within ms) */ recentOnly: (maxAgeMs: number): EventFilter => (event) => Date.now() - event.timestamp <= maxAgeMs, - /** Combina filtros com AND */ + /** Combine filters with AND */ and: (...filters: EventFilter[]): EventFilter => (event) => filters.every((f) => f(event)), - /** Combina filtros com OR */ + /** Combine filters with OR */ or: (...filters: EventFilter[]): EventFilter => (event) => @@ -705,10 +706,10 @@ export const eventFilters = { } /** - * Transformadores pré-definidos + * Pre-defined transformers */ export const eventTransformers = { - /** Adiciona timestamp de processamento */ + /** Add processing timestamp */ addProcessingTimestamp: (): EventTransformer => (event) => ({ ...event, metadata: { @@ -717,7 +718,7 @@ export const eventTransformers = { }, }), - /** Adiciona ID de trace */ + /** Add trace ID */ addTraceId: (traceIdGenerator: () => string): EventTransformer => (event) => ({ @@ -728,7 +729,7 @@ export const eventTransformers = { }, }), - /** Eleva prioridade baseado em condição */ + /** Elevate priority based on condition */ elevatepriorityIf: (condition: (event: StreamEvent) => boolean, newPriority: EventPriority): EventTransformer => (event) => diff --git a/src/Utils/baileys-logger.ts b/src/Utils/baileys-logger.ts index ea0157c4..54400338 100644 --- a/src/Utils/baileys-logger.ts +++ b/src/Utils/baileys-logger.ts @@ -1,60 +1,61 @@ /** - * @fileoverview Logger customizado específico para Baileys - * @module Utils/baileys-logger + * Custom Logger for Baileys/WhatsApp * - * Fornece: - * - Logger pré-configurado para contexto Baileys/WhatsApp - * - Categorização de eventos por tipo (connection, message, media, etc.) - * - Filtros específicos para reduzir ruído - * - Métricas de eventos WhatsApp - * - Formatação otimizada para debugging + * Provides: + * - Pre-configured logger for Baileys/WhatsApp context + * - Event categorization by type (connection, message, media, etc.) + * - Specific filters to reduce noise + * - WhatsApp event metrics + * - Optimized formatting for debugging + * + * @module Utils/baileys-logger */ import type { ILogger } from './logger.js' import { StructuredLogger, createStructuredLogger, type LogLevel, type LogEntry } from './structured-logger.js' /** - * Categorias de log específicas do Baileys + * Baileys-specific log categories */ export type BaileysLogCategory = - | 'connection' // Eventos de conexão WebSocket - | 'auth' // Autenticação e QR code - | 'message' // Envio/recebimento de mensagens - | 'media' // Upload/download de mídia - | 'group' // Operações de grupo - | 'presence' // Status de presença - | 'call' // Chamadas de voz/vídeo - | 'sync' // Sincronização de dados - | 'encryption' // Operações de criptografia - | 'retry' // Retentativas de operações - | 'socket' // Eventos de socket baixo nível - | 'binary' // Codificação/decodificação binária - | 'unknown' // Categoria desconhecida + | 'connection' // WebSocket connection events + | 'auth' // Authentication and QR code + | 'message' // Message send/receive + | 'media' // Media upload/download + | 'group' // Group operations + | 'presence' // Presence status + | 'call' // Voice/video calls + | 'sync' // Data synchronization + | 'encryption' // Encryption operations + | 'retry' // Operation retries + | 'socket' // Low-level socket events + | 'binary' // Binary encoding/decoding + | 'unknown' // Unknown category /** - * Configuração do Baileys Logger + * Baileys Logger configuration */ export interface BaileysLoggerConfig { - /** Nível de log padrão */ + /** Default log level */ level: LogLevel - /** Categorias a serem ignoradas */ + /** Categories to ignore */ ignoredCategories?: BaileysLogCategory[] - /** Categorias com nível de log elevado (debug sempre) */ + /** Categories with elevated log level (always debug) */ verboseCategories?: BaileysLogCategory[] - /** Se deve logar payloads de mensagens (pode ser sensível) */ + /** Whether to log message payloads (may be sensitive) */ logMessagePayloads?: boolean - /** Se deve logar dados binários em hex */ + /** Whether to log binary data in hex */ logBinaryData?: boolean - /** Prefixo para identificar instância */ + /** Instance identifier prefix */ instanceId?: string - /** Handler para eventos específicos */ + /** Handler for specific events */ eventHandler?: (category: BaileysLogCategory, entry: LogEntry) => void - /** Limite de tamanho para payloads logados (bytes) */ + /** Size limit for logged payloads (bytes) */ maxPayloadSize?: number } /** - * Métricas específicas do Baileys + * Baileys-specific metrics */ export interface BaileysLoggerMetrics { connectionAttempts: number @@ -72,7 +73,7 @@ export interface BaileysLoggerMetrics { } /** - * Padrões para detectar categoria de log + * Patterns to detect log category */ const CATEGORY_PATTERNS: Array<{ pattern: RegExp; category: BaileysLogCategory }> = [ { pattern: /connect|disconnect|socket|ws|websocket|open|close/i, category: 'connection' }, @@ -89,7 +90,18 @@ const CATEGORY_PATTERNS: Array<{ pattern: RegExp; category: BaileysLogCategory } ] /** - * Logger customizado para Baileys + * Custom logger for Baileys + * + * @example + * ```typescript + * const logger = createBaileysLogger({ + * level: 'debug', + * instanceId: 'session-1' + * }) + * + * logger.logConnection('connected', { duration: 1500 }) + * logger.logMessage('send', 'text', 'user@s.whatsapp.net') + * ``` */ export class BaileysLogger implements ILogger { private structuredLogger: StructuredLogger @@ -162,7 +174,7 @@ export class BaileysLogger implements ILogger { } /** - * Cria logger filho com contexto adicional + * Create child logger with additional context */ child(obj: Record): BaileysLogger { const childLogger = new BaileysLogger(this.config) @@ -171,7 +183,7 @@ export class BaileysLogger implements ILogger { } /** - * Detecta categoria do log baseado no conteúdo + * Detect log category based on content */ private detectCategory(obj: unknown, msg?: string): BaileysLogCategory { const searchText = [ @@ -190,14 +202,14 @@ export class BaileysLogger implements ILogger { } /** - * Verifica se categoria deve ser logada + * Check if category should be logged */ - private shouldLogCategory(category: BaileysLogCategory, level: LogLevel): boolean { + private shouldLogCategory(category: BaileysLogCategory, _level: LogLevel): boolean { if (this.config.ignoredCategories.includes(category)) { return false } - // Categorias verbose sempre logam em debug ou superior + // Verbose categories always log at debug or higher if (this.config.verboseCategories.includes(category)) { return true } @@ -206,14 +218,14 @@ export class BaileysLogger implements ILogger { } /** - * Sanitiza payload de mensagem + * Sanitize message payload */ private sanitizePayload(obj: unknown): unknown { if (!this.config.logMessagePayloads) { if (typeof obj === 'object' && obj !== null) { const sanitized = { ...(obj as Record) } - // Remover campos sensíveis de mensagem + // Remove sensitive message fields const sensitiveFields = ['body', 'text', 'content', 'caption', 'payload', 'data'] for (const field of sensitiveFields) { if (field in sanitized) { @@ -230,7 +242,7 @@ export class BaileysLogger implements ILogger { } } - // Limitar tamanho do payload + // Limit payload size if (typeof obj === 'object' && obj !== null) { const str = JSON.stringify(obj) if (str.length > this.config.maxPayloadSize) { @@ -246,7 +258,7 @@ export class BaileysLogger implements ILogger { } /** - * Atualiza métricas baseado no log + * Update metrics based on log */ private updateMetrics(category: BaileysLogCategory, level: LogLevel, obj: unknown): void { const objStr = typeof obj === 'object' ? JSON.stringify(obj) : String(obj) @@ -296,7 +308,7 @@ export class BaileysLogger implements ILogger { } /** - * Método principal de log + * Main log method */ private log(level: LogLevel, obj: unknown, msg?: string): void { const category = this.detectCategory(obj, msg) @@ -305,13 +317,13 @@ export class BaileysLogger implements ILogger { return } - // Atualizar métricas + // Update metrics this.updateMetrics(category, level, obj) - // Sanitizar payload + // Sanitize payload const sanitizedObj = this.sanitizePayload(obj) - // Adicionar contexto do Baileys + // Add Baileys context const enrichedObj = { category, instanceId: this.config.instanceId, @@ -319,10 +331,10 @@ export class BaileysLogger implements ILogger { ...(typeof sanitizedObj === 'object' && sanitizedObj !== null ? sanitizedObj : { value: sanitizedObj }), } - // Log estruturado + // Structured log this.structuredLogger[level](enrichedObj, msg) - // Handler de evento + // Event handler if (this.config.eventHandler) { const entry: LogEntry = { timestamp: new Date().toISOString(), @@ -357,7 +369,7 @@ export class BaileysLogger implements ILogger { } /** - * Log de conexão específico + * Log connection-specific event */ logConnection(event: 'connecting' | 'connected' | 'disconnected' | 'error', details?: Record): void { const level: LogLevel = event === 'error' ? 'error' : event === 'disconnected' ? 'warn' : 'info' @@ -365,7 +377,7 @@ export class BaileysLogger implements ILogger { } /** - * Log de mensagem específico + * Log message-specific event */ logMessage( direction: 'send' | 'receive', @@ -388,7 +400,7 @@ export class BaileysLogger implements ILogger { } /** - * Log de mídia específico + * Log media-specific event */ logMedia( operation: 'upload' | 'download', @@ -411,11 +423,11 @@ export class BaileysLogger implements ILogger { } /** - * Sanitiza JID para log (remove parte do número) + * Sanitize JID for logging (mask part of number) */ private sanitizeJid(jid: string): string { if (process.env.NODE_ENV === 'production') { - // Em produção, mascara parte do número + // In production, mask part of the number const parts = jid.split('@') if (parts.length === 2 && parts[0].length > 4) { return `${parts[0].substring(0, 4)}****@${parts[1]}` @@ -425,7 +437,7 @@ export class BaileysLogger implements ILogger { } /** - * Formata bytes para leitura humana + * Format bytes for human readability */ private formatBytes(bytes: number): string { if (bytes === 0) return '0 B' @@ -436,21 +448,21 @@ export class BaileysLogger implements ILogger { } /** - * Retorna métricas do logger + * Get logger metrics */ getMetrics(): BaileysLoggerMetrics { return { ...this.metrics } } /** - * Retorna métricas do structured logger interno + * Get internal structured logger metrics */ getStructuredMetrics() { return this.structuredLogger.getMetrics() } /** - * Reseta métricas + * Reset metrics */ resetMetrics(): void { this.metrics = this.createInitialMetrics() @@ -458,7 +470,7 @@ export class BaileysLogger implements ILogger { } /** - * Retorna instance ID + * Get instance ID */ getInstanceId(): string { return this.config.instanceId @@ -466,14 +478,14 @@ export class BaileysLogger implements ILogger { } /** - * Factory para criar Baileys Logger + * Factory to create Baileys Logger */ export function createBaileysLogger(config?: Partial): BaileysLogger { return new BaileysLogger(config) } /** - * Singleton para logger padrão do Baileys + * Default Baileys logger singleton */ let defaultBaileysLogger: BaileysLogger | null = null diff --git a/src/Utils/cache-utils.ts b/src/Utils/cache-utils.ts index 6ffc04b3..22772d22 100644 --- a/src/Utils/cache-utils.ts +++ b/src/Utils/cache-utils.ts @@ -1,44 +1,45 @@ /** - * @fileoverview Sistema de cache inteligente - * @module Utils/cache-utils + * Smart Cache System * - * Fornece: - * - Cache em memória com TTL configurável - * - Invalidação automática e manual - * - Métricas de hit/miss - * - Estratégia LRU (Least Recently Used) - * - Cache distribuído (preparado para Redis) - * - Namespace para isolamento - * - Serialização customizável + * Provides: + * - In-memory cache with configurable TTL + * - Automatic and manual invalidation + * - Hit/miss metrics + * - LRU (Least Recently Used) strategy + * - Distributed cache (prepared for Redis) + * - Namespace for isolation + * - Customizable serialization + * + * @module Utils/cache-utils */ import { LRUCache } from 'lru-cache' import { metrics } from './prometheus-metrics.js' /** - * Opções de configuração do cache + * Cache configuration options */ export interface CacheOptions { - /** Tempo de vida em ms (default: 5 minutos) */ + /** Time to live in ms (default: 5 minutes) */ ttl?: number - /** Tamanho máximo do cache (default: 1000) */ + /** Maximum cache size (default: 1000) */ maxSize?: number - /** Função para calcular tamanho de um item */ + /** Function to calculate item size */ sizeCalculation?: (value: V) => number - /** Se deve atualizar TTL no acesso */ + /** Whether to update TTL on access */ updateAgeOnGet?: boolean - /** Namespace para isolamento */ + /** Namespace for isolation */ namespace?: string - /** Callback quando item expira */ + /** Callback when item expires */ onExpire?: (key: string, value: V) => void - /** Se deve coletar métricas */ + /** Whether to collect metrics */ collectMetrics?: boolean - /** Nome do cache para métricas */ + /** Cache name for metrics */ metricName?: string } /** - * Estatísticas do cache + * Cache statistics */ export interface CacheStats { hits: number @@ -49,7 +50,7 @@ export interface CacheStats { } /** - * Item do cache com metadados + * Cache item with metadata */ export interface CacheItem { value: V @@ -60,7 +61,7 @@ export interface CacheItem { } /** - * Resultado de operação de cache + * Cache operation result */ export interface CacheResult { value: V | undefined @@ -70,7 +71,7 @@ export interface CacheResult { } /** - * Classe principal do Cache + * Main Cache class */ export class Cache { private cache: LRUCache> @@ -105,7 +106,7 @@ export class Cache { } /** - * Obtém valor do cache + * Get value from cache */ get(key: string): V | undefined { const fullKey = this.getFullKey(key) @@ -132,7 +133,7 @@ export class Cache { } /** - * Obtém valor com resultado detalhado + * Get value with detailed result */ getWithResult(key: string): CacheResult { const fullKey = this.getFullKey(key) @@ -167,7 +168,7 @@ export class Cache { } /** - * Define valor no cache + * Set value in cache */ set(key: string, value: V, ttl?: number): void { const fullKey = this.getFullKey(key) @@ -190,7 +191,7 @@ export class Cache { } /** - * Verifica se chave existe + * Check if key exists */ has(key: string): boolean { const fullKey = this.getFullKey(key) @@ -198,7 +199,7 @@ export class Cache { } /** - * Remove item do cache + * Remove item from cache */ delete(key: string): boolean { const fullKey = this.getFullKey(key) @@ -212,7 +213,7 @@ export class Cache { } /** - * Limpa todo o cache + * Clear the entire cache */ clear(): void { this.cache.clear() @@ -224,7 +225,7 @@ export class Cache { } /** - * Obtém ou define valor (cache-aside pattern) + * Get or set value (cache-aside pattern) */ async getOrSet(key: string, factory: () => V | Promise, ttl?: number): Promise { const existing = this.get(key) @@ -238,7 +239,7 @@ export class Cache { } /** - * Obtém ou define valor síncronamente + * Get or set value synchronously */ getOrSetSync(key: string, factory: () => V, ttl?: number): V { const existing = this.get(key) @@ -252,7 +253,7 @@ export class Cache { } /** - * Invalida itens por padrão + * Invalidate items by pattern */ invalidateByPattern(pattern: RegExp): number { let count = 0 @@ -274,14 +275,14 @@ export class Cache { } /** - * Invalida itens por prefixo + * Invalidate items by prefix */ invalidateByPrefix(prefix: string): number { return this.invalidateByPattern(new RegExp(`^${prefix}`)) } /** - * Retorna estatísticas do cache + * Return cache statistics */ getStats(): CacheStats { const total = this.stats.hits + this.stats.misses @@ -295,14 +296,14 @@ export class Cache { } /** - * Retorna tamanho atual + * Return current size */ get size(): number { return this.cache.size } /** - * Retorna todas as chaves + * Return all keys */ keys(): string[] { const prefix = `${this.namespace}:` @@ -310,14 +311,14 @@ export class Cache { } /** - * Retorna todos os valores + * Return all values */ values(): V[] { return Array.from(this.cache.values()).map((item) => item.value) } /** - * Retorna todos os itens com metadados + * Return all items with metadata */ entries(): Array<{ key: string; item: CacheItem }> { const prefix = `${this.namespace}:` @@ -328,7 +329,7 @@ export class Cache { } /** - * Atualiza TTL de um item + * Update TTL of an item */ touch(key: string, ttl?: number): boolean { const fullKey = this.getFullKey(key) @@ -345,7 +346,7 @@ export class Cache { } /** - * Obtém item expirado (se ainda em memória) + * Get expired item (if still in memory) */ peek(key: string): V | undefined { const fullKey = this.getFullKey(key) @@ -359,14 +360,14 @@ export class Cache { } /** - * Factory para criar cache com tipo + * Factory to create typed cache */ export function createCache(options?: CacheOptions): Cache { return new Cache(options) } /** - * Cache com múltiplos níveis (L1: memória, L2: externo) + * Multi-level cache (L1: memory, L2: external) */ export class MultiLevelCache { private l1: Cache @@ -389,17 +390,17 @@ export class MultiLevelCache { } async get(key: string): Promise { - // Tentar L1 primeiro + // Try L1 first const l1Value = this.l1.get(key) if (l1Value !== undefined) { return l1Value } - // Tentar L2 se disponível + // Try L2 if available if (this.l2) { const l2Value = await this.l2.get(key) if (l2Value !== undefined) { - // Promover para L1 + // Promote to L1 this.l1.set(key, l2Value) return l2Value } @@ -433,7 +434,7 @@ export class MultiLevelCache { } /** - * Decorator para cachear resultado de método + * Decorator to cache method result */ export function cached(options: CacheOptions & { keyGenerator?: (...args: unknown[]) => string } = {}) { const cache = new Cache(options) @@ -465,7 +466,7 @@ export function cached(options: CacheOptions & { keyGenerator?: (...args: } /** - * Wrapper para função com cache + * Wrapper for function with cache */ export function withCache unknown>( fn: T, @@ -497,7 +498,7 @@ export function withCache unknown>( } /** - * Cache global singleton por namespace + * Global singleton cache by namespace */ const globalCaches: Map> = new Map() diff --git a/src/Utils/circuit-breaker.ts b/src/Utils/circuit-breaker.ts index f2caa7be..fc20e135 100644 --- a/src/Utils/circuit-breaker.ts +++ b/src/Utils/circuit-breaker.ts @@ -1,66 +1,85 @@ /** - * @fileoverview Proteção contra falhas em cascata - Circuit Breaker - * @module Utils/circuit-breaker + * Circuit Breaker Pattern Implementation * - * Fornece: - * - Estados: Closed, Open, Half-Open - * - Thresholds configuráveis - * - Recuperação automática - * - Callbacks de estado - * - Integração com métricas - * - Fallback handlers + * Provides protection against cascading failures by monitoring operation outcomes + * and temporarily blocking requests when failure thresholds are exceeded. + * + * States: + * - CLOSED: Normal operation, requests pass through + * - OPEN: Failures exceeded threshold, requests are blocked + * - HALF_OPEN: Testing recovery, limited requests allowed + * + * @module Utils/circuit-breaker */ import { EventEmitter } from 'events' import { metrics } from './prometheus-metrics.js' /** - * Estados do Circuit Breaker + * Circuit breaker operational states */ export type CircuitState = 'closed' | 'open' | 'half-open' /** - * Opções de configuração do Circuit Breaker + * Failure record with timestamp for sliding window tracking */ -export interface CircuitBreakerOptions { - /** Nome do circuit breaker (para métricas) */ - name: string - /** Número de falhas para abrir o circuito */ - failureThreshold?: number - /** Número de sucessos para fechar o circuito (em half-open) */ - successThreshold?: number - /** Tempo em ms para tentar half-open após open */ - resetTimeout?: number - /** Timeout para operações em ms */ - timeout?: number - /** Função para determinar se erro deve contar como falha */ - isFailure?: (error: Error) => boolean - /** Coletar métricas */ - collectMetrics?: boolean - /** Função de fallback quando circuito está aberto */ - fallback?: () => T | Promise - /** Callback quando estado muda */ - onStateChange?: (from: CircuitState, to: CircuitState) => void - /** Callback em falha */ - onFailure?: (error: Error) => void - /** Callback em sucesso */ - onSuccess?: () => void - /** Callback quando circuito abre */ - onOpen?: () => void - /** Callback quando circuito fecha */ - onClose?: () => void +export interface FailureRecord { + timestamp: number + error: Error } /** - * Estatísticas do Circuit Breaker + * Circuit breaker configuration options + */ +export interface CircuitBreakerOptions { + /** Unique identifier for this circuit breaker (used in metrics/logging) */ + name: string + /** Number of failures within the window to trigger OPEN state (default: 5) */ + failureThreshold?: number + /** Time window in ms for counting failures (default: 60000) */ + failureWindow?: number + /** Number of successes required in HALF_OPEN to return to CLOSED (default: 2) */ + successThreshold?: number + /** Time in ms to wait before transitioning from OPEN to HALF_OPEN (default: 30000) */ + resetTimeout?: number + /** Timeout for individual operations in ms (default: 10000) */ + timeout?: number + /** Minimum number of requests before circuit can trip (default: 5) */ + volumeThreshold?: number + /** Predicate to determine if an error should count as a failure */ + shouldCountError?: (error: Error) => boolean + /** Whether to collect Prometheus metrics (default: true) */ + collectMetrics?: boolean + /** Fallback function when circuit is OPEN */ + fallback?: () => T | Promise + /** Callback when state changes */ + onStateChange?: (from: CircuitState, to: CircuitState) => void + /** Callback on failure */ + onFailure?: (error: Error) => void + /** Callback on success */ + onSuccess?: () => void + /** Callback when circuit opens */ + onOpen?: () => void + /** Callback when circuit closes */ + onClose?: () => void + /** Callback when circuit enters half-open */ + onHalfOpen?: () => void +} + +/** + * Circuit breaker statistics */ export interface CircuitBreakerStats { state: CircuitState failures: number successes: number + consecutiveFailures: number + consecutiveSuccesses: number totalCalls: number totalFailures: number totalSuccesses: number + totalRejected: number + failureRate: number lastFailureTime?: number lastSuccessTime?: number lastStateChange?: number @@ -70,7 +89,7 @@ export interface CircuitBreakerStats { } /** - * Erro lançado quando circuito está aberto + * Error thrown when circuit is OPEN and request is rejected */ export class CircuitOpenError extends Error { constructor( @@ -83,7 +102,7 @@ export class CircuitOpenError extends Error { } /** - * Erro de timeout + * Error thrown when operation exceeds timeout */ export class CircuitTimeoutError extends Error { constructor( @@ -96,20 +115,39 @@ export class CircuitTimeoutError extends Error { } /** - * Classe principal do Circuit Breaker + * Circuit Breaker implementation with sliding window failure tracking + * + * @example + * ```typescript + * const breaker = new CircuitBreaker({ + * name: 'whatsapp-api', + * failureThreshold: 5, + * resetTimeout: 30000 + * }) + * + * try { + * const result = await breaker.execute(() => sendMessage(msg)) + * } catch (error) { + * if (error instanceof CircuitOpenError) { + * // Circuit is open, use fallback + * } + * } + * ``` */ export class CircuitBreaker extends EventEmitter { private state: CircuitState = 'closed' - private failures = 0 - private successes = 0 + private failureRecords: FailureRecord[] = [] + private consecutiveFailures = 0 + private consecutiveSuccesses = 0 private totalCalls = 0 private totalFailures = 0 private totalSuccesses = 0 + private totalRejected = 0 private lastFailureTime?: number private lastSuccessTime?: number - private lastStateChange?: number + private lastStateChange: number private resetTimer?: ReturnType - private options: Required + private readonly options: Required constructor(options: CircuitBreakerOptions) { super() @@ -117,10 +155,12 @@ export class CircuitBreaker extends EventEmitter { this.options = { name: options.name, failureThreshold: options.failureThreshold ?? 5, + failureWindow: options.failureWindow ?? 60000, successThreshold: options.successThreshold ?? 2, resetTimeout: options.resetTimeout ?? 30000, timeout: options.timeout ?? 10000, - isFailure: options.isFailure ?? (() => true), + volumeThreshold: options.volumeThreshold ?? 5, + shouldCountError: options.shouldCountError ?? (() => true), collectMetrics: options.collectMetrics ?? true, fallback: options.fallback ?? (() => { throw new CircuitOpenError(this.options.name, this.state) @@ -130,27 +170,46 @@ export class CircuitBreaker extends EventEmitter { onSuccess: options.onSuccess ?? (() => {}), onOpen: options.onOpen ?? (() => {}), onClose: options.onClose ?? (() => {}), + onHalfOpen: options.onHalfOpen ?? (() => {}), } this.lastStateChange = Date.now() } /** - * Executa operação protegida pelo circuit breaker + * Check if the circuit allows execution + */ + canExecute(): boolean { + if (this.state === 'closed') { + return true + } + + if (this.state === 'open') { + return false + } + + // HALF_OPEN: allow limited requests for testing + return true + } + + /** + * Execute an async operation with circuit breaker protection */ async execute(operation: () => T | Promise): Promise { this.totalCalls++ - // Verificar estado + // Check if circuit allows execution if (this.state === 'open') { + this.totalRejected++ + if (this.options.collectMetrics) { - metrics.errors.inc({ category: 'circuit_breaker', code: 'open' }) + metrics.errors.inc({ category: 'circuit_breaker', code: 'rejected' }) } return this.options.fallback() as T } - // Executar com timeout + // Execute with timeout protection try { const result = await this.executeWithTimeout(operation) this.recordSuccess() @@ -162,14 +221,16 @@ export class CircuitBreaker extends EventEmitter { } /** - * Executa operação síncrona protegida + * Execute a synchronous operation with circuit breaker protection */ executeSync(operation: () => T): T { this.totalCalls++ if (this.state === 'open') { + this.totalRejected++ + if (this.options.collectMetrics) { - metrics.errors.inc({ category: 'circuit_breaker', code: 'open' }) + metrics.errors.inc({ category: 'circuit_breaker', code: 'rejected' }) } return this.options.fallback() as T @@ -186,7 +247,7 @@ export class CircuitBreaker extends EventEmitter { } /** - * Executa com timeout + * Execute operation with timeout wrapper */ private async executeWithTimeout(operation: () => T | Promise): Promise { return new Promise((resolve, reject) => { @@ -207,12 +268,13 @@ export class CircuitBreaker extends EventEmitter { } /** - * Registra sucesso + * Record a successful operation */ private recordSuccess(): void { this.totalSuccesses++ this.lastSuccessTime = Date.now() - this.failures = 0 + this.consecutiveSuccesses++ + this.consecutiveFailures = 0 if (this.options.collectMetrics) { metrics.socketEvents.inc({ event: 'circuit_success' }) @@ -221,28 +283,34 @@ export class CircuitBreaker extends EventEmitter { this.options.onSuccess() this.emit('success') + // In HALF_OPEN state, check if we can close the circuit if (this.state === 'half-open') { - this.successes++ - - if (this.successes >= this.options.successThreshold) { + if (this.consecutiveSuccesses >= this.options.successThreshold) { this.transitionTo('closed') } } } /** - * Registra falha + * Record a failed operation */ private recordFailure(error: Error): void { - // Verificar se erro deve contar como falha - if (!this.options.isFailure(error)) { + // Check if this error should count as a failure + if (!this.options.shouldCountError(error)) { return } + const now = Date.now() this.totalFailures++ - this.lastFailureTime = Date.now() - this.failures++ - this.successes = 0 + this.lastFailureTime = now + this.consecutiveFailures++ + this.consecutiveSuccesses = 0 + + // Add to failure records for sliding window + this.failureRecords.push({ timestamp: now, error }) + + // Clean old failures outside the window + this.cleanOldFailures() if (this.options.collectMetrics) { metrics.errors.inc({ category: 'circuit_breaker', code: 'failure' }) @@ -251,15 +319,35 @@ export class CircuitBreaker extends EventEmitter { this.options.onFailure(error) this.emit('failure', error) + // State transition logic if (this.state === 'half-open') { + // Any failure in HALF_OPEN immediately reopens the circuit this.transitionTo('open') - } else if (this.state === 'closed' && this.failures >= this.options.failureThreshold) { - this.transitionTo('open') + } else if (this.state === 'closed') { + // Check if we should trip the circuit + const recentFailures = this.failureRecords.length + + if ( + this.totalCalls >= this.options.volumeThreshold && + recentFailures >= this.options.failureThreshold + ) { + this.transitionTo('open') + } } } /** - * Transiciona para novo estado + * Remove failure records outside the sliding window + */ + private cleanOldFailures(): void { + const cutoff = Date.now() - this.options.failureWindow + this.failureRecords = this.failureRecords.filter( + (record) => record.timestamp > cutoff + ) + } + + /** + * Transition to a new state */ private transitionTo(newState: CircuitState): void { const oldState = this.state @@ -271,31 +359,39 @@ export class CircuitBreaker extends EventEmitter { this.state = newState this.lastStateChange = Date.now() - // Limpar timer existente + // Clear existing reset timer if (this.resetTimer) { clearTimeout(this.resetTimer) this.resetTimer = undefined } - // Resetar contadores baseado no novo estado - if (newState === 'closed') { - this.failures = 0 - this.successes = 0 - this.options.onClose() - this.emit('close') - } else if (newState === 'open') { - this.successes = 0 - this.options.onOpen() - this.emit('open') + // State-specific actions + switch (newState) { + case 'closed': + this.consecutiveFailures = 0 + this.consecutiveSuccesses = 0 + this.failureRecords = [] + this.options.onClose() + this.emit('close') + break - // Agendar tentativa de half-open - this.resetTimer = setTimeout(() => { - this.transitionTo('half-open') - }, this.options.resetTimeout) - } else if (newState === 'half-open') { - this.successes = 0 - this.failures = 0 - this.emit('half-open') + case 'open': + this.consecutiveSuccesses = 0 + this.options.onOpen() + this.emit('open') + + // Schedule transition to HALF_OPEN + this.resetTimer = setTimeout(() => { + this.transitionTo('half-open') + }, this.options.resetTimeout) + break + + case 'half-open': + this.consecutiveSuccesses = 0 + this.consecutiveFailures = 0 + this.options.onHalfOpen() + this.emit('half-open') + break } this.options.onStateChange(oldState, newState) @@ -303,58 +399,68 @@ export class CircuitBreaker extends EventEmitter { } /** - * Força abertura do circuito + * Manually trip the circuit to OPEN state */ trip(): void { this.transitionTo('open') } /** - * Força fechamento do circuito + * Manually reset the circuit to CLOSED state */ reset(): void { this.transitionTo('closed') } /** - * Retorna estado atual + * Get current circuit state */ getState(): CircuitState { return this.state } /** - * Verifica se circuito está aberto + * Check if circuit is OPEN */ isOpen(): boolean { return this.state === 'open' } /** - * Verifica se circuito está fechado + * Check if circuit is CLOSED */ isClosed(): boolean { return this.state === 'closed' } /** - * Verifica se circuito está half-open + * Check if circuit is HALF_OPEN */ isHalfOpen(): boolean { return this.state === 'half-open' } /** - * Retorna estatísticas + * Get circuit breaker statistics */ getStats(): CircuitBreakerStats { + this.cleanOldFailures() + + const failureRate = this.totalCalls > 0 + ? (this.totalFailures / this.totalCalls) * 100 + : 0 + return { state: this.state, - failures: this.failures, - successes: this.successes, + failures: this.failureRecords.length, + successes: this.consecutiveSuccesses, + consecutiveFailures: this.consecutiveFailures, + consecutiveSuccesses: this.consecutiveSuccesses, totalCalls: this.totalCalls, totalFailures: this.totalFailures, totalSuccesses: this.totalSuccesses, + totalRejected: this.totalRejected, + failureRate, lastFailureTime: this.lastFailureTime, lastSuccessTime: this.lastSuccessTime, lastStateChange: this.lastStateChange, @@ -365,38 +471,39 @@ export class CircuitBreaker extends EventEmitter { } /** - * Retorna nome do circuit breaker + * Get circuit breaker name */ getName(): string { return this.options.name } /** - * Destroy e limpa recursos + * Destroy circuit breaker and clean up resources */ destroy(): void { if (this.resetTimer) { clearTimeout(this.resetTimer) } + this.failureRecords = [] this.removeAllListeners() } } /** - * Factory para criar circuit breaker + * Factory function to create a circuit breaker */ export function createCircuitBreaker(options: CircuitBreakerOptions): CircuitBreaker { return new CircuitBreaker(options) } /** - * Registry de circuit breakers + * Registry for managing multiple circuit breakers */ export class CircuitBreakerRegistry { private breakers: Map = new Map() /** - * Obtém ou cria circuit breaker + * Get or create a circuit breaker by name */ get(name: string, options?: Omit): CircuitBreaker { if (!this.breakers.has(name)) { @@ -407,14 +514,14 @@ export class CircuitBreakerRegistry { } /** - * Verifica se circuit breaker existe + * Check if a circuit breaker exists */ has(name: string): boolean { return this.breakers.has(name) } /** - * Remove circuit breaker + * Remove a circuit breaker */ remove(name: string): boolean { const breaker = this.breakers.get(name) @@ -426,14 +533,14 @@ export class CircuitBreakerRegistry { } /** - * Retorna todos os circuit breakers + * Get all circuit breakers */ getAll(): Map { return new Map(this.breakers) } /** - * Retorna estatísticas de todos os circuit breakers + * Get statistics for all circuit breakers */ getAllStats(): Record { const stats: Record = {} @@ -444,7 +551,7 @@ export class CircuitBreakerRegistry { } /** - * Reseta todos os circuit breakers + * Reset all circuit breakers to CLOSED state */ resetAll(): void { for (const breaker of this.breakers.values()) { @@ -453,7 +560,7 @@ export class CircuitBreakerRegistry { } /** - * Destroy todos os circuit breakers + * Destroy all circuit breakers */ destroyAll(): void { for (const breaker of this.breakers.values()) { @@ -464,12 +571,22 @@ export class CircuitBreakerRegistry { } /** - * Registry global + * Global circuit breaker registry instance */ export const globalCircuitRegistry = new CircuitBreakerRegistry() /** - * Decorator para proteger método com circuit breaker + * Decorator to protect a method with circuit breaker + * + * @example + * ```typescript + * class MyService { + * @circuitBreaker({ failureThreshold: 3 }) + * async fetchData() { + * return await api.getData() + * } + * } + * ``` */ export function circuitBreaker(options: Omit & { name?: string } = {}) { return function ( @@ -492,7 +609,15 @@ export function circuitBreaker(options: Omit & { } /** - * Wrapper para função com circuit breaker + * Wrap a function with circuit breaker protection + * + * @example + * ```typescript + * const protectedFetch = withCircuitBreaker( + * fetchData, + * { name: 'api-fetch', failureThreshold: 5 } + * ) + * ``` */ export function withCircuitBreaker unknown>( fn: T, @@ -506,7 +631,7 @@ export function withCircuitBreaker unknown>( } /** - * Verifica saúde de todos os circuit breakers + * Get health status of all circuit breakers */ export function getCircuitHealth(): { healthy: boolean @@ -529,4 +654,100 @@ export function getCircuitHealth(): { } } +/** + * Create a pre-configured circuit breaker for WhatsApp PreKey operations + * + * This circuit breaker is optimized for handling encryption/session errors + * that commonly occur with WhatsApp's Signal protocol implementation. + * + * @example + * ```typescript + * const preKeyBreaker = createPreKeyCircuitBreaker() + * + * async function sendEncryptedMessage(msg) { + * return preKeyBreaker.execute(async () => { + * return await encryptAndSend(msg) + * }) + * } + * ``` + */ +export function createPreKeyCircuitBreaker( + customOptions?: Partial +): CircuitBreaker { + const preKeyErrorPatterns = [ + 'prekey', + 'pre-key', + 'session', + 'signal', + 'encrypt', + 'decrypt', + 'cipher', + 'key', + ] + + return new CircuitBreaker({ + name: 'prekey-operations', + failureThreshold: 5, + failureWindow: 60000, + resetTimeout: 30000, + successThreshold: 2, + shouldCountError: (error: Error) => { + const message = error.message.toLowerCase() + return preKeyErrorPatterns.some((pattern) => message.includes(pattern)) + }, + ...customOptions, + }) +} + +/** + * Create a circuit breaker for WebSocket connection operations + */ +export function createConnectionCircuitBreaker( + customOptions?: Partial +): CircuitBreaker { + const connectionErrorPatterns = [ + 'econnrefused', + 'econnreset', + 'etimedout', + 'enotfound', + 'socket', + 'websocket', + 'connection', + 'network', + ] + + return new CircuitBreaker({ + name: 'connection-operations', + failureThreshold: 3, + failureWindow: 30000, + resetTimeout: 60000, + successThreshold: 1, + shouldCountError: (error: Error) => { + const message = error.message.toLowerCase() + const code = (error as NodeJS.ErrnoException).code?.toLowerCase() || '' + return connectionErrorPatterns.some( + (pattern) => message.includes(pattern) || code.includes(pattern) + ) + }, + ...customOptions, + }) +} + +/** + * Create a circuit breaker for message sending operations + */ +export function createMessageCircuitBreaker( + customOptions?: Partial +): CircuitBreaker { + return new CircuitBreaker({ + name: 'message-operations', + failureThreshold: 5, + failureWindow: 60000, + resetTimeout: 15000, + successThreshold: 2, + timeout: 30000, + ...customOptions, + }) +} + export default CircuitBreaker diff --git a/src/Utils/index.ts b/src/Utils/index.ts index 1780cacd..9e4af329 100644 --- a/src/Utils/index.ts +++ b/src/Utils/index.ts @@ -17,18 +17,18 @@ export * from './process-message' export * from './message-retry-manager' export * from './browser-utils' -// === Novos Utilitários de Observabilidade e Resiliência === +// === Observability and Resilience Utilities === -// Logging estruturado +// Structured logging export * from './structured-logger' export * from './logger-adapter' export * from './baileys-logger' -// Observabilidade e rastreamento +// Observability and tracing export * from './trace-context' export * from './prometheus-metrics' -// Resiliência e performance +// Resilience and performance export * from './cache-utils' export * from './circuit-breaker' export * from './retry-utils' diff --git a/src/Utils/prometheus-metrics.ts b/src/Utils/prometheus-metrics.ts index 85eb75a5..70e1863b 100644 --- a/src/Utils/prometheus-metrics.ts +++ b/src/Utils/prometheus-metrics.ts @@ -1,42 +1,43 @@ /** - * @fileoverview Exposição de métricas no formato Prometheus + * Prometheus Metrics Exposition + * + * Provides: + * - Counters for event counting + * - Gauges for instantaneous values + * - Histograms for value distribution + * - Summaries for percentiles + * - /metrics endpoint ready for scraping + * - Dynamic labels + * - Baileys events integration + * + * Note: Works standalone without prom-client, + * but can be integrated with prom-client if available. + * * @module Utils/prometheus-metrics - * - * Fornece: - * - Counters para contagem de eventos - * - Gauges para valores instantâneos - * - Histograms para distribuição de valores - * - Summaries para percentis - * - Endpoint /metrics pronto para scraping - * - Labels dinâmicas - * - Integração com Baileys events - * - * Nota: Funciona de forma standalone sem prom-client, - * mas pode ser integrado com prom-client se disponível. */ /** - * Tipo de métrica + * Metric type */ export type MetricType = 'counter' | 'gauge' | 'histogram' | 'summary' /** - * Labels para métricas + * Metric labels */ export type Labels = Record /** - * Buckets padrão para histogramas (em ms) + * Default histogram buckets (in ms) */ export const DEFAULT_BUCKETS = [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000] /** - * Percentis padrão para summaries + * Default summary percentiles */ export const DEFAULT_PERCENTILES = [0.5, 0.9, 0.95, 0.99] /** - * Interface base para métricas + * Base metric interface */ export interface BaseMetric { name: string @@ -46,7 +47,7 @@ export interface BaseMetric { } /** - * Valor de uma métrica com labels + * Metric value with labels */ export interface MetricValue { labels: Labels @@ -55,7 +56,7 @@ export interface MetricValue { } /** - * Valores de histograma + * Histogram values */ export interface HistogramValue { labels: Labels @@ -65,7 +66,7 @@ export interface HistogramValue { } /** - * Valores de summary + * Summary values */ export interface SummaryValue { labels: Labels @@ -75,7 +76,7 @@ export interface SummaryValue { } /** - * Classe Counter - incrementa monotonicamente + * Counter class - monotonically increasing */ export class Counter implements BaseMetric { readonly type = 'counter' as const @@ -88,7 +89,7 @@ export class Counter implements BaseMetric { ) {} /** - * Incrementa o counter + * Increment the counter */ inc(labelsOrValue?: Labels | number, value?: number): void { let labels: Labels = {} @@ -117,7 +118,7 @@ export class Counter implements BaseMetric { } /** - * Retorna valor atual + * Get current value */ get(labels: Labels = {}): number { const key = this.labelsToKey(labels) @@ -125,7 +126,7 @@ export class Counter implements BaseMetric { } /** - * Reseta o counter + * Reset the counter */ reset(labels?: Labels): void { if (labels) { @@ -137,7 +138,7 @@ export class Counter implements BaseMetric { } /** - * Retorna todos os valores + * Get all values */ getValues(): MetricValue[] { return Array.from(this.values.values()) @@ -148,7 +149,7 @@ export class Counter implements BaseMetric { } /** - * Cria versão com labels pré-definidas + * Create version with pre-defined labels */ labels(labels: Labels): { inc: (value?: number) => void } { return { @@ -158,7 +159,7 @@ export class Counter implements BaseMetric { } /** - * Classe Gauge - valor que pode aumentar e diminuir + * Gauge class - value that can increase and decrease */ export class Gauge implements BaseMetric { readonly type = 'gauge' as const @@ -171,7 +172,7 @@ export class Gauge implements BaseMetric { ) {} /** - * Define valor + * Set value */ set(labelsOrValue: Labels | number, value?: number): void { let labels: Labels = {} @@ -193,7 +194,7 @@ export class Gauge implements BaseMetric { } /** - * Incrementa valor + * Increment value */ inc(labelsOrValue?: Labels | number, value?: number): void { let labels: Labels = {} @@ -214,7 +215,7 @@ export class Gauge implements BaseMetric { } /** - * Decrementa valor + * Decrement value */ dec(labelsOrValue?: Labels | number, value?: number): void { let labels: Labels = {} @@ -235,14 +236,14 @@ export class Gauge implements BaseMetric { } /** - * Define para timestamp atual + * Set to current timestamp */ setToCurrentTime(labels: Labels = {}): void { this.set(labels, Date.now() / 1000) } /** - * Retorna valor atual + * Get current value */ get(labels: Labels = {}): number { const key = this.labelsToKey(labels) @@ -250,7 +251,7 @@ export class Gauge implements BaseMetric { } /** - * Reseta o gauge + * Reset the gauge */ reset(labels?: Labels): void { if (labels) { @@ -262,7 +263,7 @@ export class Gauge implements BaseMetric { } /** - * Retorna todos os valores + * Get all values */ getValues(): MetricValue[] { return Array.from(this.values.values()) @@ -273,7 +274,7 @@ export class Gauge implements BaseMetric { } /** - * Cria versão com labels pré-definidas + * Create version with pre-defined labels */ labels(labels: Labels): { set: (value: number) => void @@ -288,12 +289,12 @@ export class Gauge implements BaseMetric { } /** - * Timer helper - retorna função para parar e registrar duração + * Timer helper - returns function to stop and record duration */ startTimer(labels: Labels = {}): () => number { const start = process.hrtime.bigint() return () => { - const duration = Number(process.hrtime.bigint() - start) / 1_000_000_000 // segundos + const duration = Number(process.hrtime.bigint() - start) / 1_000_000_000 // seconds this.set(labels, duration) return duration } @@ -301,7 +302,7 @@ export class Gauge implements BaseMetric { } /** - * Classe Histogram - distribuição de valores em buckets + * Histogram class - distribution of values in buckets */ export class Histogram implements BaseMetric { readonly type = 'histogram' as const @@ -318,7 +319,7 @@ export class Histogram implements BaseMetric { } /** - * Observa um valor + * Observe a value */ observe(labelsOrValue: Labels | number, value?: number): void { let labels: Labels = {} @@ -344,7 +345,7 @@ export class Histogram implements BaseMetric { this.values.set(key, histValue) } - // Incrementar buckets apropriados + // Increment appropriate buckets for (const bucket of this.buckets) { if (observeValue <= bucket) { histValue.buckets.set(bucket, (histValue.buckets.get(bucket) ?? 0) + 1) @@ -368,7 +369,7 @@ export class Histogram implements BaseMetric { } /** - * Retorna valores do histogram + * Get histogram values */ get(labels: Labels = {}): HistogramValue | undefined { const key = this.labelsToKey(labels) @@ -376,7 +377,7 @@ export class Histogram implements BaseMetric { } /** - * Reseta o histogram + * Reset the histogram */ reset(labels?: Labels): void { if (labels) { @@ -388,14 +389,14 @@ export class Histogram implements BaseMetric { } /** - * Retorna todos os valores + * Get all values */ getValues(): HistogramValue[] { return Array.from(this.values.values()) } /** - * Retorna buckets configurados + * Get configured buckets */ getBuckets(): number[] { return [...this.buckets] @@ -406,7 +407,7 @@ export class Histogram implements BaseMetric { } /** - * Cria versão com labels pré-definidas + * Create version with pre-defined labels */ labels(labels: Labels): { observe: (value: number) => void @@ -420,7 +421,7 @@ export class Histogram implements BaseMetric { } /** - * Classe Summary - percentis de valores + * Summary class - value percentiles */ export class Summary implements BaseMetric { readonly type = 'summary' as const @@ -441,7 +442,7 @@ export class Summary implements BaseMetric { } /** - * Observa um valor + * Observe a value */ observe(labelsOrValue: Labels | number, value?: number): void { let labels: Labels = {} @@ -471,7 +472,7 @@ export class Summary implements BaseMetric { summaryValue.sum += observeValue summaryValue.count++ - // Limitar tamanho + // Limit size if (summaryValue.values.length > this.maxSize) { const removed = summaryValue.values.shift() if (removed !== undefined) { @@ -494,7 +495,7 @@ export class Summary implements BaseMetric { } /** - * Calcula percentil + * Calculate percentile */ getPercentile(labels: Labels, percentile: number): number | undefined { const key = this.labelsToKey(labels) @@ -510,7 +511,7 @@ export class Summary implements BaseMetric { } /** - * Retorna valores do summary + * Get summary values */ get(labels: Labels = {}): SummaryValue | undefined { const key = this.labelsToKey(labels) @@ -518,7 +519,7 @@ export class Summary implements BaseMetric { } /** - * Reseta o summary + * Reset the summary */ reset(labels?: Labels): void { if (labels) { @@ -530,14 +531,14 @@ export class Summary implements BaseMetric { } /** - * Retorna todos os valores + * Get all values */ getValues(): SummaryValue[] { return Array.from(this.values.values()) } /** - * Retorna percentis configurados + * Get configured percentiles */ getPercentiles(): number[] { return [...this.percentiles] @@ -548,7 +549,7 @@ export class Summary implements BaseMetric { } /** - * Cria versão com labels pré-definidas + * Create version with pre-defined labels */ labels(labels: Labels): { observe: (value: number) => void @@ -562,7 +563,7 @@ export class Summary implements BaseMetric { } /** - * Registry de métricas + * Metrics registry */ export class MetricsRegistry { private metrics: Map = new Map() @@ -575,7 +576,7 @@ export class MetricsRegistry { } /** - * Registra uma métrica + * Register a metric */ register(metric: T): T { const fullName = this.prefix ? `${this.prefix}_${metric.name}` : metric.name @@ -584,7 +585,7 @@ export class MetricsRegistry { } /** - * Obtém uma métrica + * Get a metric */ get(name: string): Counter | Gauge | Histogram | Summary | undefined { const fullName = this.prefix ? `${this.prefix}_${name}` : name @@ -592,7 +593,7 @@ export class MetricsRegistry { } /** - * Remove uma métrica + * Remove a metric */ remove(name: string): boolean { const fullName = this.prefix ? `${this.prefix}_${name}` : name @@ -600,7 +601,7 @@ export class MetricsRegistry { } /** - * Reseta todas as métricas + * Reset all metrics */ resetAll(): void { for (const metric of this.metrics.values()) { @@ -609,7 +610,7 @@ export class MetricsRegistry { } /** - * Retorna métricas no formato Prometheus + * Return metrics in Prometheus format */ async metrics(): Promise { const lines: string[] = [] @@ -675,7 +676,7 @@ export class MetricsRegistry { } /** - * Retorna content type para Prometheus + * Return content type for Prometheus */ contentType(): string { return 'text/plain; version=0.0.4; charset=utf-8' @@ -694,84 +695,84 @@ export class MetricsRegistry { } } -// === Métricas Padrão do Baileys === +// === Default Baileys Metrics === /** - * Registry global para métricas do Baileys + * Global registry for Baileys metrics */ export const baileysMetrics = new MetricsRegistry({ prefix: 'baileys' }) /** - * Métricas pré-definidas para Baileys + * Pre-defined metrics for Baileys */ export const metrics = { - // Conexão + // Connection connectionAttempts: baileysMetrics.register( - new Counter('connection_attempts_total', 'Total de tentativas de conexão', ['status']) + new Counter('connection_attempts_total', 'Total connection attempts', ['status']) ), connectionState: baileysMetrics.register( - new Gauge('connection_state', 'Estado atual da conexão (0=desconectado, 1=conectado)', ['instance']) + new Gauge('connection_state', 'Current connection state (0=disconnected, 1=connected)', ['instance']) ), connectionDuration: baileysMetrics.register( - new Gauge('connection_duration_seconds', 'Duração da conexão atual em segundos', ['instance']) + new Gauge('connection_duration_seconds', 'Current connection duration in seconds', ['instance']) ), - // Mensagens + // Messages messagesSent: baileysMetrics.register( - new Counter('messages_sent_total', 'Total de mensagens enviadas', ['type']) + new Counter('messages_sent_total', 'Total messages sent', ['type']) ), messagesReceived: baileysMetrics.register( - new Counter('messages_received_total', 'Total de mensagens recebidas', ['type']) + new Counter('messages_received_total', 'Total messages received', ['type']) ), messageLatency: baileysMetrics.register( - new Histogram('message_latency_ms', 'Latência de envio de mensagem em ms', ['type'], [10, 50, 100, 250, 500, 1000, 2500, 5000]) + new Histogram('message_latency_ms', 'Message send latency in ms', ['type'], [10, 50, 100, 250, 500, 1000, 2500, 5000]) ), - // Mídia + // Media mediaUploads: baileysMetrics.register( - new Counter('media_uploads_total', 'Total de uploads de mídia', ['type', 'status']) + new Counter('media_uploads_total', 'Total media uploads', ['type', 'status']) ), mediaDownloads: baileysMetrics.register( - new Counter('media_downloads_total', 'Total de downloads de mídia', ['type', 'status']) + new Counter('media_downloads_total', 'Total media downloads', ['type', 'status']) ), mediaSize: baileysMetrics.register( - new Histogram('media_size_bytes', 'Tamanho de mídia em bytes', ['type', 'direction'], [1024, 10240, 102400, 1048576, 10485760]) + new Histogram('media_size_bytes', 'Media size in bytes', ['type', 'direction'], [1024, 10240, 102400, 1048576, 10485760]) ), - // Erros + // Errors errors: baileysMetrics.register( - new Counter('errors_total', 'Total de erros', ['category', 'code']) + new Counter('errors_total', 'Total errors', ['category', 'code']) ), // Retries retries: baileysMetrics.register( - new Counter('retries_total', 'Total de retentativas', ['operation']) + new Counter('retries_total', 'Total retries', ['operation']) ), retryLatency: baileysMetrics.register( - new Histogram('retry_latency_ms', 'Latência de retentativas em ms', ['operation']) + new Histogram('retry_latency_ms', 'Retry latency in ms', ['operation']) ), // Socket socketEvents: baileysMetrics.register( - new Counter('socket_events_total', 'Total de eventos de socket', ['event']) + new Counter('socket_events_total', 'Total socket events', ['event']) ), socketLatency: baileysMetrics.register( - new Histogram('socket_latency_ms', 'Latência de operações de socket em ms', ['operation']) + new Histogram('socket_latency_ms', 'Socket operation latency in ms', ['operation']) ), - // Criptografia + // Encryption encryptionOperations: baileysMetrics.register( - new Counter('encryption_operations_total', 'Total de operações de criptografia', ['operation']) + new Counter('encryption_operations_total', 'Total encryption operations', ['operation']) ), // Cache - cacheHits: baileysMetrics.register(new Counter('cache_hits_total', 'Total de cache hits', ['cache'])), - cacheMisses: baileysMetrics.register(new Counter('cache_misses_total', 'Total de cache misses', ['cache'])), - cacheSize: baileysMetrics.register(new Gauge('cache_size', 'Tamanho atual do cache', ['cache'])), + cacheHits: baileysMetrics.register(new Counter('cache_hits_total', 'Total cache hits', ['cache'])), + cacheMisses: baileysMetrics.register(new Counter('cache_misses_total', 'Total cache misses', ['cache'])), + cacheSize: baileysMetrics.register(new Gauge('cache_size', 'Current cache size', ['cache'])), } /** - * Helper para criar endpoint HTTP de métricas + * Helper to create HTTP metrics endpoint */ export function createMetricsHandler(registry: MetricsRegistry = baileysMetrics) { return async (_req: unknown, res: { setHeader: (name: string, value: string) => void; end: (body: string) => void }) => { diff --git a/src/Utils/retry-utils.ts b/src/Utils/retry-utils.ts index 7410e5dd..68edcb88 100644 --- a/src/Utils/retry-utils.ts +++ b/src/Utils/retry-utils.ts @@ -1,15 +1,16 @@ /** - * @fileoverview Lógica de retry inteligente - * @module Utils/retry-utils + * Smart Retry Logic * - * Fornece: + * Provides: * - Exponential backoff - * - Jitter para evitar thundering herd - * - Max attempts configurável - * - Predicates de retry customizáveis - * - Integração com circuit breaker - * - Hooks de eventos - * - Cancelamento + * - Jitter to avoid thundering herd + * - Configurable max attempts + * - Customizable retry predicates + * - Circuit breaker integration + * - Event hooks + * - Cancellation support + * + * @module Utils/retry-utils */ import { EventEmitter } from 'events' @@ -17,48 +18,48 @@ import { metrics } from './prometheus-metrics.js' import type { CircuitBreaker } from './circuit-breaker.js' /** - * Estratégias de backoff + * Backoff strategies */ export type BackoffStrategy = 'exponential' | 'linear' | 'constant' | 'fibonacci' /** - * Opções de configuração de retry + * Retry configuration options */ export interface RetryOptions { - /** Número máximo de tentativas (default: 3) */ + /** Maximum number of attempts (default: 3) */ maxAttempts?: number - /** Delay base em ms (default: 1000) */ + /** Base delay in ms (default: 1000) */ baseDelay?: number - /** Delay máximo em ms (default: 30000) */ + /** Maximum delay in ms (default: 30000) */ maxDelay?: number - /** Estratégia de backoff (default: exponential) */ + /** Backoff strategy (default: exponential) */ backoffStrategy?: BackoffStrategy - /** Multiplicador para exponential backoff (default: 2) */ + /** Multiplier for exponential backoff (default: 2) */ backoffMultiplier?: number - /** Percentual de jitter (0-1, default: 0.1) */ + /** Jitter percentage (0-1, default: 0.1) */ jitter?: number - /** Função para determinar se deve retry */ + /** Function to determine if should retry */ shouldRetry?: (error: Error, attempt: number) => boolean | Promise - /** Timeout por tentativa em ms */ + /** Timeout per attempt in ms */ timeout?: number - /** Nome da operação para métricas */ + /** Operation name for metrics */ operationName?: string - /** Coletar métricas */ + /** Collect metrics */ collectMetrics?: boolean - /** Circuit breaker para integração */ + /** Circuit breaker for integration */ circuitBreaker?: CircuitBreaker - /** Callback antes de cada retry */ + /** Callback before each retry */ onRetry?: (error: Error, attempt: number, delay: number) => void | Promise - /** Callback em sucesso */ + /** Callback on success */ onSuccess?: (result: unknown, attempt: number) => void - /** Callback em falha final */ + /** Callback on final failure */ onFailure?: (error: Error, attempts: number) => void - /** Signal para cancelamento */ + /** Signal for cancellation */ abortSignal?: AbortSignal } /** - * Resultado de operação com retry + * Result of operation with retry */ export interface RetryResult { success: boolean @@ -70,7 +71,7 @@ export interface RetryResult { } /** - * Contexto de retry + * Retry context */ export interface RetryContext { attempt: number @@ -81,7 +82,7 @@ export interface RetryContext { } /** - * Erro de retry esgotado + * Retry exhausted error */ export class RetryExhaustedError extends Error { constructor( @@ -97,7 +98,7 @@ export class RetryExhaustedError extends Error { } /** - * Erro de abort + * Abort error */ export class RetryAbortedError extends Error { constructor(public readonly attempt: number) { @@ -107,7 +108,7 @@ export class RetryAbortedError extends Error { } /** - * Calcula delay com base na estratégia + * Calculate delay based on strategy */ export function calculateDelay( attempt: number, @@ -142,10 +143,10 @@ export function calculateDelay( delay = baseDelay } - // Aplicar cap de delay máximo + // Apply max delay cap delay = Math.min(delay, maxDelay) - // Aplicar jitter + // Apply jitter if (jitter > 0) { const jitterAmount = delay * jitter delay = delay + (Math.random() * 2 - 1) * jitterAmount @@ -155,7 +156,7 @@ export function calculateDelay( } /** - * Calcula número de Fibonacci + * Calculate Fibonacci number */ function fibonacciNumber(n: number): number { if (n <= 1) return 1 @@ -170,7 +171,7 @@ function fibonacciNumber(n: number): number { } /** - * Sleep com suporte a abort + * Sleep with abort support */ async function sleep(ms: number, signal?: AbortSignal): Promise { return new Promise((resolve, reject) => { @@ -194,7 +195,7 @@ async function sleep(ms: number, signal?: AbortSignal): Promise { } /** - * Executa operação com timeout + * Execute operation with timeout */ async function executeWithTimeout( operation: () => Promise, @@ -236,7 +237,7 @@ async function executeWithTimeout( } /** - * Função principal de retry + * Main retry function */ export async function retry( operation: (context: RetryContext) => T | Promise, @@ -269,7 +270,7 @@ export async function retry( let lastError: Error | undefined - // Verificar abort inicial + // Check initial abort if (config.abortSignal?.aborted) { throw new RetryAbortedError(0) } @@ -277,19 +278,19 @@ export async function retry( for (let attempt = 1; attempt <= config.maxAttempts; attempt++) { context.attempt = attempt - // Verificar abort + // Check abort if (config.abortSignal?.aborted) { context.aborted = true throw new RetryAbortedError(attempt) } - // Verificar circuit breaker + // Check circuit breaker if (config.circuitBreaker?.isOpen()) { throw new Error(`Circuit breaker "${config.circuitBreaker.getName()}" is open`) } try { - // Executar operação + // Execute operation let result: T if (config.timeout) { @@ -302,7 +303,7 @@ export async function retry( result = await operation(context) } - // Sucesso + // Success if (config.collectMetrics) { metrics.retries.inc({ operation: config.operationName }) } @@ -313,11 +314,11 @@ export async function retry( lastError = error as Error context.lastError = lastError - // Verificar se deve retry + // Check if should retry const shouldRetry = await config.shouldRetry(lastError, attempt) if (!shouldRetry || attempt >= config.maxAttempts) { - // Falha final + // Final failure if (config.collectMetrics) { metrics.errors.inc({ category: 'retry', code: 'exhausted' }) } @@ -327,7 +328,7 @@ export async function retry( throw new RetryExhaustedError(lastError, attempt, config.operationName) } - // Calcular delay + // Calculate delay const delay = calculateDelay( attempt, config.baseDelay, @@ -337,24 +338,24 @@ export async function retry( config.jitter ) - // Callback de retry + // Retry callback await config.onRetry(lastError, attempt, delay) if (config.collectMetrics) { metrics.retryLatency.observe({ operation: config.operationName }, delay) } - // Aguardar delay + // Wait for delay await sleep(delay, config.abortSignal) } } - // Nunca deve chegar aqui, mas TypeScript precisa + // Should never reach here, but TypeScript needs this throw new RetryExhaustedError(lastError || new Error('Unknown error'), config.maxAttempts, config.operationName) } /** - * Retry com resultado detalhado + * Retry with detailed result */ export async function retryWithResult( operation: (context: RetryContext) => T | Promise, @@ -393,7 +394,7 @@ export async function retryWithResult( } /** - * Factory para criar função de retry configurada + * Factory to create configured retry function */ export function createRetrier(defaultOptions: RetryOptions = {}) { return ( @@ -405,7 +406,7 @@ export function createRetrier(defaultOptions: RetryOptions = {}) { } /** - * Decorator para adicionar retry a método + * Decorator to add retry to method */ export function withRetry(options: RetryOptions = {}) { return function ( @@ -428,7 +429,7 @@ export function withRetry(options: RetryOptions = {}) { } /** - * Wrapper para função com retry + * Wrapper for function with retry */ export function retryable unknown>( fn: T, @@ -440,7 +441,7 @@ export function retryable unknown>( } /** - * Classe para gerenciar retries com estado + * Class to manage retries with state */ export class RetryManager extends EventEmitter { private activeRetries: Map void; context: RetryContext }> = new Map() @@ -452,14 +453,14 @@ export class RetryManager extends EventEmitter { } /** - * Executa operação com retry + * Execute operation with retry */ async execute( id: string, operation: (context: RetryContext) => T | Promise, options?: RetryOptions ): Promise { - // Cancelar retry anterior com mesmo ID + // Cancel previous retry with same ID this.cancel(id) const abortController = new AbortController() @@ -487,7 +488,7 @@ export class RetryManager extends EventEmitter { } /** - * Cancela retry em andamento + * Cancel in-progress retry */ cancel(id: string): boolean { const active = this.activeRetries.get(id) @@ -501,7 +502,7 @@ export class RetryManager extends EventEmitter { } /** - * Cancela todos os retries + * Cancel all retries */ cancelAll(): void { for (const [id, active] of this.activeRetries) { @@ -512,21 +513,21 @@ export class RetryManager extends EventEmitter { } /** - * Verifica se há retry ativo + * Check if there is an active retry */ isActive(id: string): boolean { return this.activeRetries.has(id) } /** - * Retorna contexto de retry ativo + * Return active retry context */ getContext(id: string): RetryContext | undefined { return this.activeRetries.get(id)?.context } /** - * Retorna IDs de retries ativos + * Return active retry IDs */ getActiveIds(): string[] { return Array.from(this.activeRetries.keys()) @@ -534,36 +535,36 @@ export class RetryManager extends EventEmitter { } /** - * Predicates comuns para shouldRetry + * Common predicates for shouldRetry */ export const retryPredicates = { - /** Sempre retry (até max attempts) */ + /** Always retry (up to max attempts) */ always: () => true, - /** Nunca retry */ + /** Never retry */ never: () => false, - /** Retry apenas em erros de rede */ + /** Retry only on network errors */ onNetworkError: (error: Error) => { const networkErrors = ['ECONNREFUSED', 'ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'EAI_AGAIN'] return networkErrors.some((code) => error.message.includes(code) || (error as NodeJS.ErrnoException).code === code) }, - /** Retry apenas em erros específicos */ + /** Retry only on specific errors */ onErrorCodes: (codes: string[]) => (error: Error): boolean => { return codes.some((code) => error.message.includes(code) || (error as NodeJS.ErrnoException).code === code) }, - /** Retry exceto em erros específicos */ + /** Retry except on specific errors */ exceptErrorCodes: (codes: string[]) => (error: Error): boolean => { return !codes.some((code) => error.message.includes(code) || (error as NodeJS.ErrnoException).code === code) }, - /** Retry em erros HTTP 5xx ou timeout */ + /** Retry on HTTP 5xx errors or timeout */ onServerError: (error: Error) => { const message = error.message.toLowerCase() return ( @@ -575,14 +576,14 @@ export const retryPredicates = { ) }, - /** Combina múltiplos predicates com OR */ + /** Combine multiple predicates with OR */ or: (...predicates: Array<(error: Error, attempt: number) => boolean>) => (error: Error, attempt: number): boolean => { return predicates.some((p) => p(error, attempt)) }, - /** Combina múltiplos predicates com AND */ + /** Combine multiple predicates with AND */ and: (...predicates: Array<(error: Error, attempt: number) => boolean>) => (error: Error, attempt: number): boolean => { @@ -591,10 +592,10 @@ export const retryPredicates = { } /** - * Configurações pré-definidas de retry + * Pre-defined retry configurations */ export const retryConfigs = { - /** Retry agressivo (muitas tentativas, delays curtos) */ + /** Aggressive retry (many attempts, short delays) */ aggressive: { maxAttempts: 10, baseDelay: 100, @@ -603,7 +604,7 @@ export const retryConfigs = { jitter: 0.2, }, - /** Retry conservador (poucas tentativas, delays longos) */ + /** Conservative retry (few attempts, long delays) */ conservative: { maxAttempts: 3, baseDelay: 2000, @@ -612,7 +613,7 @@ export const retryConfigs = { jitter: 0.1, }, - /** Retry rápido (para operações curtas) */ + /** Fast retry (for short operations) */ fast: { maxAttempts: 5, baseDelay: 50, @@ -621,7 +622,7 @@ export const retryConfigs = { jitter: 0.05, }, - /** Retry para operações de rede */ + /** Retry for network operations */ network: { maxAttempts: 5, baseDelay: 1000, diff --git a/src/Utils/structured-logger.ts b/src/Utils/structured-logger.ts index be2167c8..7d21102e 100644 --- a/src/Utils/structured-logger.ts +++ b/src/Utils/structured-logger.ts @@ -1,25 +1,26 @@ /** - * @fileoverview Sistema de logging estruturado para InfiniteAPI - * @module Utils/structured-logger + * Structured Logging System for InfiniteAPI * - * Fornece: - * - Níveis de log configuráveis (trace, debug, info, warn, error, fatal) - * - Formatação JSON para análise - * - Contexto hierárquico com child loggers - * - Integração com sistemas externos (hooks) - * - Suporte a métricas de logging - * - Sanitização de dados sensíveis + * Provides: + * - Configurable log levels (trace, debug, info, warn, error, fatal) + * - JSON formatting for log analysis + * - Hierarchical context with child loggers + * - External system integration via hooks + * - Logging metrics support + * - Sensitive data sanitization + * + * @module Utils/structured-logger */ import type { ILogger } from './logger.js' /** - * Níveis de log disponíveis (ordenados por severidade) + * Available log levels (ordered by severity) */ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'silent' /** - * Valores numéricos para cada nível de log + * Numeric values for each log level */ export const LOG_LEVEL_VALUES: Record = { trace: 10, @@ -32,55 +33,55 @@ export const LOG_LEVEL_VALUES: Record = { } /** - * Configuração do logger estruturado + * Structured logger configuration */ export interface StructuredLoggerConfig { - /** Nível mínimo de log a ser registrado */ + /** Minimum log level to record */ level: LogLevel - /** Nome do serviço/componente */ + /** Service/component name */ name?: string - /** Contexto adicional a ser incluído em todos os logs */ + /** Additional context to include in all logs */ context?: Record - /** Se deve formatar como JSON (true) ou texto legível (false) */ + /** Format as JSON (true) or human-readable text (false) */ jsonFormat?: boolean - /** Campos a serem sanitizados (senhas, tokens, etc.) */ + /** Fields to sanitize (passwords, tokens, etc.) */ redactFields?: string[] - /** Hook para enviar logs para sistemas externos */ + /** Hook for sending logs to external systems */ externalHook?: (entry: LogEntry) => void | Promise - /** Se deve incluir stack trace em erros */ + /** Include stack trace in errors */ includeStackTrace?: boolean - /** Timezone para timestamps (default: UTC) */ + /** Timezone for timestamps (default: UTC) */ timezone?: string } /** - * Entrada de log estruturada + * Structured log entry */ export interface LogEntry { - /** Timestamp ISO 8601 */ + /** ISO 8601 timestamp */ timestamp: string - /** Nível do log */ + /** Log level */ level: LogLevel - /** Valor numérico do nível */ + /** Numeric level value */ levelValue: number - /** Mensagem principal */ + /** Main message */ message: string - /** Nome do logger/componente */ + /** Logger/component name */ name?: string - /** Contexto adicional */ + /** Additional context */ context?: Record - /** Dados do objeto logado */ + /** Logged object data */ data?: Record - /** Stack trace (para erros) */ + /** Stack trace (for errors) */ stack?: string - /** ID de correlação para rastreamento */ + /** Correlation ID for tracing */ correlationId?: string - /** Duração de operação em ms (se aplicável) */ + /** Operation duration in ms (if applicable) */ durationMs?: number } /** - * Métricas de logging + * Logger metrics */ export interface LoggerMetrics { totalLogs: number @@ -90,7 +91,7 @@ export interface LoggerMetrics { } /** - * Campos padrão a serem sanitizados + * Default fields to sanitize */ const DEFAULT_REDACT_FIELDS = [ 'password', @@ -109,7 +110,19 @@ const DEFAULT_REDACT_FIELDS = [ ] /** - * Classe principal do Logger Estruturado + * Structured Logger main class + * + * @example + * ```typescript + * const logger = createStructuredLogger({ + * level: 'info', + * name: 'my-service', + * jsonFormat: true + * }) + * + * logger.info({ userId: '123' }, 'User logged in') + * logger.error(new Error('Connection failed')) + * ``` */ export class StructuredLogger implements ILogger { private config: Required @@ -144,14 +157,14 @@ export class StructuredLogger implements ILogger { } /** - * Getter para o nível atual do logger (compatibilidade com ILogger) + * Get current log level (ILogger compatibility) */ get level(): string { return this.config.level } /** - * Setter para o nível do logger + * Set log level */ set level(newLevel: string) { if (newLevel in LOG_LEVEL_VALUES) { @@ -160,7 +173,7 @@ export class StructuredLogger implements ILogger { } /** - * Cria um logger filho com contexto adicional + * Create a child logger with additional context */ child(obj: Record): StructuredLogger { const childLogger = new StructuredLogger({ @@ -172,14 +185,14 @@ export class StructuredLogger implements ILogger { } /** - * Verifica se o nível de log está habilitado + * Check if log level is enabled */ isLevelEnabled(level: LogLevel): boolean { return LOG_LEVEL_VALUES[level] >= LOG_LEVEL_VALUES[this.config.level] } /** - * Método principal de logging + * Main logging method */ private log(level: LogLevel, obj: unknown, msg?: string): void { if (!this.isLevelEnabled(level)) { @@ -188,13 +201,13 @@ export class StructuredLogger implements ILogger { const entry = this.createLogEntry(level, obj, msg) - // Atualizar métricas + // Update metrics this.updateMetrics(level) // Output this.output(entry) - // Hook externo (async, não bloqueia) + // External hook (async, non-blocking) if (this.config.externalHook) { Promise.resolve(this.config.externalHook(entry)).catch(() => { // Silently ignore hook errors @@ -203,7 +216,7 @@ export class StructuredLogger implements ILogger { } /** - * Cria uma entrada de log estruturada + * Create a structured log entry */ private createLogEntry(level: LogLevel, obj: unknown, msg?: string): LogEntry { const timestamp = new Date().toISOString() @@ -211,7 +224,7 @@ export class StructuredLogger implements ILogger { let data: Record | undefined let stack: string | undefined - // Processar objeto + // Process object if (obj instanceof Error) { message = message || obj.message if (this.config.includeStackTrace && obj.stack) { @@ -231,7 +244,7 @@ export class StructuredLogger implements ILogger { message = message || obj } - // Extrair correlationId e durationMs se presentes + // Extract correlationId and durationMs if present const correlationId = data?.correlationId as string | undefined const durationMs = data?.durationMs as number | undefined @@ -250,7 +263,7 @@ export class StructuredLogger implements ILogger { } /** - * Sanitiza dados sensíveis + * Sanitize sensitive data */ private sanitize(obj: Record): Record { const sanitized: Record = {} @@ -271,7 +284,7 @@ export class StructuredLogger implements ILogger { } /** - * Atualiza métricas internas + * Update internal metrics */ private updateMetrics(level: LogLevel): void { this.metrics.totalLogs++ @@ -284,7 +297,7 @@ export class StructuredLogger implements ILogger { } /** - * Output do log + * Output log entry */ private output(entry: LogEntry): void { const output = this.config.jsonFormat ? JSON.stringify(entry) : this.formatText(entry) @@ -308,7 +321,7 @@ export class StructuredLogger implements ILogger { } /** - * Formata log como texto legível + * Format log as human-readable text */ private formatText(entry: LogEntry): string { const parts = [ @@ -333,7 +346,7 @@ export class StructuredLogger implements ILogger { return text } - // Métodos de conveniência para cada nível de log + // Convenience methods for each log level trace(obj: unknown, msg?: string): void { this.log('trace', obj, msg) @@ -360,21 +373,21 @@ export class StructuredLogger implements ILogger { } /** - * Log com contexto temporário + * Log with temporary context */ withContext(context: Record): StructuredLogger { return this.child(context) } /** - * Log com correlation ID + * Log with correlation ID */ withCorrelationId(correlationId: string): StructuredLogger { return this.child({ correlationId }) } /** - * Log de operação com duração + * Log operation with duration tracking */ logOperation( operationName: string, @@ -412,14 +425,14 @@ export class StructuredLogger implements ILogger { } /** - * Retorna métricas do logger + * Get logger metrics */ getMetrics(): LoggerMetrics { return { ...this.metrics } } /** - * Reseta métricas + * Reset metrics */ resetMetrics(): void { this.metrics = { @@ -439,7 +452,7 @@ export class StructuredLogger implements ILogger { } /** - * Factory para criar logger estruturado + * Factory to create structured logger */ export function createStructuredLogger(config: Partial = {}): StructuredLogger { return new StructuredLogger({ @@ -449,7 +462,7 @@ export function createStructuredLogger(config: Partial = } /** - * Logger padrão singleton + * Default singleton logger */ let defaultLogger: StructuredLogger | null = null @@ -469,7 +482,7 @@ export function setDefaultLogger(logger: StructuredLogger): void { } /** - * Utilitário para medir tempo de execução + * Utility to measure execution time */ export function createTimer(): { elapsed: () => number; elapsedMs: () => string } { const start = process.hrtime.bigint() diff --git a/src/Utils/trace-context.ts b/src/Utils/trace-context.ts index dd76c76b..398baa49 100644 --- a/src/Utils/trace-context.ts +++ b/src/Utils/trace-context.ts @@ -1,153 +1,154 @@ /** - * @fileoverview Contexto de rastreamento para requests - * @module Utils/trace-context + * Request Tracing Context * - * Fornece: - * - Geração de trace IDs únicos - * - Context propagation entre operações - * - Correlation IDs para rastrear requests + * Provides: + * - Unique trace ID generation + * - Context propagation between operations + * - Correlation IDs for request tracking * - Performance timing - * - Span tracking para operações aninhadas - * - Baggage para dados contextuais + * - Span tracking for nested operations + * - Baggage for contextual data + * + * @module Utils/trace-context */ import { randomBytes } from 'crypto' import { AsyncLocalStorage } from 'async_hooks' /** - * Identificadores de trace + * Trace identifiers */ export interface TraceIds { - /** ID único do trace (16 bytes hex) */ + /** Unique trace ID (16 bytes hex) */ traceId: string - /** ID do span atual (8 bytes hex) */ + /** Current span ID (8 bytes hex) */ spanId: string - /** ID do span pai (opcional) */ + /** Parent span ID (optional) */ parentSpanId?: string - /** ID de correlação para logging */ + /** Correlation ID for logging */ correlationId: string } /** - * Dados de baggage (contexto propagado) + * Baggage data (propagated context) */ export type Baggage = Record /** - * Status de um span + * Span status */ export type SpanStatus = 'unset' | 'ok' | 'error' /** - * Span representa uma unidade de trabalho + * Span represents a unit of work */ export interface Span { - /** Nome da operação */ + /** Operation name */ name: string - /** IDs de rastreamento */ + /** Trace identifiers */ traceIds: TraceIds - /** Timestamp de início (ms) */ + /** Start timestamp (ms) */ startTime: number - /** Timestamp de fim (ms) */ + /** End timestamp (ms) */ endTime?: number - /** Duração em ms */ + /** Duration in ms */ duration?: number - /** Status do span */ + /** Span status */ status: SpanStatus - /** Atributos do span */ + /** Span attributes */ attributes: Record - /** Eventos ocorridos durante o span */ + /** Events occurred during the span */ events: SpanEvent[] - /** Se o span está finalizado */ + /** Whether the span has ended */ ended: boolean } /** - * Evento dentro de um span + * Event within a span */ export interface SpanEvent { - /** Nome do evento */ + /** Event name */ name: string - /** Timestamp do evento */ + /** Event timestamp */ timestamp: number - /** Atributos do evento */ + /** Event attributes */ attributes?: Record } /** - * Contexto completo de trace + * Complete trace context */ export interface TraceContext { - /** IDs de rastreamento */ + /** Trace identifiers */ traceIds: TraceIds - /** Baggage (dados propagados) */ + /** Baggage (propagated data) */ baggage: Baggage - /** Span atual */ + /** Current span */ currentSpan?: Span - /** Stack de spans (para spans aninhados) */ + /** Span stack (for nested spans) */ spanStack: Span[] - /** Timestamp de criação do contexto */ + /** Context creation timestamp */ createdAt: number - /** Metadados adicionais */ + /** Additional metadata */ metadata: Record } /** - * Opções para criar um novo contexto + * Options for creating a new context */ export interface CreateContextOptions { - /** Trace ID existente (para propagação) */ + /** Existing trace ID (for propagation) */ traceId?: string /** Parent span ID */ parentSpanId?: string - /** Correlation ID existente */ + /** Existing correlation ID */ correlationId?: string - /** Baggage inicial */ + /** Initial baggage */ baggage?: Baggage - /** Metadados iniciais */ + /** Initial metadata */ metadata?: Record } /** - * Opções para criar um span + * Options for creating a span */ export interface CreateSpanOptions { - /** Nome do span */ + /** Span name */ name: string - /** Atributos iniciais */ + /** Initial attributes */ attributes?: Record - /** Se deve ser filho do span atual */ + /** Whether to be a child of the current span */ asChild?: boolean } /** - * Storage assíncrono para contexto de trace + * Async storage for trace context */ const traceStorage = new AsyncLocalStorage() /** - * Gera um ID hexadecimal aleatório + * Generate a random hexadecimal ID */ function generateId(bytes: number): string { return randomBytes(bytes).toString('hex') } /** - * Gera um trace ID (16 bytes = 32 chars hex) + * Generate a trace ID (16 bytes = 32 chars hex) */ export function generateTraceId(): string { return generateId(16) } /** - * Gera um span ID (8 bytes = 16 chars hex) + * Generate a span ID (8 bytes = 16 chars hex) */ export function generateSpanId(): string { return generateId(8) } /** - * Gera um correlation ID mais legível + * Generate a more readable correlation ID */ export function generateCorrelationId(): string { const timestamp = Date.now().toString(36) @@ -156,7 +157,7 @@ export function generateCorrelationId(): string { } /** - * Cria um novo contexto de trace + * Create a new trace context */ export function createTraceContext(options: CreateContextOptions = {}): TraceContext { const traceId = options.traceId || generateTraceId() @@ -178,14 +179,14 @@ export function createTraceContext(options: CreateContextOptions = {}): TraceCon } /** - * Obtém o contexto de trace atual + * Get the current trace context */ export function getCurrentContext(): TraceContext | undefined { return traceStorage.getStore() } /** - * Obtém o contexto de trace atual ou cria um novo + * Get the current trace context or create a new one */ export function getOrCreateContext(): TraceContext { const existing = getCurrentContext() @@ -196,14 +197,14 @@ export function getOrCreateContext(): TraceContext { } /** - * Executa função com contexto de trace + * Execute function with trace context */ export function runWithContext(context: TraceContext, fn: () => T): T { return traceStorage.run(context, fn) } /** - * Executa função com novo contexto de trace + * Execute function with new trace context */ export function runWithNewContext(options: CreateContextOptions, fn: () => T): T { const context = createTraceContext(options) @@ -211,7 +212,7 @@ export function runWithNewContext(options: CreateContextOptions, fn: () => T) } /** - * Executa função assíncrona com contexto de trace + * Execute async function with trace context */ export async function runWithContextAsync( context: TraceContext, @@ -221,7 +222,7 @@ export async function runWithContextAsync( } /** - * Cria um novo span + * Create a new span */ export function createSpan(options: CreateSpanOptions): Span { const context = getCurrentContext() @@ -246,13 +247,13 @@ export function createSpan(options: CreateSpanOptions): Span { } /** - * Inicia um span no contexto atual + * Start a span in the current context */ export function startSpan(options: CreateSpanOptions): Span { const context = getOrCreateContext() const span = createSpan({ ...options, asChild: true }) - // Push span atual para stack e define novo como atual + // Push current span to stack and set new one as current if (context.currentSpan) { context.spanStack.push(context.currentSpan) } @@ -262,7 +263,7 @@ export function startSpan(options: CreateSpanOptions): Span { } /** - * Finaliza um span + * End a span */ export function endSpan(span: Span, status?: SpanStatus): void { if (span.ended) { @@ -274,7 +275,7 @@ export function endSpan(span: Span, status?: SpanStatus): void { span.status = status || 'ok' span.ended = true - // Pop span do stack no contexto + // Pop span from stack in context const context = getCurrentContext() if (context && context.currentSpan === span) { context.currentSpan = context.spanStack.pop() @@ -282,7 +283,7 @@ export function endSpan(span: Span, status?: SpanStatus): void { } /** - * Adiciona evento a um span + * Add event to a span */ export function addSpanEvent(span: Span, name: string, attributes?: Record): void { if (span.ended) { @@ -297,7 +298,7 @@ export function addSpanEvent(span: Span, name: string, attributes?: Record): void { if (span.ended) { @@ -308,7 +309,7 @@ export function setSpanAttributes(span: Span, attributes: Record unknown>( @@ -378,7 +379,7 @@ export function traced(name?: string) { } /** - * Wrapper para rastrear função + * Wrapper for tracing a function */ export function traceFunction unknown>( name: string, @@ -414,7 +415,7 @@ export function traceFunction unknown>( } /** - * Executa operação com span automático + * Execute operation with automatic span */ export async function withSpan( name: string, @@ -435,7 +436,7 @@ export async function withSpan( } /** - * Executa operação síncrona com span automático + * Execute sync operation with automatic span */ export function withSpanSync( name: string, @@ -455,10 +456,10 @@ export function withSpanSync( } } -// === Gerenciamento de Baggage === +// === Baggage Management === /** - * Define item no baggage + * Set item in baggage */ export function setBaggage(key: string, value: string | number | boolean): void { const context = getCurrentContext() @@ -468,7 +469,7 @@ export function setBaggage(key: string, value: string | number | boolean): void } /** - * Obtém item do baggage + * Get item from baggage */ export function getBaggage(key: string): string | number | boolean | undefined { const context = getCurrentContext() @@ -476,7 +477,7 @@ export function getBaggage(key: string): string | number | boolean | undefined { } /** - * Obtém todo o baggage + * Get all baggage */ export function getAllBaggage(): Baggage { const context = getCurrentContext() @@ -484,7 +485,7 @@ export function getAllBaggage(): Baggage { } /** - * Remove item do baggage + * Remove item from baggage */ export function removeBaggage(key: string): void { const context = getCurrentContext() @@ -493,10 +494,10 @@ export function removeBaggage(key: string): void { } } -// === Utilitários de Headers === +// === Header Utilities === /** - * Headers padrão para propagação de trace + * Standard headers for trace propagation */ export const TRACE_HEADERS = { TRACE_ID: 'x-trace-id', @@ -507,7 +508,7 @@ export const TRACE_HEADERS = { } as const /** - * Injeta contexto em headers HTTP + * Inject context into HTTP headers */ export function injectTraceHeaders(headers: Record): Record { const context = getCurrentContext() @@ -524,7 +525,7 @@ export function injectTraceHeaders(headers: Record): Record 0) { result[TRACE_HEADERS.BAGGAGE] = Object.entries(context.baggage) .map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`) @@ -535,7 +536,7 @@ export function injectTraceHeaders(headers: Record): Record): CreateContextOptions { const options: CreateContextOptions = {} @@ -568,7 +569,7 @@ export function extractTraceHeaders(headers: Record) } /** - * Exporta trace context para serialização + * Export trace context for serialization */ export function exportContext(context: TraceContext): string { return JSON.stringify({ @@ -579,7 +580,7 @@ export function exportContext(context: TraceContext): string { } /** - * Importa trace context de string serializada + * Import trace context from serialized string */ export function importContext(serialized: string): CreateContextOptions { try { @@ -599,19 +600,19 @@ export function importContext(serialized: string): CreateContextOptions { // === Timer Utilities === /** - * Timer de alta precisão + * High precision timer */ export interface PrecisionTimer { - /** Retorna tempo decorrido em milliseconds */ + /** Return elapsed time in milliseconds */ elapsed(): number - /** Retorna tempo decorrido formatado */ + /** Return formatted elapsed time */ elapsedFormatted(): string - /** Para o timer e retorna duração */ + /** Stop the timer and return duration */ stop(): number } /** - * Cria um timer de alta precisão + * Create a high precision timer */ export function createPrecisionTimer(): PrecisionTimer { const start = process.hrtime.bigint()