UNPKG

performance-monitor-logger

Version:

## Instalação

1,690 lines 76.7 kB
import { Component, NgModule, ɵɵdefineInjectable, Injectable } from '@angular/core';
import { __awaiter } from 'tslib';
import { HttpResponse } from '@angular/common/http';
import { tap } from 'rxjs/operators';

class PerformanceMonitorComponent {
    constructor() { }
    ngOnInit() { }
}
PerformanceMonitorComponent.decorators = [
    { type: Component, args: [{
                selector: 'lib-performanceMonitorLogger',
                template: ` <p>performance-monitor-logger works!</p> `
            },] }
];
PerformanceMonitorComponent.ctorParameters = () => [];

// performance-monitor.module.ts
class PerformanceMonitorModule {
}
PerformanceMonitorModule.decorators = [
    { type: NgModule, args: [{
                declarations: [PerformanceMonitorComponent],
                imports: [],
                exports: [PerformanceMonitorComponent],
            },] }
];

class PerformanceMonitorService {
    constructor() { }
}
PerformanceMonitorService.ɵprov = ɵɵdefineInjectable({ factory: function PerformanceMonitorService_Factory() { return new PerformanceMonitorService(); }, token: PerformanceMonitorService, providedIn: "root" });
PerformanceMonitorService.decorators = [
    { type: Injectable, args: [{
                providedIn: 'root'
            },] }
];
PerformanceMonitorService.ctorParameters = () => [];

/**
 * 📊 SISTEMA DE LOGS DE PERFORMANCE
 *
 * Sistema completo de monitoramento de performance para aplicações Angular
 * com feedback visual através de emojis e relatórios automáticos.
 *
 * Características:
 * - 📈 Monitoramento em Tempo Real
 * - 🟢🟡🔴 Classificação Visual por Performance
 * - ⏰ Relatórios Automáticos após Inatividade
 * - 📊 Análise Detalhada com Recomendações
 * - 🛠️ API Pública para Controle Manual
 *
 * @author Pedro Henrique dos Santos Leuchs
 * @version 1.0.23
 * @since 2025-07-11
 */
class LogsPerformatico {
    constructor(serviceName = 'Service') {
        this.monitoramentoAtivo = true; // Começa ativo na primeira vez
        // 📊 Propriedades do Sistema
        this.performanceTimers = new Map();
        this.performanceSummary = new Map();
        this.httpRequests = new Map();
        // 🆕 Stack para contexto de função monitorada
        this.functionContextStack = [];
        this.eventContextStack = [];
        this.timerStack = [];
        // 🆕 Estado de atividade para fechamento inteligente de contexto de UI
        this.lastTimerActivityAt = null;
        this.firstTimerInEventAt = null;
        this.currentEventBeginAt = null;
        // 🔒 Controle de funções já instrumentadas
        this.instrumentedFns = new WeakMap();
        this.serviceName = serviceName;
    }
    /**
     * Registra requisição HTTP associando à função monitorada ativa (se houver)
     * @param payload Dados da requisição
     */
    registerHttpRequest(authReq, tempo, error = false) {
        var _a, _b;
        // Nome robusto da operação HTTP
        let nome = '(desconhecido)';
        try {
            const byBodyName = (_a = authReq === null || authReq === void 0 ? void 0 : authReq.body) === null || _a === void 0 ? void 0 : _a.name;
            const bySql = this.extrairNomeDeSQL((_b = authReq === null || authReq === void 0 ? void 0 : authReq.body) === null || _b === void 0 ? void 0 : _b.sqlInstruction);
            const method = (authReq === null || authReq === void 0 ? void 0 : authReq.method) || 'HTTP';
            const url = authReq === null || authReq === void 0 ? void 0 : authReq.url;
            // Usa nome planejado (body/sql), senão fallback para método + último segmento da URL
            if (byBodyName) {
                nome = byBodyName;
            }
            else if (bySql) {
                nome = bySql;
            }
            else if (url) {
                const cleanUrl = String(url).split('?')[0];
                const segs = cleanUrl.split('/').filter(Boolean);
                const tail = segs[segs.length - 1] || cleanUrl;
                nome = `${method} ${tail}`;
            }
            else {
                nome = method;
            }
        }
        catch (_c) { }
        const currentEvent = this.eventContextStack.length
            ? this.eventContextStack[this.eventContextStack.length - 1]
            : undefined;
        const key = currentEvent ? `${currentEvent} | ${nome}` : nome;
        const atual = this.httpRequests.get(key) || {
            tempoTotal: 0,
            execucoes: 0,
            errors: 0,
        };
        atual.tempoTotal += Number.isFinite(tempo) ? tempo : 0;
        atual.execucoes += 1;
        if (error)
            atual.errors += 1;
        this.httpRequests.set(key, atual);
    }
    // Função auxiliar para extrair nome da função de um SQL, ex: PACK_PARMGEN.CONSULTA_PARAMETRO(...)
    extrairNomeDeSQL(sql) {
        if (!sql)
            return null;
        const match = sql.match(/([A-Z0-9_]+\.[A-Z0-9_]+)/i);
        return match ? match[1] : null;
    }
    /**
     * 🚀 Mensagem de boas-vindas do sistema
     */
    showWelcomeMessage() {
        console.log(`%cSISTEMA DE MONITORAMENTO DE PERFORMANCE`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
    }
    /**
     * ⏱️ Inicia cronômetro para uma operação
     * @param operationName Nome da operação a ser monitorada
     */
    startTimer(operationName) {
        if (!this.monitoramentoAtivo)
            return;
        // Se for a primeira operação, abre borda visual
        if (this.performanceTimers.size === 0 &&
            this.performanceSummary.size === 0) {
            const border = '═'.repeat(56);
            console.log(`%cINÍCIO DO MONITORAMENTO DE PERFORMANCE`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
        }
        this.performanceTimers.set(operationName, Date.now());
        // Empilha contexto de função monitorada
        this.functionContextStack.push(operationName);
        // Adiciona contexto ao stack para medir tempo exclusivo
        const currentEvent = this.eventContextStack.length
            ? this.eventContextStack[0] // usa rótulo raiz do fluxo do usuário
            : undefined;
        // Se a operação vier como pack.metodo, usamos pack como evento e metodo como nome
        const parts = String(operationName).split('.');
        let displayEvent;
        let displayName;
        if (parts.length >= 2) {
            displayEvent = parts[0];
            displayName = parts[parts.length - 1];
        }
        else {
            // Sem prefixo (pack) no nome — exibe apenas o método
            displayEvent = undefined;
            displayName = operationName;
        }
        const fullName = displayEvent
            ? `${displayEvent} | ${displayName}`
            : displayName;
        // Registra atividade de timer para controle de janela do evento
        const nowStart = Date.now();
        if (currentEvent && this.firstTimerInEventAt === null) {
            this.firstTimerInEventAt = nowStart;
        }
        this.lastTimerActivityAt = nowStart;
        this.timerStack.push({
            name: operationName,
            start: nowStart,
            childElapsed: 0,
            event: currentEvent,
            fullName,
        });
        console.log(`║ [INÍCIO] ${fullName} - ${new Date().toLocaleTimeString()}`);
    }
    /**
     * 🏁 Finaliza cronômetro e exibe resultado com emoji
     * @param operationName Nome da operação
     * @param customMessage Mensagem customizada opcional (ex: para erros)
     * @param customThresholds Thresholds customizados para esta operação específica
     */
    endTimer(operationName, customMessage, customThresholds) {
        var _a, _b, _c, _d, _e, _f;
        if (!this.monitoramentoAtivo)
            return;
        // Usa pilha de contexto para calcular tempo exclusivo (exclui filhos)
        const now = Date.now();
        let ctx = this.timerStack.length
            ? this.timerStack[this.timerStack.length - 1]
            : undefined;
        if (!ctx || ctx.name !== operationName) {
            // Busca robusta caso encerramento fora de ordem
            let foundIndex = -1;
            for (let i = this.timerStack.length - 1; i >= 0; i--) {
                if (this.timerStack[i].name === operationName) {
                    foundIndex = i;
                    break;
                }
            }
            if (foundIndex === -1) {
                console.warn(`⚠️ Timer não encontrado para: ${operationName}`);
                return;
            }
            ctx = this.timerStack[foundIndex];
            this.timerStack.splice(foundIndex, 1);
        }
        else {
            this.timerStack.pop();
        }
        // Atualiza atividade de timers (para lógica de fechamento por silêncio)
        this.lastTimerActivityAt = now;
        const inclusiveElapsed = now - ctx.start;
        const exclusiveElapsed = Math.max(0, inclusiveElapsed - ctx.childElapsed);
        // Propaga tempo do filho para o pai (para excluir na contagem do pai)
        const parent = this.timerStack[this.timerStack.length - 1];
        if (parent) {
            parent.childElapsed += inclusiveElapsed;
        }
        // Remove referência antiga baseada em Map (mantida para compatibilidade)
        this.performanceTimers.delete(operationName);
        // Desempilha contexto de função monitorada (somente se topo)
        if (this.functionContextStack[this.functionContextStack.length - 1] ===
            operationName) {
            this.functionContextStack.pop();
        }
        // Determina status baseado no tempo exclusivo
        const { emoji, status, color } = this.getPerformanceStatus(exclusiveElapsed, customThresholds, (_a = ctx.fullName) !== null && _a !== void 0 ? _a : (ctx.event ? `${ctx.event} | ${operationName}` : operationName));
        // Log com emoji e cor (com mensagem customizada se fornecida)
        const message = customMessage
            ? `${(_b = ctx.fullName) !== null && _b !== void 0 ? _b : (ctx.event ? `${ctx.event} | ${operationName}` : operationName)} - ${customMessage}`
            : `${(_c = ctx.fullName) !== null && _c !== void 0 ? _c : (ctx.event ? `${ctx.event} | ${operationName}` : operationName)}`;
        const fixedStatus = `[${status}]`.padEnd(5);
        const fixedMessage = message.padEnd(10);
        const fixedTime = exclusiveElapsed.toString().padStart(5);
        console.log(`║ %c${emoji} ${fixedStatus} ${fixedMessage} - ${fixedTime}ms `, color);
        // Mostra thresholds customizados se estiverem sendo usados
        if (customThresholds &&
            (customThresholds.ok ||
                customThresholds.lento ||
                customThresholds.critico)) {
            console.log(`⚙️ Thresholds customizados: OK<${(_d = customThresholds.ok) !== null && _d !== void 0 ? _d : 500}ms, LENTO<${(_e = customThresholds.lento) !== null && _e !== void 0 ? _e : 2000}ms`);
        }
        // Atualiza sumário com tempo exclusivo
        this.updatePerformanceSummary((_f = ctx.fullName) !== null && _f !== void 0 ? _f : (ctx.event ? `${ctx.event} | ${operationName}` : operationName), exclusiveElapsed, customThresholds);
    }
    /**
     * 🎨 Determina status de performance baseado no tempo
     * @param elapsedTime Tempo decorrido em milissegundos
     * @param customThresholds Thresholds customizados para esta operação específica
     * @param operationName Nome da operação (para calcular thresholds dinâmicos)
     * @returns Objeto com emoji, status e cor
     */
    getPerformanceStatus(elapsedTime, customThresholds, operationName) {
        var _a, _b, _c;
        let okThreshold;
        let lentoThreshold;
        let criticoThreshold;
        if (customThresholds &&
            (customThresholds.ok ||
                customThresholds.lento ||
                customThresholds.critico)) {
            // Usa thresholds customizados se fornecidos
            okThreshold = (_a = customThresholds.ok) !== null && _a !== void 0 ? _a : 500;
            lentoThreshold = (_b = customThresholds.lento) !== null && _b !== void 0 ? _b : 2000;
            criticoThreshold = (_c = customThresholds.critico) !== null && _c !== void 0 ? _c : Number.MAX_SAFE_INTEGER;
        }
        else if (operationName) {
            // Calcula thresholds dinâmicos baseados na quantidade de execuções
            const stats = this.performanceSummary.get(operationName);
            // Corrige: soma +1 para considerar a execução atual
            const executionCount = stats ? stats.count + 1 : 1;
            const dynamicThresholds = this.calculateDynamicThresholds(executionCount);
            okThreshold = dynamicThresholds.ok;
            lentoThreshold = dynamicThresholds.lento;
            criticoThreshold = dynamicThresholds.critico;
        }
        else {
            // Usa thresholds padrões
            okThreshold = 500;
            lentoThreshold = 2000;
            criticoThreshold = Number.MAX_SAFE_INTEGER;
        }
        if (elapsedTime < okThreshold) {
            return {
                emoji: '🟢',
                status: 'OK',
                color: 'color: green; font-weight: bold;',
            };
        }
        else if (elapsedTime < lentoThreshold) {
            return {
                emoji: '🟡',
                status: 'LENTO',
                color: 'color: orange; font-weight: bold;',
            };
        }
        else {
            return {
                emoji: '🔴',
                status: 'CRÍTICO',
                color: 'color: red; font-weight: bold;',
            };
        }
    }
    /**
     * 📈 Atualiza estatísticas de performance para o sumário
     * @param operationName Nome da operação
     * @param elapsedTime Tempo decorrido em milissegundos
     * @param customThresholds Thresholds customizados para armazenar (opcional)
     */
    updatePerformanceSummary(operationName, elapsedTime, customThresholds) {
        const existing = this.performanceSummary.get(operationName);
        if (existing) {
            existing.count++;
            existing.totalTime += elapsedTime;
            existing.minTime = Math.min(existing.minTime, elapsedTime);
            existing.maxTime = Math.max(existing.maxTime, elapsedTime);
            // Atualiza thresholds customizados se fornecidos
            if (customThresholds &&
                (customThresholds.ok ||
                    customThresholds.lento ||
                    customThresholds.critico)) {
                existing.customThresholds = customThresholds;
            }
        }
        else {
            this.performanceSummary.set(operationName, {
                count: 1,
                totalTime: elapsedTime,
                minTime: elapsedTime,
                maxTime: elapsedTime,
                customThresholds: customThresholds,
            });
        }
        // Não faz mais nada aqui, relatório só manual
    }
    /**
     * 📊 Exibe relatório resumido de todas as operações (compactado)
     */
    showPerformanceSummary(showFullDetails = false) {
        // Fecha borda do monitoramento ao gerar relatório
        console.log('%c RELATÓRIO DE PERFORMANCE', 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
        if (this.performanceSummary.size === 0) {
            console.log('%c Nenhuma operação monitorada ainda.', 'color: gray; font-style: italic;');
            return;
        }
        const sortedEntries = Array.from(this.performanceSummary.entries()).sort((a, b) => b[1].maxTime - a[1].maxTime);
        const table = Object.fromEntries(sortedEntries.map(([operation, stats]) => {
            const avgTime = Math.round(stats.totalTime / stats.count);
            const { emoji, status } = this.getPerformanceStatus(avgTime, stats.customThresholds, operation);
            return [
                operation,
                {
                    Execuções: stats.count,
                    'Média(ms)': `${avgTime}ms (${status}) ${emoji}`.trim(),
                    'Mínimo(ms)': stats.minTime,
                    'Máximo(ms)': stats.maxTime,
                    'Total(ms)': stats.totalTime,
                },
            ];
        }));
        console.table(table);
        // Removido: agora a análise detalhada é chamada apenas pelo botão
    }
    /**
     * 📑 Exibe relatório detalhado de requisições HTTP agrupadas por função
     */
    showHttpRequestsReport() {
        console.log(`%cRELATÓRIO DETALHADO DE REQUISIÇÕES HTTP`, 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
        if (this.httpRequests.size === 0) {
            console.log('Nenhuma requisição HTTP registrada.');
            return;
        }
        const tabela = Object.fromEntries(Array.from(this.httpRequests.entries())
            .sort((a, b) => b[1].tempoTotal - a[1].tempoTotal)
            .map(([nome, dados]) => [
            nome,
            {
                Execuções: dados.execucoes,
                Média: `${Math.round(dados.tempoTotal / dados.execucoes)}ms`,
                'Tempo Total': `${Math.round(dados.tempoTotal)}ms`,
                Erros: dados.errors,
            },
        ]));
        console.table(tabela);
    }
    /**
     * 🔍 Gera análise detalhada de performance
     */
    generateDetailedAnalysis() {
        // Título discreto
        console.log('%cANÁLISE DETALHADA DE PERFORMANCE', 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
        console.log('');
        if (this.performanceSummary.size === 0) {
            console.log('%cNenhuma operação registrada para análise.', 'color: #bbb; font-style: italic; font-size: 14px; padding: 4px 0 4px 10px;');
            return;
        }
        const { criticalOperations, slowOperations, fastOperations } = this.categorizeOperations();
        // Helper para bloco discreto com texto claro
        const printBlock = (color, emoji, operation, avg, extra) => {
            const style = `border-left: 4px solid ${color}; padding: 2px 0 2px 10px; margin: 2px 0 2px 0; font-size: 14px; color: #fff; background: none; font-weight: normal;`;
            console.log(`%c${emoji} ${operation}  -  ${avg}ms (média)  ${extra}`.trim(), style);
        };
        if (criticalOperations.length > 0) {
            console.log('%c🔴 CRÍTICAS ( > 2000ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
            criticalOperations.forEach(([operation, stats]) => {
                const avg = Math.round(stats.totalTime / stats.count);
                const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
                printBlock('#c53030', emoji, operation, avg, '| otimizar urgente');
            });
        }
        if (slowOperations.length > 0) {
            console.log('%c🟡 LENTAS (500ms - 2000ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
            slowOperations.forEach(([operation, stats]) => {
                const avg = Math.round(stats.totalTime / stats.count);
                const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
                printBlock('#b7791f', emoji, operation, avg, '| pode ser melhorada');
            });
        }
        if (fastOperations.length > 0) {
            console.log('%c🟢 RÁPIDAS ( < 500ms)', 'color: #fff; font-size: 14px; font-weight: bold; margin-top: 10px;');
            fastOperations.forEach(([operation, stats]) => {
                const avg = Math.round(stats.totalTime / stats.count);
                const { emoji } = this.getPerformanceStatus(avg, stats.customThresholds, operation);
                printBlock('#38a169', emoji, operation, avg, '| excelente');
            });
        }
        // Recomendações
        console.log('%cRecomendações:', 'color: #fff; font-size: 13px; font-weight: bold; margin-top: 14px; padding: 2px 0 2px 0;');
        if (criticalOperations.length > 0) {
            console.log('%c• Priorize a otimização das operações críticas.', 'color: #fff; font-size: 13px; font-weight: normal;');
            console.log('%c• Considere implementar cache ou otimizar consultas SQL para essas operações.', 'color: #fff; font-size: 13px; font-weight: normal;');
        }
        if (slowOperations.length > 0) {
            console.log('%c• Operações lentas podem se beneficiar de índices no banco de dados.', 'color: #fff; font-size: 13px; font-weight: normal;');
        }
        if (criticalOperations.length === 0 && slowOperations.length === 0) {
            console.log('%c• Todas as operações estão com performance excelente!', 'color: #fff; font-size: 13px; font-weight: normal;');
        }
        // Score
        const totalOperations = this.performanceSummary.size;
        const fastCount = fastOperations.length;
        const performanceScore = totalOperations > 0
            ? Math.round((fastCount / totalOperations) * 100)
            : 100;
        let scoreColor = performanceScore >= 80
            ? '#38a169'
            : performanceScore >= 60
                ? '#f6e05e'
                : '#c53030';
        let scoreMsg = performanceScore >= 80
            ? 'Excelente! A maioria das operações está performando bem.'
            : performanceScore >= 60
                ? 'Médio. Há espaço para melhorias de performance.'
                : 'Crítico. Muitas operações precisam de otimização urgente.';
        console.log(`%cScore de performance: ${performanceScore}%`, `color: #fff; background: ${scoreColor}; font-size: 14px; font-weight: bold; margin-top: 10px; padding: 2px 8px; border-radius: 3px;`);
        console.log(`%c${scoreMsg}`, `color: #fff; font-size: 13px; font-weight: normal; margin-bottom: 8px;`);
        console.log('');
    }
    /**
     * 📋 Categoriza operações por performance
     * @returns Objeto com arrays de operações categorizadas
     */
    categorizeOperations() {
        const criticalOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => stats.totalTime / stats.count > 2000);
        const slowOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => {
            const avg = stats.totalTime / stats.count;
            return avg >= 500 && avg <= 2000;
        });
        const fastOperations = Array.from(this.performanceSummary.entries()).filter(([_, stats]) => stats.totalTime / stats.count < 500);
        return { criticalOperations, slowOperations, fastOperations };
    }
    /**
     * 💡 Exibe recomendações baseadas na análise
     * @param criticalOperations Array de operações críticas
     * @param slowOperations Array de operações lentas
     */
    showRecommendations(criticalOperations, slowOperations) {
        console.log('RECOMENDAÇÕES:');
        if (criticalOperations.length > 0) {
            console.log('   Priorize a otimização das operações críticas.');
            console.log('   Considere implementar cache ou otimizar consultas SQL para essas operações.');
        }
        if (slowOperations.length > 0) {
            console.log('   Operações lentas podem se beneficiar de índices no banco de dados.');
        }
        if (criticalOperations.length === 0 && slowOperations.length === 0) {
            console.log('   Todas as operações estão com performance excelente!');
        }
    }
    /**
     * 📊 Exibe score de performance
     * @param fastOperationsCount Número de operações rápidas
     */
    showPerformanceScore(fastOperationsCount) {
        const totalOperations = this.performanceSummary.size;
        const performanceScore = Math.round((fastOperationsCount / totalOperations) * 100);
        console.log(`SCORE DE PERFORMANCE: ${performanceScore}%`);
        if (performanceScore >= 80) {
            console.log('Excelente! A maioria das operações está performando bem.');
        }
        else if (performanceScore >= 60) {
            console.log('Médio. Há espaço para melhorias de performance.');
        }
        else {
            console.log('Crítico. Muitas operações precisam de otimização urgente.');
        }
    }
    // ===== API PÚBLICA =====
    /**
     * 📊 Método público para exibir relatório de performance
     * Pode ser chamado no console do navegador para análise
     */
    getPerformanceReport() {
        this.showPerformanceSummary();
    }
    /**
     * 🏷️ Controla contexto de evento atual (ex.: AppInit, Click)
     */
    beginEventContext(label) {
        if (label) {
            // Se não há contexto aberto, inicia uma nova janela raiz
            if (this.eventContextStack.length === 0) {
                this.currentEventBeginAt = Date.now();
                this.firstTimerInEventAt = null;
                this.lastTimerActivityAt = null;
            }
            this.eventContextStack.push(label);
        }
    }
    endEventContext() {
        if (this.eventContextStack.length > 0) {
            this.eventContextStack.pop();
        }
    }
    runInEventContext(label, fn) {
        return __awaiter(this, void 0, void 0, function* () {
            this.beginEventContext(label);
            try {
                const result = fn();
                return yield Promise.resolve(result);
            }
            finally {
                this.endEventContext();
            }
        });
    }
    /**
     * 🧹 Método público para limpar dados de performance
     * Útil para resetar estatísticas e começar nova análise
     */
    resetPerformanceData() {
        this.performanceTimers.clear();
        this.performanceSummary.clear();
        this.httpRequests.clear();
        // Mensagem estilizada de limpeza
        console.log('%c🧹 [CLEANUP] Dados de performance limpos', 'color: #fff; background: #43248bff; font-size: 13px; font-weight: bold; border-radius: 4px; padding: 4px 12px; margin: 6px 0;');
    }
    /**
     * ⏰ Configura o delay para relatório por inatividade
     * @param seconds Número de segundos de inatividade para mostrar relatório
     */
    setInactivityDelay(seconds) {
        // Desabilitado
        console.log('⏰ Relatório por inatividade está desabilitado.');
    }
    /**
     * 🔇 Desabilita relatório automático por inatividade
     */
    disableInactivityReport() {
        // Desabilitado
        console.log('🔇 Relatório por inatividade já está desabilitado');
    }
    /**
     * ⚙️ Configura o threshold para relatório automático
     * @param threshold Número de operações para mostrar relatório automaticamente
     */
    setAutoReportThreshold(threshold) {
        // Desabilitado
        console.log('⚙️ Threshold para relatório automático está desabilitado.');
    }
    /**
     * 🔇 Desabilita relatório automático
     */
    disableAutoReport() {
        // Desabilitado
        console.log('🔇 Relatório automático desabilitado');
    }
    /**
     * 📈 Obtém estatísticas resumidas sem exibir no console
     * @returns Objeto com estatísticas de performance
     */
    getStatistics() {
        const { criticalOperations, slowOperations, fastOperations } = this.categorizeOperations();
        const totalOperations = this.performanceSummary.size;
        let totalTime = 0;
        let totalCount = 0;
        this.performanceSummary.forEach((stats) => {
            totalTime += stats.totalTime;
            totalCount += stats.count;
        });
        const averageTime = totalCount > 0 ? Math.round(totalTime / totalCount) : 0;
        const performanceScore = totalOperations > 0
            ? Math.round((fastOperations.length / totalOperations) * 100)
            : 100;
        return {
            totalOperations,
            averageTime,
            fastOperations: fastOperations.length,
            slowOperations: slowOperations.length,
            criticalOperations: criticalOperations.length,
            performanceScore,
        };
    }
    /**
     * 🎯 Obtém dados de uma operação específica
     * @param operationName Nome da operação
     * @returns Estatísticas da operação ou null se não encontrada
     */
    getOperationStats(operationName) {
        const baseStats = this.performanceSummary.get(operationName);
        if (!baseStats)
            return null;
        // Extrai nomes das requisições HTTP como array de strings
        const httpRequestEntry = this.httpRequests.get(operationName);
        const httpCalls = httpRequestEntry ? [operationName] : [];
        return Object.assign(Object.assign({}, baseStats), { httpCalls });
    }
    /**
     * 📋 Lista todas as operações monitoradas
     * @returns Array com nomes das operações
     */
    getMonitoredOperations() {
        return Array.from(this.performanceSummary.keys());
    }
    /**
     * 🔄 Verifica se há timers ativos
     * @returns true se há timers em execução
     */
    hasActiveTimers() {
        return this.timerStack.length > 0;
    }
    /**
     * 🗑️ Limpa apenas os timers ativos (não as estatísticas)
     */
    clearActiveTimers() {
        this.performanceTimers.clear();
        this.timerStack = [];
        this.functionContextStack = [];
        console.log('%c🗑️ [CLEANUP] Timers ativos limpos', 'color: #fff; background: #23272f; font-size: 13px; font-weight: bold; border-radius: 4px; padding: 4px 12px; margin: 6px 0;');
    }
    // ===== AUTO‑INSTRUMENTAÇÃO (sem decorators) =====
    /**
     * 🔌 Instrumenta todas as funções públicas de um objeto/instância sem alterar assinatura
     * @param obj Objeto ou instância a instrumentar
     * @param options Filtros e ajustes de nome
     */
    instrumentObject(obj, options) {
        if (!obj || typeof obj !== 'object')
            return;
        const opt = Object.assign({ preserveToString: true }, options);
        const shouldInclude = (name) => {
            const inc = opt.include;
            const exc = opt.exclude;
            const match = (p) => typeof p === 'string' ? name.includes(p) : p.test(name);
            const okInc = !inc || inc.some(match);
            const okExc = exc ? !exc.some(match) : true;
            return okInc && okExc;
        };
        const instrumentProp = (target, key, path) => {
            try {
                const value = target[key];
                if (typeof value !== 'function')
                    return;
                const original = value;
                if (original.__pm_instrumented)
                    return;
                if (!shouldInclude(key))
                    return;
                const opBase = key;
                const opName = opt.nameTransform
                    ? opt.nameTransform(opBase, path)
                    : opBase;
                const wrapped = this.createWrapper(original, opName, opt.preserveToString);
                // Sombra no objeto (não altera prototype global)
                Object.defineProperty(target, key, {
                    value: wrapped,
                    writable: true,
                    configurable: true,
                });
            }
            catch (_a) { }
        };
        // Instrumenta próprias propriedades
        Object.getOwnPropertyNames(obj).forEach((k) => instrumentProp(obj, k, [k]));
        // Instrumenta métodos do prototype (sobrescrevendo no instance)
        const proto = Object.getPrototypeOf(obj);
        if (proto && proto !== Object.prototype) {
            Object.getOwnPropertyNames(proto)
                .filter((k) => k !== 'constructor')
                .forEach((k) => instrumentProp(obj, k, [k]));
        }
    }
    /**
     * 🌐 Instrumenta funções em um namespace (ex.: window) com filtros e profundidade
     * @param root Objeto raiz (ex.: window)
     * @param options include/exclude, nameTransform, preserveToString, maxDepth
     */
    instrumentNamespace(root, options) {
        if (!root || typeof root !== 'object')
            return;
        const opt = Object.assign({ preserveToString: true, maxDepth: 2 }, options);
        const shouldInclude = (name) => {
            const inc = opt.include;
            const exc = opt.exclude;
            const match = (p) => typeof p === 'string' ? name.includes(p) : p.test(name);
            const okInc = !inc || inc.some(match);
            const okExc = exc ? !exc.some(match) : true;
            return okInc && okExc;
        };
        const visited = new WeakSet();
        const walk = (node, depth, path) => {
            if (!node || typeof node !== 'object')
                return;
            if (visited.has(node))
                return;
            visited.add(node);
            if (depth > (opt.maxDepth || 2))
                return;
            const keys = [];
            try {
                keys.push(...Object.getOwnPropertyNames(node));
            }
            catch (_a) { }
            for (const key of keys) {
                let value;
                try {
                    value = node[key];
                }
                catch (_b) {
                    continue;
                }
                if (typeof value === 'function') {
                    const original = value;
                    if (original.__pm_instrumented)
                        continue;
                    if (!shouldInclude(key))
                        continue;
                    const opBase = key;
                    const opName = opt.nameTransform
                        ? opt.nameTransform(opBase, path.concat(key))
                        : opBase;
                    const wrapped = this.createWrapper(original, opName, opt.preserveToString);
                    // Tenta sobrescrever função no namespace
                    try {
                        Object.defineProperty(node, key, {
                            value: wrapped,
                            writable: true,
                            configurable: true,
                        });
                    }
                    catch (_c) { }
                }
                else if (typeof value === 'object' && value) {
                    // Recurse
                    walk(value, depth + 1, path.concat(key));
                }
            }
        };
        walk(root, 0, []);
    }
    /**
     * 🪝 Envolve a função setService(target.setService) para instrumentar o serviço passado
     * @param target Objeto que possui setService(service)
     * @param label Rótulo usado no nome das operações (ex.: "packTela")
     * @param options Opções de instrumentação (nameTransform, exclude, etc.)
     */
    hookSetService(target, label, options) {
        var _a;
        if (!target || typeof target !== 'object')
            return;
        const fn = target.setService;
        if (typeof fn !== 'function')
            return;
        const alreadyHooked = fn.__pm_setservice_hooked;
        if (alreadyHooked)
            return;
        const opt = Object.assign({ preserveToString: true, exclude: ['constructor'] }, options);
        const labelFinal = label ||
            (((_a = target === null || target === void 0 ? void 0 : target.constructor) === null || _a === void 0 ? void 0 : _a.name) ? String(target.constructor.name) : 'service');
        // Garante que o label (pack) entre na path[0] quando houver nameTransform customizado
        const nameTransform = opt.nameTransform
            ? (n, path) => opt.nameTransform(n, [labelFinal].concat(path || []))
            : (n) => `${labelFinal}.${n}`;
        const original = fn;
        const logger = this;
        const wrapped = function (serviceInstance, ...rest) {
            try {
                // Instrumenta o próprio objeto alvo (pack), pois são seus métodos que queremos medir
                const packObj = this || target;
                if (packObj && typeof packObj === 'object') {
                    const alreadyInstrumentedPack = packObj
                        .__pm_pack_instrumented;
                    if (!alreadyInstrumentedPack) {
                        logger.instrumentObject(packObj, Object.assign(Object.assign({}, opt), { nameTransform }));
                        try {
                            Object.defineProperty(packObj, '__pm_pack_instrumented', {
                                value: true,
                                configurable: true,
                                enumerable: false,
                                writable: false,
                            });
                        }
                        catch (_a) { }
                    }
                }
            }
            catch (_b) { }
            return original.apply(this, [serviceInstance, ...rest]);
        };
        // marca como hookado para evitar duplicação
        wrapped.__pm_setservice_hooked = true;
        try {
            Object.defineProperty(target, 'setService', {
                value: wrapped,
                writable: true,
                configurable: true,
            });
        }
        catch (_b) { }
        // Instrumenta imediatamente o pack (cobre casos onde setService já foi chamado antes do hook)
        try {
            const packObj = target;
            const alreadyInstrumentedPack = packObj.__pm_pack_instrumented;
            if (!alreadyInstrumentedPack) {
                this.instrumentObject(packObj, Object.assign(Object.assign({}, opt), { nameTransform }));
                Object.defineProperty(packObj, '__pm_pack_instrumented', {
                    value: true,
                    configurable: true,
                    enumerable: false,
                    writable: false,
                });
            }
        }
        catch (_c) { }
    }
    /**
     * 🪝 Hook de setService para vários alvos em uma única chamada
     * @param targets Mapa de label -> objeto que contém setService
     * @param options Opções de instrumentação
     */
    hookSetServiceBulk(targets, options) {
        if (!targets || typeof targets !== 'object')
            return;
        for (const [label, obj] of Object.entries(targets)) {
            const hasSetService = !!(obj && typeof obj.setService === 'function');
            if (hasSetService) {
                this.hookSetService(obj, label, options);
            }
            else if (obj && typeof obj === 'object') {
                // Instrumenta objetos sem setService usando o label como prefixo
                const opt = Object.assign({}, options);
                const nameTransform = opt.nameTransform
                    ? (n, path) => opt.nameTransform(n, [String(label)].concat(path || []))
                    : (n) => `${String(label)}.${n}`;
                this.instrumentObject(obj, Object.assign(Object.assign({}, opt), { nameTransform, exclude: ['constructor', ...(opt.exclude || [])] }));
            }
        }
    }
    /**
     * 🌐 Varre um namespace e aplica hook em qualquer função setService encontrada
     * @param root Objeto raiz (ex.: window, module scope wrapper, etc.)
     * @param options Filtros e regras de nomeação
     */
    hookSetServiceNamespace(root, options) {
        if (!root || typeof root !== 'object')
            return;
        const opt = Object.assign({ preserveToString: true, exclude: ['constructor'], maxWalkDepth: 2 }, options);
        const shouldIncludeObj = (pathStr) => {
            const inc = opt.includeObjects;
            const exc = opt.excludeObjects;
            const match = (p) => typeof p === 'string' ? pathStr.includes(p) : p.test(pathStr);
            const okInc = !inc || inc.some(match);
            const okExc = exc ? !exc.some(match) : true;
            return okInc && okExc;
        };
        const visited = new WeakSet();
        const walk = (node, depth, path) => {
            if (!node || typeof node !== 'object')
                return;
            if (visited.has(node))
                return;
            visited.add(node);
            if (depth > (opt.maxWalkDepth || 2))
                return;
            const keys = [];
            try {
                keys.push(...Object.getOwnPropertyNames(node));
            }
            catch (_a) { }
            for (const key of keys) {
                let value;
                try {
                    value = node[key];
                }
                catch (_b) {
                    continue;
                }
                const pathNow = path.concat(key);
                const pathStr = pathNow.join('.');
                if (value && typeof value === 'object') {
                    // Se o objeto tiver setService, aplica hook
                    const fn = value.setService;
                    if (typeof fn === 'function' && shouldIncludeObj(pathStr)) {
                        const labelFromPath = opt.labelFromPath || ((p) => p.join('.'));
                        this.hookSetService(value, labelFromPath(pathNow), opt);
                    }
                    // Continua a varredura
                    walk(value, depth + 1, pathNow);
                }
            }
        };
        walk(root, 0, []);
    }
    // ===== Helpers =====
    /**
     * Cria wrapper que inicia/encerra timer preservando contexto e toString()
     */
    createWrapper(original, opName, preserveToString) {
        // Idempotência: reutiliza wrapper se já existir
        const existing = this.instrumentedFns.get(original);
        if (existing)
            return existing;
        const logger = this;
        const wrapped = function (...args) {
            // Determina nome final com fallback ao nome do arquivo (pack) via stacktrace
            let finalName = opName;
            try {
                const parts = String(opName).split('.');
                const methodName = parts[parts.length - 1];
                const prefix = parts.length > 1 ? parts[0] : undefined;
                if (!prefix || prefix.toLowerCase() === methodName.toLowerCase()) {
                    const fileLabel = logger.guessFileLabelFromStack();
                    if (fileLabel) {
                        finalName = `${fileLabel}.${methodName}`;
                    }
                }
            }
            catch (_a) { }
            // Herda label do evento ativo (já garantido pelo logger)
            logger.startTimer(finalName);
            try {
                const result = original.apply(this, args);
                if (result && typeof result.then === 'function') {
                    // Promises/async
                    return result.finally(() => {
                        logger.endTimer(finalName);
                    });
                }
                else {
                    logger.endTimer(finalName);
                    return result;
                }
            }
            catch (e) {
                logger.endTimer(finalName, 'erro');
                throw e;
            }
        };
        // Marca instrumentado
        original.__pm_instrumented = true;
        wrapped.__pm_instrumented = true;
        // Preserva toString() para compatibilidade com regex
        if (preserveToString) {
            try {
                Object.defineProperty(wrapped, 'toString', {
                    value: function () {
                        return original.toString();
                    },
                    writable: false,
                    configurable: true,
                });
            }
            catch (_a) { }
        }
        // Tenta igualar o nome (nem sempre é possível)
        try {
            Object.defineProperty(wrapped, 'name', {
                value: original.name || opName,
                configurable: true,
            });
        }
        catch (_b) { }
        this.instrumentedFns.set(original, wrapped);
        return wrapped;
    }
    /**
     * 🔎 Tenta inferir o nome do arquivo (pack) a partir do stacktrace
     * Preferência por nomes que começam com "pack"
     */
    guessFileLabelFromStack() {
        try {
            const err = new Error();
            const stack = String(err.stack || '');
            const lines = stack.split('\n');
            const candidates = [];
            for (const line of lines) {
                const m = line.match(/[\\\/]([A-Za-z0-9_-]+)\.(?:ts|js)(?::\d+:\d+|\?|$)/);
                if (m && m[1]) {
                    const base = m[1];
                    if (/^pack/i.test(base)) {
                        return base;
                    }
                    candidates.push(base);
                }
            }
            return candidates[0];
        }
        catch (_a) {
            return undefined;
        }
    }
    /**
     * 💾 Exporta dados de performance para JSON
     * @returns String JSON com todos os dados
     */
    exportData() {
        const data = {
            timestamp: new Date().toISOString(),
            serviceName: this.serviceName,
            statistics: this.getStatistics(),
            operations: Array.from(this.performanceSummary.entries()).map(([name, stats]) => (Object.assign(Object.assign({ name }, stats), { averageTime: Math.round(stats.totalTime / stats.count) }))),
        };
        return JSON.stringify(data, null, 2);
    }
    /**
     * 🎯 Calcula thresholds dinâmicos baseados na quantidade de execuções
     * @param executionCount Número de execuções da operação
     * @returns Objeto com thresholds calculados dinamicamente
     */
    calculateDynamicThresholds(executionCount) {
        if (executionCount === 1) {
            // Primeira execução - expectativas mais relaxadas
            return {
                ok: 1000,
                lento: 2000,
                critico: 2000,
            };
        }
        else if (executionCount <= 5) {
            // Até 5 execuções - sistema ainda aquecendo
            return {
                ok: 500,
                lento: 1000,
                critico: 1000,
            };
        }
        else if (executionCount <= 10) {
            // Até 10 execuções - sistema otimizado
            return {
                ok: 100,
                lento: 250,
                critico: 250,
            };
        }
        else {
            // Acima de 10 execuções - expectativas altas
            return {
                ok: 20,
                lento: 60,
                critico: 60,
            };
        }
    }
    injectMonitorButton() {
        if (document.getElementById('btn-monitorar-performance'))
            return;
        // ====== ESTILO MODERNO E BOTÃO DE OLHO ======
        const BUTTON_COLOR = '#23272f'; // cinza escuro elegante
        const BUTTON_TEXT_COLOR = '#f3f4f6';
        const BUTTON_RADIUS = '8px';
        const BUTTON_FONT_SIZE = '13px';
        const BUTTON_PADDING = '10px 5px';
        const BUTTON_WIDTH = '210px';
        const BUTTON_HEIGHT = '44px';
        const BUTTON_BOX_SHADOW = '0 2px 12px #0002';
        const BUTTON_BORDER = '1px solid #353a40';
        // Container para animação
        const container = document.createElement('div');
        container.id = 'performance-monitor-container';
        Object.assign(container.style, {
            position: 'fixed',
            bottom: '5px',
            left: '50px',
            zIndex: '9999',
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'flex-end',
            transition: 'transform 0.5s cubic-bezier(.4,2,.6,1)',
            gap: '10px',
        });
        // Função para criar botões padronizados
        const criarBotao = (id, texto) => {
            const btn = document.createElement('button');
            btn.id = id;
            btn.innerText = texto;
            Object.assign(btn.style, {
                width: BUTTON_WIDTH,
                height: BUTTON_HEIGHT,
                background: BUTTON_COLOR,
                color: BUTTON_TEXT_COLOR,
                borderRadius: BUTTON_RADIUS,
                border: BUTTON_BORDER,
                padding: BUTTON_PADDING,
                fontSize: BUTTON_FONT_SIZE,
                fontWeight: '600',
                cursor: 'pointer',
                boxShadow: BUTTON_BOX_SHADOW,
                letterSpacing: '0.01em',
                textAlign: 'center',
                transition: 'background 0.2s, color 0.2s, transform 0.5s cubic-bezier(.4,2,.6,1)',
            });
            btn.onmouseenter = () => {
                btn.style.background = '#353a40';
                btn.style.color = '#fff';
            };
            btn.onmouseleave = () => {
                btn.style.background = BUTTON_COLOR;
                btn.style.color = BUTTON_TEXT_COLOR;
            };
            return btn;
        };
        // Botões principais
        const btnMain = criarBotao('btn-monitorar-performance', 'Parar monitoramento');
        const btnFinalizar = criarBotao('btn-finalizar-monitoramento', 'Finalizar monitoramento');
        const btnRequisicoesDetalhadas = criarBotao('btn-req-detalhadas-performance', 'Requisições detalhadas');
        const btnAnaliseDetalhada = criarBotao('btn-analise-detalhada-performance', 'Análise detalhada');
        const btnRelatorioEvento = criarBotao('btn-relatorio-evento-performance', 'Relatório separado');
        // Inicialmente escondidos
        btnRequisicoesDetalhadas.style.display = 'none';
        btnAnaliseDetalhada.style.display = 'none';
        btnFinalizar.style.display = 'none';
        btnRelatorioEvento.style.display = 'none';
        // Botão de esconder/mostrar com ícone de olho (fora do container)
        const btnToggle = document.createElement('button');
        btnToggle.id = 'btn-toggle-performance';
        btnToggle.innerHTML = `
    <img
      src="https://cdn-icons-png.flaticon.com/512/565/565655.png"
      alt="esconder"
      style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
    />
    `;
        Object.assign(btnToggle.style, {
            width: '38px',
            height: '38px',
            background: '#23272f',
            color: '#f3f4f6',
            borderRadius: '50%',
            border: BUTTON_BORDER,
            fontSize: '20px',
            fontWeight: 'bold',
            cursor: 'pointer',
            boxShadow: BUTTON_BOX_SHADOW,
            position: 'fixed',
            left: '5px',
            bottom: '5px',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
            zIndex: '10000',
        });
        btnToggle.onmouseenter = () => {
            btnToggle.style.background = '#353a40';
            btnToggle.style.color = '#fff';
        };
        btnToggle.onmouseleave = () => {
            btnToggle.style.background = '#23272f';
            btnToggle.style.color = '#f3f4f6';
        };
        let hidden = false;
        btnToggle.onclick = () => {
            hidden = !hidden;
            if (hidden) {
                container.style.display = 'none';
                btnToggle.innerHTML = `
    <img
      src="https://cdn-icons-png.flaticon.com/512/159/159604.png"
      alt="mostrar"
      style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
    />
  `;
            }
            else {
                container.style.display = 'flex';
                btnToggle.innerHTML = `
    <img
      src="https://cdn-icons-png.flaticon.com/512/565/565655.png"
      alt="esconder"
      style="width:15px; height:15px; vertical-align:middle; filter: invert(1);"
    />
  `;
            }
        };
        // Estado dos botões
        let estado = 'monitorando';
        const atualizarBotoes = (novoEstado) => {
            estado = novoEstado;
            switch (estado) {
                case 'inativo':
                    btnMain.innerText = 'Iniciar monitoramento';
                    this.monitoramentoAtivo = false;
                    btnFinalizar.style.display = 'none';
                    btnRequisicoesDetalhadas.style.display = 'none';
                    btnAnaliseDetalhada.style.display = 'none';
                    btnRelatorioEvento.style.display = 'none';
                    break;
                case 'monitorando':
                    btnMain.innerText = 'Parar monitoramento';
                    this.monitoramentoAtivo = true;
                    btnFinalizar.style.display = 'none';
                    btnRequisicoesDetalhadas.style.display = 'none';
                    btnAnaliseDetalhada.style.display = 'none';
                    btnRelatorioEvento.style.display = 'none';
                    break;
                case 'relatorio':
                    btnMain.innerText = 'Gerar relatório';
                    this.monitoramentoAtivo = false;
                    btnFinalizar.style.display = 'none';
                    btnRequisicoesDetalhadas.style.display = 'block';
                    btnAnaliseDetalhada.style.display = 'block';
                    btnRelatorioEvento.style.display = 'block';
                    break;
                case 'relatorio_gerado':
                    btnMain.innerText = 'Gerar relatório';
                    this.monitoramentoAtivo = false;
                    btnFinalizar.style.display = 'block';
                    btnRequisicoesDetalhadas.style.display = 'block';
                    btnAnaliseDetalhada.style.display = 'block';
                    btnRelatorioEvento.style.display = 'block';
                    break;
            }
        };
        // Ações dos botões
        btnMain.onclick = () => {
            if (estado === 'inativo') {
                this.resetPerformanceData();
                this.showWelcomeMessage();
                atualizarBotoes('monitorando');
            }
            else if (estado === 'monitorando') {
                atualizarBotoes('relatorio');
            }
            else if (estado === 'relatorio' || estado === 'relatorio_gerado') {
                this.showPerformanceSummary(false);
                atualizarBotoes('relatorio_gerado');
            }
        };
        btnFinalizar.onclick = () => {
            this.resetPerformanceData();
            atualizarBotoes('inativo');
        };
        // Apêndice
        this.showWelcomeMessage();
        container.appendChild(btnMain);
        container.appendChild(btnRequisicoesDetalhadas);
        container.appendChild(btnRelatorioEvento);
        container.appendChild(btnAnaliseDetalhada);
        container.appendChild(btnFinalizar);
        document.body.appendChild(container);
        document.body.appendChild(btnToggle);
        // Ação do botão de requisições detalhadas
        btnRequisicoesDetalhadas.onclick = () => {
            this.showHttpRequestsReport();
        };
        // Ação do botão de análise detalhada
        btnAnaliseDetalhada.onclick = () => {
            this.generateDetailedAnalysis();
        };
        // Ação do relatório por evento
        btnRelatorioEvento.onclick = () => {
            this.showEventReport(true);
        };
        // ====== Captura automática de eventos de UI para separar por evento ======
        try {
            // Evita adicionar múltiplos listeners
            const anyWindow = window;
            if (!anyWindow.__pm_auto_ui_listeners_added) {
                anyWindow.__pm_auto_ui_listeners_added = true;
                const describeTarget = (el, ev) => {
                    try {
                        const isKeyboard = typeof KeyboardEvent !== 'undefined' &&
                            ev instanceof KeyboardEvent;
                        const keyPart = isKeyboard && ev.key
                            ? `[${ev.key}]`
                            : '';
                        if (!el || typeof el !== 'object')
                            return `ui${keyPart}`;
                        const tag = (el.tagName || el.nodeName || '').toString().toLowerCase() ||
                            'element';
                        const id = el.id ? `#${el.id}` : '';
                        const rawClass = typeof el.className === 'string'
                            ? el.className
                            : el.classList &&
                                typeof el.classList === 'object' &&
                                typeof el.classList.toString === 'function'
                                ? el.classList.toString()
                                : '';
                        const filtered = rawClass
                            .split(' ')
                            .filter((c) => c && !c.startsWith('ng-') && !c.startsWith('cdk-'))
                            .slice(0, 2)
                            .join('.');
                        const cls = filtered ? `.${filtered}` : '';
                        const includeText = tag === 'button' || tag === 'a';
                        const txt = includeText && typeof el.innerText === 'string'
                            ? el.innerText.trim().slice(0, 30)
                            : '';
                        return `${tag}${id}${cls}${keyPart}${txt ? `:"${txt}"` : ''}`;
                    }
                    catch (_a) {
                        return 'ui';
                    }
                };
                let uiEventEndTimer = null;
                let uiEventEndInterval = null;
                const startUiEventContext = (label) => {
                    if (uiEventEndTimer) {
                        clearTimeout(uiEventEndTimer);
                        uiEventEndTimer = null;
                    }
                    if (uiEventEndInterval) {
                        clearInterval(uiEventEndInterval);
                        uiEventEndInterval = null;
                    }
                    this.beginEventContext(label);
                    // Se já há um contexto aberto, não cria nova janela de encerramento (evento aninhado)
                    if (this.eventContextStack.length > 1) {
                        return;
                    }
                    // Janela inteligente: espera primeiro timer e fecha após período de silêncio
                    const MAX_WINDOW_MS = 60000;
                    const GRACE_MS = 2000; // tempo para permitir timers que começam depois
                    const QUIET_MS = 600; // tempo sem atividade para considerar fluxo encerrado
                    uiEventEndInterval = setInterval(() => {
                        var _a, _b;
                        const now = Date.now();
                        const active = this.hasActiveTimers();
                        const beginAt = (_a = this.currentEventBeginAt) !== null && _a !== void 0 ? _a : now;
                        const hasStarted = this.firstTimerInEventAt !== null;
                        const graceExceeded = now - beginAt > GRACE_MS;
                        const lastActivity = (_b = this.lastTimerActivityAt) !== null && _b !== void 0 ? _b : beginAt;
                        const quietEnough = hasStarted && !active && now - lastActivity > QUIET_MS;
                        const exceededMax = now - beginAt > MAX_WINDOW_MS;
                        if (quietEnough || (!hasStarted && graceExceeded) || exceededMax) {
                            this.endEventContext();
                            clearInterval(uiEventEndInterval);
                            uiEventEndInterval = null;
                        }
                    }, 250);
                    // Fallback: fecha no máximo após MAX_WINDOW_MS
                    uiEventEndTimer = setTimeout(() => {
                        if (uiEventEndInterval) {
                            clearInterval(uiEventEndInterval);
                            uiEventEndInterval = null;
                        }
                        this.endEventContext();
                        uiEventEndTimer = null;
                    }, MAX_WINDOW_MS);
                };
                ['click', 'submit', 'change', 'keydown'].forEach((type) => {
                    document.addEventListener(type, (ev) => {
                        const target = ev.target;
                        const label = `UserInput:${type} ${describeTarget(target, ev)}`;
                        startUiEventContext(label);
                    }, true // captura cedo na fase de captura
                    );
                });
                // Contexto inicial para operações de inicialização da tela
                this.beginEventContext('AppInit');
                setTimeout(() => this.endEventContext(), 3000);
            }
        }
        catch (e) {
            // Evita quebrar caso ambiente não permita acesso ao document
            console.warn('[PerformanceMonitor] Falha ao ativar captura automática de UI:', e);
        }
    }
    showEventReport(includeHttp = true) {
        console.log('%cRELATÓRIO POR EVENTO', 'color: #bbc7c8ff; font-size: 18px; font-weight: bold; text-shadow: 1px 1px #000; background-color: #43248bff; border-radius: 5px; padding: 10px 20px;');
        if (this.performanceSummary.size === 0) {
            console.log('Nenhuma operação monitorada ainda.');
            return;
        }
        const events = new Map();
        this.performanceSummary.forEach((stats, fullName) => {
            const parts = fullName.split(' | ');
            const event = parts.length > 1 ? parts[0] : 'SemEvento';
            const opName = parts.length > 1 ? parts.slice(1).join(' | ') : fullName;
            if (!events.has(event)) {
                events.set(event, { totalTime: 0, totalExec: 0, operations: [] });
            }
            const group = events.get(event);
            group.totalTime += stats.totalTime;
            group.totalExec += stats.count;
            group.operations.push({ name: opName, stats });
        });
        let overallTime = 0;
        let overallExec = 0;
        Array.from(events.entries())
            .sort((a, b) => b[1].totalTime - a[1].totalTime)
            .forEach(([event, group]) => {
            overallTime += group.totalTime;
            overallExec += group.totalExec;
            console.log(`%cEVENTO: ${event}  |  Tempo: ${Math.round(group.totalTime)}ms  |  Execuções: ${group.totalExec}`, 'color: #fff; background: #28647b; font-size: 13px; font-weight: bold; border-radius: 4px; padding: 3px 8px;');
            const table = Object.fromEntries(group.operations
                .sort((a, b) => b.stats.totalTime - a.stats.totalTime)
                .map(({ name, stats }) => {
                const avgTime = Math.round(stats.totalTime / stats.count);
                const { emoji, status } = this.getPerformanceStatus(avgTime, stats.customThresholds, name);
                return [
                    name,
                    {
                        Execuções: stats.count,
                        'Média(ms)': `${avgTime}ms (${status}) ${emoji}`.trim(),
                        'Mínimo(ms)': stats.minTime,
                        'Máximo(ms)': stats.maxTime,
                        'Total(ms)': Math.round(stats.totalTime),
                    },
                ];
            }));
            console.table(table);
            if (includeHttp) {
                const httpTable = {};
                this.httpRequests.forEach((dados, key) => {
                    const parts = key.split(' | ');
                    const httpEvent = parts.length > 1 ? parts[0] : 'SemEvento';
                    const httpName = parts.length > 1 ? parts.slice(1).join(' | ') : key;
                    if (httpEvent === event) {
                        httpTable[httpName] = {
                            Execuções: dados.execucoes,
                            Média: `${Math.round(dados.tempoTotal / Math.max(dados.execucoes, 1))}ms`,
                            'Tempo Total': `${Math.round(dados.tempoTotal)}ms`,
                            Erros: dados.errors,
                        };
                    }
                });
                const hasHttpRows = Object.keys(httpTable).length > 0;
                if (hasHttpRows) {
                    console.log(`%cHTTP (${event})`, 'color: #fff; background: #28336b; font-size: 12px; font-weight: bold; border-radius: 4px; padding: 2px 8px;');
                    console.table(httpTable);
                }
            }
        });
        console.log(`%cTOTAL GERAL: ${Math.round(overallTime)}ms  |  Execuções: ${overallExec}`, 'color: #fff; background: #43248bff; font-size: 14px; font-weight: bold; border-radius: 4px; padding: 4px 12px; margin-top: 8px;');
    }
}
/**
 * 🏭 Factory function para criar instância do LogsPerformatico
 * @param serviceName Nome do serviço (para logs personalizados)
 * @returns Nova instância de LogsPerformatico
 */
function createPerformanceLogger(serviceName = 'Service') {
    return new LogsPerformatico(serviceName);
}

// shared/performanceLoggerGlobal.ts
const GlobalPerformanceLogger = createPerformanceLogger('GLOBAL');
// Implementação unificada
function PerformanceMonitor(options) {
    return function (target, propertyKey, descriptor) {
        // --- CASO 1: Class Decorator ---
        // Se propertyKey e descriptor forem undefined, estamos decorando uma classe
        if (!propertyKey && !descriptor) {
            const constructor = target;
            const opts = options;
            const excludeList = (typeof opts === 'object' && (opts === null || opts === void 0 ? void 0 : opts.exclude)) || [];
            const prefix = (typeof opts === 'object' && (opts === null || opts === void 0 ? void 0 : opts.prefix)) || '';
            // Itera sobre todas as propriedades do prototype da classe
            for (const prop of Object.getOwnPropertyNames(constructor.prototype)) {
                // Ignora construtor e itens excluídos
                if (prop === 'constructor' || excludeList.includes(prop)) {
                    continue;
                }
                const propDesc = Object.getOwnPropertyDescriptor(constructor.prototype, prop);
                // Verifica se é método
                if (propDesc && typeof propDesc.value === 'function') {
                    const originalMethod = propDesc.value;
                    const opName = prefix ? `${prefix}.${prop}` : prop;
                    // Substitui o método original pelo wrapper
                    propDesc.value = function (...args) {
                        const instanceLogger = this
                            .performanceLogger;
                        const logger = instanceLogger || GlobalPerformanceLogger;
                        if (!logger) {
                            return originalMethod.apply(this, args);
                        }
                        logger.startTimer(opName);
                        let result;
                        try {
                            result = originalMethod.apply(this, args);
                        }
                        catch (err) {
                            // Em caso de erro síncrono, finaliza timer e re-lança
                            logger.endTimer(opName);
                            throw err;
                        }
                        // Verifica se é Promise (assíncrono)
                        if (result && typeof result.then === 'function') {
                            return result
                                .then((res) => {
                                logger.endTimer(opName);
                                return res;
                            })
                                .catch((err) => {
                                logger.endTimer(opName);
                                throw err;
                            });
                        }
                        else {
                            // Síncrono
                            logger.endTimer(opName);
                            return result;
                        }
                    };
                    // Redefine a propriedade no prototype
                    Object.defineProperty(constructor.prototype, prop, propDesc);
                }
            }
            return; // Retorna void para Class Decorator
        }
        // --- CASO 2: Method Decorator ---
        if (!descriptor)
            return; // Segurança
        const originalMethod = descriptor.value;
        let opName;
        let customMethod;
        let customThresholds;
        let eventLabel;
        // Parsing simples para method options
        if (typeof options === 'object' &&
            options !== null &&
            !('exclude' in options) &&
            !('prefix' in options)) {
            const opts = options;
            opName = opts.name || propertyKey;
            customMethod = opts.method;
            customThresholds = {
                ok: opts.ok,
                lento: opts.lento,
                critico: opts.critico,
            };
            eventLabel = opts.event;
        }
        else if (typeof options === 'function') {
            opName = options.name || propertyKey;
            customMethod = options;
        }
        else if (typeof options === 'string') {
            opName = options;
        }
        else {
            opName = propertyKey;
        }
        descriptor.value = function (...args) {
            const instanceLogger = this
                .performanceLogger;
            const logger = instanceLogger || GlobalPerformanceLogger;
            if (logger) {
                if (eventLabel) {
                    logger.beginEventContext(eventLabel);
                }
                logger.startTimer(opName);
                let result;
                try {
                    const methodToCall = customMethod || originalMethod;
                    result = methodToCall.apply(this, args);
                }
                catch (err) {
                    logger.endTimer(opName, undefined, customThresholds);
                    if (eventLabel)
                        logger.endEventContext();
                    throw err;
                }
                if (result && typeof result.then === 'function') {
                    return result
                        .then((res) => {
                        logger.endTimer(opName, undefined, customThresholds);
                        if (eventLabel)
                            logger.endEventContext();
                        return res;
                    })
                        .catch((err) => {
                        logger.endTimer(opName, undefined, customThresholds);
                        if (eventLabel)
                            logger.endEventContext();
                        throw err;
                    });
                }
                else {
                    logger.endTimer(opName, undefined, customThresholds);
                    if (eventLabel)
                        logger.endEventContext();
                    return result;
                }
            }
            else {
                const methodToCall = customMethod || originalMethod;
                return methodToCall.apply(this, args);
            }
        };
        return descriptor;
    };
}

class PerformanceMonitorInterceptor {
    intercept(req, next) {
        var _a;
        // Ignora arquivos de assets
        if (!req.url.startsWith('./assets')) {
            // 🔹 Recupera dados da sessão
            let tela = sessionStorage.getItem('idTela');
            if (!tela) {
                tela = 'SEL041';
                sessionStorage.setItem('idTela', tela);
            }
            const cdUsuario = (_a = sessionStorage.getItem('cdUsuario')) !== null && _a !== void 0 ? _a : '';
            // 🔹 Clona a requisição adicionando headers
            const authReq = req.clone({
                headers: req.headers
                    .set('tela_id', tela)
                    .set('tela', 'SEL041')
                    .set('user', cdUsuario),
            });
            const start = performance.now();
            return next.handle(authReq).pipe(tap({
                next: (event) => {
                    var _a, _b;
                    if (event instanceof HttpResponse) {
                        const tempo = performance.now() - start;
                        // ✅ Verificação segura do body e suas propriedades
                        const operationType = ((_a = authReq.body) === null || _a === void 0 ? void 0 : _a.operationType) || 'UNKNOWN';
                        const operationName = ((_b = authReq.body) === null || _b === void 0 ? void 0 : _b.name) || '';
                        console.log(`%c🟢 [HTTP] ${operationType} ${operationType === 'SELECT_FULL' ? '' : operationName} - ${tempo.toFixed(1)}ms`, 'color: #7048cfff; font-weight: bold;');
                        GlobalPerformanceLogger.registerHttpRequest(authReq, tempo, false);
                    }
                },
                error: (error) => {
                    var _a, _b;
                    const tempo = performance.now() - start;
                    // ✅ Verificação segura do body e suas propriedades
                    const operationType = ((_a = authReq.body) === null || _a === void 0 ? void 0 : _a.operationType) || 'UNKNOWN';
                    const operationName = ((_b = authReq.body) === null || _b === void 0 ? void 0 : _b.name) || '';
                    console.groupCollapsed(`%c🔴 [HTTP] ${operationType} ${operationName} - ${tempo.toFixed(1)}ms`, 'color: #7048cfff; font-weight: bold;');
                    console.error(error);
                    console.groupEnd();
                    GlobalPerformanceLogger.registerHttpRequest(authReq, tempo, true);
                },
            }));
        }
        // Requisição de asset, passa direto
        return next.handle(req);
    }
}
PerformanceMonitorInterceptor.decorators = [
    { type: Injectable }
];

/*
 * Public API Surface of performance-monitor
 */

/**
 * Generated bundle index. Do not edit.
 */

export { GlobalPerformanceLogger, PerformanceMonitor, PerformanceMonitorComponent, PerformanceMonitorInterceptor, PerformanceMonitorModule, PerformanceMonitorService, LogsPerformatico as ɵa, createPerformanceLogger as ɵb };
//# sourceMappingURL=performance-monitor-logger.js.map