/** * Utilitários para otimização de performance */ // Cache otimizado com LRU (Least Recently Used) export class LRUCache { private cache = new Map(); private readonly maxSize: number; constructor(maxSize: number = 100) { this.maxSize = maxSize; } get(key: K): V | undefined { const value = this.cache.get(key); if (value !== undefined) { // Move para o final (mais recente) this.cache.delete(key); this.cache.set(key, value); } return value; } set(key: K, value: V): void { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.maxSize) { // Remove o mais antigo (primeiro da Map) const firstKey = this.cache.keys().next().value; this.cache.delete(firstKey); } this.cache.set(key, value); } clear(): void { this.cache.clear(); } size(): number { return this.cache.size; } } // Debounce otimizado export function debounce any>( func: T, wait: number, immediate?: boolean ): (...args: Parameters) => void { let timeout: NodeJS.Timeout | null = null; return function executedFunction(...args: Parameters) { const later = () => { timeout = null; if (!immediate) func(...args); }; const callNow = immediate && !timeout; if (timeout) clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func(...args); }; } // Throttle otimizado export function throttle any>( func: T, limit: number ): (...args: Parameters) => void { let inThrottle: boolean = false; return function executedFunction(...args: Parameters) { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // Memoização otimizada export function memoize any>( fn: T, getKey?: (...args: Parameters) => string ): T & { cache: LRUCache>; clearCache: () => void } { const cache = new LRUCache>(50); const memoized = function (...args: Parameters): ReturnType { const key = getKey ? getKey(...args) : JSON.stringify(args); const cached = cache.get(key); if (cached !== undefined) { return cached; } const result = fn(...args); cache.set(key, result); return result; } as T & { cache: LRUCache>; clearCache: () => void }; memoized.cache = cache; memoized.clearCache = () => cache.clear(); return memoized; } // Worker Pool para cálculos pesados export class WorkerPool { private workers: Worker[] = []; private queue: Array<{ resolve: (value: any) => void; reject: (error: any) => void; data: any; }> = []; private busyWorkers = new Set(); constructor(workerScript: string, poolSize: number = navigator.hardwareConcurrency || 4) { for (let i = 0; i < poolSize; i++) { const worker = new Worker(workerScript); worker.onmessage = (e) => this.handleWorkerMessage(worker, e); worker.onerror = (e) => this.handleWorkerError(worker, e); this.workers.push(worker); } } execute(data: any): Promise { return new Promise((resolve, reject) => { this.queue.push({ resolve, reject, data }); this.processQueue(); }); } private processQueue(): void { if (this.queue.length === 0) return; const availableWorker = this.workers.find(w => !this.busyWorkers.has(w)); if (!availableWorker) return; const task = this.queue.shift()!; this.busyWorkers.add(availableWorker); (availableWorker as any)._currentTask = task; availableWorker.postMessage(task.data); } private handleWorkerMessage(worker: Worker, e: MessageEvent): void { const task = (worker as any)._currentTask; if (task) { task.resolve(e.data); this.busyWorkers.delete(worker); delete (worker as any)._currentTask; this.processQueue(); } } private handleWorkerError(worker: Worker, e: ErrorEvent): void { const task = (worker as any)._currentTask; if (task) { task.reject(e); this.busyWorkers.delete(worker); delete (worker as any)._currentTask; this.processQueue(); } } terminate(): void { this.workers.forEach(worker => worker.terminate()); this.workers = []; this.queue = []; this.busyWorkers.clear(); } } // Lazy loading de imagens otimizado export class LazyImageLoader { private observer: IntersectionObserver; private images = new Set(); constructor(rootMargin: string = '50px') { this.observer = new IntersectionObserver( (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target as HTMLImageElement; this.loadImage(img); } }); }, { rootMargin } ); } observe(img: HTMLImageElement): void { this.images.add(img); this.observer.observe(img); } unobserve(img: HTMLImageElement): void { this.images.delete(img); this.observer.unobserve(img); } private loadImage(img: HTMLImageElement): void { const src = img.dataset.src; if (src) { img.src = src; img.onload = () => { img.classList.add('loaded'); this.observer.unobserve(img); }; } } disconnect(): void { this.observer.disconnect(); this.images.clear(); } } // Monitor de performance export class PerformanceMonitor { private metrics = new Map(); private observers: PerformanceObserver[] = []; constructor() { this.setupObservers(); } private setupObservers(): void { if ('PerformanceObserver' in window) { // Long tasks try { const longTaskObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { this.addMetric('long-task', entry.duration); } }); longTaskObserver.observe({ entryTypes: ['longtask'] }); this.observers.push(longTaskObserver); } catch (e) { // Long tasks não suportado } // Layout shifts try { const layoutShiftObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if ((entry as any).value && !(entry as any).hadRecentInput) { this.addMetric('layout-shift', (entry as any).value); } } }); layoutShiftObserver.observe({ entryTypes: ['layout-shift'] }); this.observers.push(layoutShiftObserver); } catch (e) { // Layout shift não suportado } } } addMetric(name: string, value: number): void { if (!this.metrics.has(name)) { this.metrics.set(name, []); } this.metrics.get(name)!.push(value); } getMetrics(): Record { const result: Record = {}; this.metrics.forEach((values, name) => { result[name] = { avg: values.reduce((a, b) => a + b, 0) / values.length, max: Math.max(...values), count: values.length }; }); return result; } clear(): void { this.metrics.clear(); } disconnect(): void { this.observers.forEach(observer => observer.disconnect()); this.observers = []; } } // Instâncias globais export const performanceMonitor = new PerformanceMonitor(); export const imageLoader = new LazyImageLoader(); // Hook para uso em React export function usePerformanceMetrics() { return { getMetrics: () => performanceMonitor.getMetrics(), addMetric: (name: string, value: number) => performanceMonitor.addMetric(name, value), clear: () => performanceMonitor.clear() }; }