UNPKG

performance-monitor-logger

Version:

## Instalação

1,439 lines 214 kB
/**
 * 📊 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
 */
import { __awaiter } from "tslib";
export 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
 */
export function createPerformanceLogger(serviceName = 'Service') {
    return new LogsPerformatico(serviceName);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nLXBlcmZvcm1hdGljby5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3BlcmZvcm1hbmNlLW1vbml0b3ItbG9nZ2VyL3NyYy9saWIvbG9nLXBlcmZvcm1hdGljby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRzs7QUFpQ0gsTUFBTSxPQUFPLGdCQUFnQjtJQTRCM0IsWUFBWSxjQUFzQixTQUFTO1FBM0JuQyx1QkFBa0IsR0FBWSxJQUFJLENBQUMsQ0FBQywrQkFBK0I7UUFDM0UsNkJBQTZCO1FBQ3JCLHNCQUFpQixHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO1FBQzlDLHVCQUFrQixHQUFHLElBQUksR0FBRyxFQUE0QixDQUFDO1FBRXpELGlCQUFZLEdBR2hCLElBQUksR0FBRyxFQUFFLENBQUM7UUFFZCw4Q0FBOEM7UUFDdEMseUJBQW9CLEdBQWEsRUFBRSxDQUFDO1FBQ3BDLHNCQUFpQixHQUFhLEVBQUUsQ0FBQztRQUNqQyxlQUFVLEdBTVosRUFBRSxDQUFDO1FBQ1QsdUVBQXVFO1FBQy9ELHdCQUFtQixHQUFrQixJQUFJLENBQUM7UUFDMUMsd0JBQW1CLEdBQWtCLElBQUksQ0FBQztRQUMxQyx3QkFBbUIsR0FBa0IsSUFBSSxDQUFDO1FBQ2xELDJDQUEyQztRQUNuQyxvQkFBZSxHQUFHLElBQUksT0FBTyxFQUFzQixDQUFDO1FBRzFELElBQUksQ0FBQyxXQUFXLEdBQUcsV0FBVyxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxtQkFBbUIsQ0FDakIsT0FBWSxFQUNaLEtBQWEsRUFDYixRQUFpQixLQUFLOztRQUV0QixnQ0FBZ0M7UUFDaEMsSUFBSSxJQUFJLEdBQVcsZ0JBQWdCLENBQUM7UUFDcEMsSUFBSTtZQUNGLE1BQU0sVUFBVSxHQUFHLE1BQUEsT0FBTyxhQUFQLE9BQU8sdUJBQVAsT0FBTyxDQUFFLElBQUksMENBQUUsSUFBMEIsQ0FB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c291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIPCfk4ogU0lTVEVNQSBERSBMT0dTIERFIFBFUkZPUk1BTkNFXHJcbiAqXHJcbiAqIFNpc3RlbWEgY29tcGxldG8gZGUgbW9uaXRvcmFtZW50byBkZSBwZXJmb3JtYW5jZSBwYXJhIGFwbGljYcOnw7VlcyBBbmd1bGFyXHJcbiAqIGNvbSBmZWVkYmFjayB2aXN1YWwgYXRyYXbDqXMgZGUgZW1vamlzIGUgcmVsYXTDs3Jpb3MgYXV0b23DoXRpY29zLlxyXG4gKlxyXG4gKiBDYXJhY3RlcsOtc3RpY2FzOlxyXG4gKiAtIPCfk4ggTW9uaXRvcmFtZW50byBlbSBUZW1wbyBSZWFsXHJcbiAqIC0g8J+fovCfn6Hwn5S0IENsYXNzaWZpY2HDp8OjbyBWaXN1YWwgcG9yIFBlcmZvcm1hbmNlXHJcbiAqIC0g4o+wIFJlbGF0w7NyaW9zIEF1dG9tw6F0aWNvcyBhcMOzcyBJbmF0aXZpZGFkZVxyXG4gKiAtIPCfk4ogQW7DoWxpc2UgRGV0YWxoYWRhIGNvbSBSZWNvbWVuZGHDp8O1ZXNcclxuICogLSDwn5ug77iPIEFQSSBQw7pibGljYSBwYXJhIENvbnRyb2xlIE1hbnVhbFxyXG4gKlxyXG4gKiBAYXV0aG9yIFBlZHJvIEhlbnJpcXVlIGRvcyBTYW50b3MgTGV1Y2hzXHJcbiAqIEB2ZXJzaW9uIDEuMC4yM1xyXG4gKiBAc2luY2UgMjAyNS0wNy0xMVxyXG4gKi9cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgUGVyZm9ybWFuY2VTdGF0cyB7XHJcbiAgY291bnQ6IG51bWJlcjtcclxuICB0b3RhbFRpbWU6IG51bWJlcjtcclxuICBtaW5UaW1lOiBudW1iZXI7XHJcbiAgbWF4VGltZTogbnVtYmVyO1xyXG4gIGN1c3RvbVRocmVzaG9sZHM/OiB7IG9rPzogbnVtYmVyOyBsZW50bz86IG51bWJlcjsgY3JpdGljbz86IG51bWJlciB9OyAvLyDwn4aVIEFybWF6ZW5hIHRocmVzaG9sZHMgY3VzdG9taXphZG9zXHJcbn1cclxuXHJcbmV4cG9ydCBpbnRlcmZhY2UgRHluYW1pY1RocmVzaG9sZHMge1xyXG4gIG9rOiBudW1iZXI7XHJcbiAgbGVudG86IG51bWJlcjtcclxuICBjcml0aWNvOiBudW1iZXI7XHJcbn1cclxuXHJcbi8vIPCfjpvvuI8gT3DDp8O1ZXMgcGFyYSBhdXRv4oCRaW5zdHJ1bWVudGHDp8OjbyBzZW0gZGVjb3JhdG9yc1xyXG5leHBvcnQgaW50ZXJmYWNlIEluc3RydW1lbnRPcHRpb25zIHtcclxuICBpbmNsdWRlPzogQXJyYXk8c3RyaW5nIHwgUmVnRXhwPjtcclxuICBleGNsdWRlPzogQXJyYXk8c3RyaW5nIHwgUmVnRXhwPjtcclxuICBuYW1lVHJhbnNmb3JtPzogKG5hbWU6IHN0cmluZywgcGF0aD86IHN0cmluZ1tdKSA9PiBzdHJpbmc7XHJcbiAgcHJlc2VydmVUb1N0cmluZz86IGJvb2xlYW47IC8vIG1hbnTDqW0gZm4udG9TdHJpbmcoKSBvcmlnaW5hbCBwYXJhIGNvbXBhdGliaWxpZGFkZSBjb20gcmVnZXhcclxuICBtYXhEZXB0aD86IG51bWJlcjsgLy8gdXNhZG8gbm8gaW5zdHJ1bWVudE5hbWVzcGFjZVxyXG59XHJcblxyXG4vLyDwn5SnIE9ww6fDtWVzIHBhcmEgYXV0b+KAkWluc3RydW1lbnRhw6fDo28gYmFzZWFkYSBlbSBjaGFtYWRhcyBzZXRTZXJ2aWNlKC4uLilcclxuZXhwb3J0IGludGVyZmFjZSBIb29rU2V0U2VydmljZU9wdGlvbnMgZXh0ZW5kcyBJbnN0cnVtZW50T3B0aW9ucyB7XHJcbiAgaW5jbHVkZU9iamVjdHM/OiBBcnJheTxzdHJpbmcgfCBSZWdFeHA+OyAvLyBmaWx0cm8gZGUgb2JqZXRvcyAocG9yIGNhbWluaG8vbm9tZSlcclxuICBleGNsdWRlT2JqZWN0cz86IEFycmF5PHN0cmluZyB8IFJlZ0V4cD47XHJcbiAgbWF4V2Fsa0RlcHRoPzogbnVtYmVyOyAvLyBwcm9mdW5kaWRhZGUgbcOheGltYSBhbyB2YXJyZXIgbmFtZXNwYWNlc1xyXG4gIGxhYmVsRnJvbVBhdGg/OiAocGF0aDogc3RyaW5nW10pID0+IHN0cmluZzsgLy8gY29tbyBub21lYXIgc2VydmnDp29zXHJcbn1cclxuXHJcbmV4cG9ydCBjbGFzcyBMb2dzUGVyZm9ybWF0aWNvIHtcclxuICBwcml2YXRlIG1vbml0b3JhbWVudG9BdGl2bzogYm9vbGVhbiA9IHRydWU7IC8vIENvbWXDp2EgYXRpdm8gbmEgcHJpbWVpcmEgdmV6XHJcbiAgLy8g8J+TiiBQcm9wcmllZGFkZXMgZG8gU2lzdGVtYVxyXG4gIHByaXZhdGUgcGVyZm9ybWFuY2VUaW1lcnMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xyXG4gIHByaXZhdGUgcGVyZm9ybWFuY2VTdW1tYXJ5ID0gbmV3IE1hcDxzdHJpbmcsIFBlcmZvcm1hbmNlU3RhdHM+KCk7XHJcbiAgcHJpdmF0ZSBzZXJ2aWNlTmFtZTogc3RyaW5nO1xyXG4gIHByaXZhdGUgaHR0cFJlcXVlc3RzOiBNYXA8XHJcbiAgICBzdHJpbmcsXHJcbiAgICB7IHRlbXBvVG90YWw6IG51bWJlcjsgZXhlY3Vjb2VzOiBudW1iZXI7IGVycm9yczogbnVtYmVyIH1cclxuICA+ID0gbmV3IE1hcCgpO1xyXG5cclxuICAvLyDwn4aVIFN0YWNrIHBhcmEgY29udGV4dG8gZGUgZnVuw6fDo28gbW9uaXRvcmFkYVxyXG4gIHByaXZhdGUgZnVuY3Rpb25Db250ZXh0U3RhY2s6IHN0cmluZ1tdID0gW107XHJcbiAgcHJpdmF0ZSBldmVudENvbnRleHRTdGFjazogc3RyaW5nW10gPSBbXTtcclxuICBwcml2YXRlIHRpbWVyU3RhY2s6IHtcclxuICAgIG5hbWU6IHN0cmluZztcclxuICAgIHN0YXJ0OiBudW1iZXI7XHJcbiAgICBjaGlsZEVsYXBzZWQ6IG51bWJlcjtcclxuICAgIGV2ZW50Pzogc3RyaW5nO1xyXG4gICAgZnVsbE5hbWU/OiBzdHJpbmc7XHJcbiAgfVtdID0gW107XHJcbiAgLy8g8J+GlSBFc3RhZG8gZGUgYXRpdmlkYWRlIHBhcmEgZmVjaGFtZW50byBpbnRlbGlnZW50ZSBkZSBjb250ZXh0byBkZSBVSVxyXG4gIHByaXZhdGUgbGFzdFRpbWVyQWN0aXZpdHlBdDogbnVtYmVyIHwgbnVsbCA9IG51bGw7XHJcbiAgcHJpdmF0ZSBmaXJzdFRpbWVySW5FdmVudEF0OiBudW1iZXIgfCBudWxsID0gbnVsbDtcclxuICBwcml2YXRlIGN1cnJlbnRFdmVudEJlZ2luQXQ6IG51bWJlciB8IG51bGwgPSBudWxsO1xyXG4gIC8vIPCflJIgQ29udHJvbGUgZGUgZnVuw6fDtWVzIGrDoSBpbnN0cnVtZW50YWRhc1xyXG4gIHByaXZhdGUgaW5zdHJ1bWVudGVkRm5zID0gbmV3IFdlYWtNYXA8RnVuY3Rpb24sIEZ1bmN0aW9uPigpO1xyXG5cclxuICBjb25zdHJ1Y3RvcihzZXJ2aWNlTmFtZTogc3RyaW5nID0gJ1NlcnZpY2UnKSB7XHJcbiAgICB0aGlzLnNlcnZpY2VOYW1lID0gc2VydmljZU5hbWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBSZWdpc3RyYSByZXF1aXNpw6fDo28gSFRUUCBhc3NvY2lhbmRvIMOgIGZ1bsOnw6NvIG1vbml0b3JhZGEgYXRpdmEgKHNlIGhvdXZlcilcclxuICAgKiBAcGFyYW0gcGF5bG9hZCBEYWRvcyBkYSByZXF1aXNpw6fDo29cclxuICAgKi9cclxuICByZWdpc3Rlckh0dHBSZXF1ZXN0KFxyXG4gICAgYXV0aFJlcTogYW55LFxyXG4gICAgdGVtcG86IG51bWJlcixcclxuICAgIGVycm9yOiBib29sZWFuID0gZmFsc2VcclxuICApOiB2b2lkIHtcclxuICAgIC8vIE5vbWUgcm9idXN0byBkYSBvcGVyYcOnw6NvIEhUVFBcclxuICAgIGxldCBub21lOiBzdHJpbmcgPSAnKGRlc2NvbmhlY2lkbyknO1xyXG4gICAgdHJ5IHtcclxuICAgICAgY29uc3QgYnlCb2R5TmFtZSA9IGF1dGhSZXE/LmJvZHk/Lm5hbWUgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xyXG4gICAgICBjb25zdCBieVNxbCA9IHRoaXMuZXh0cmFpck5vbWVEZVNRTChcclxuICAgICAgICBhdXRoUmVxPy5ib2R5Py5zcWxJbnN0cnVjdGlvbiBhcyBzdHJpbmcgfCB1bmRlZmluZWRcclxuICAgICAgKTtcclxuICAgICAgY29uc3QgbWV0aG9kID0gKGF1dGhSZXE/Lm1ldGhvZCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpIHx8ICdIVFRQJztcclxuICAgICAgY29uc3QgdXJsOiBzdHJpbmcgfCB1bmRlZmluZWQgPSBhdXRoUmVxPy51cmwgYXMgc3RyaW5nIHwgdW5kZWZpbmVkO1xyXG5cclxuICAgICAgLy8gVXNhIG5vbWUgcGxhbmVqYWRvIChib2R5L3NxbCksIHNlbsOjbyBmYWxsYmFjayBwYXJhIG3DqXRvZG8gKyDDumx0aW1vIHNlZ21lbnRvIGRhIFVSTFxyXG4gICAgICBpZiAoYnlCb2R5TmFtZSkge1xyXG4gICAgICAgIG5vbWUgPSBieUJvZHlOYW1lO1xyXG4gICAgICB9IGVsc2UgaWYgKGJ5U3FsKSB7XHJcbiAgICAgICAgbm9tZSA9IGJ5U3FsO1xyXG4gICAgICB9IGVsc2UgaWYgKHVybCkge1xyXG4gICAgICAgIGNvbnN0IGNsZWFuVXJsID0gU3RyaW5nKHVybCkuc3BsaXQoJz8nKVswXTtcclxuICAgICAgICBjb25zdCBzZWdzID0gY2xlYW5Vcmwuc3BsaXQoJy8nKS5maWx0ZXIoQm9vbGVhbik7XHJcbiAgICAgICAgY29uc3QgdGFpbCA9IHNlZ3Nbc2Vncy5sZW5ndGggLSAxXSB8fCBjbGVhblVybDtcclxuICAgICAgICBub21lID0gYCR7bWV0aG9kfSAke3RhaWx9YDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBub21lID0gbWV0aG9kO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgY29uc3QgY3VycmVudEV2ZW50ID0gdGhpcy5ldmVudENvbnRleHRTdGFjay5sZW5ndGhcclxuICAgICAgPyB0aGlzLmV2ZW50Q29udGV4dFN0YWNrW3RoaXMuZXZlbnRDb250ZXh0U3RhY2subGVuZ3RoIC0gMV1cclxuICAgICAgOiB1bmRlZmluZWQ7XHJcbiAgICBjb25zdCBrZXkgPSBjdXJyZW50RXZlbnQgPyBgJHtjdXJyZW50RXZlbnR9IHwgJHtub21lfWAgOiBub21lO1xyXG5cclxuICAgIGNvbnN0IGF0dWFsID0gdGhpcy5odHRwUmVxdWVzdHMuZ2V0KGtleSkgfHwge1xyXG4gICAgICB0ZW1wb1RvdGFsOiAwLFxyXG4gICAgICBleGVjdWNvZXM6IDAsXHJcbiAgICAgIGVycm9yczogMCxcclxuICAgIH07XHJcblxyXG4gICAgYXR1YWwudGVtcG9Ub3RhbCArPSBOdW1iZXIuaXNGaW5pdGUodGVtcG8pID8gdGVtcG8gOiAwO1xyXG4gICAgYXR1YWwuZXhlY3Vjb2VzICs9IDE7XHJcbiAgICBpZiAoZXJyb3IpIGF0dWFsLmVycm9ycyArPSAxO1xyXG5cclxuICAgIHRoaXMuaHR0cFJlcXVlc3RzLnNldChrZXksIGF0dWFsKTtcclxuICB9XHJcblxyXG4gIC8vIEZ1bsOnw6NvIGF1eGlsaWFyIHBhcmEgZXh0cmFpciBub21lIGRhIGZ1bsOnw6NvIGRlIHVtIFNRTCwgZXg6IFBBQ0tfUEFSTUdFTi5DT05TVUxUQV9QQVJBTUVUUk8oLi4uKVxyXG4gIHByaXZhdGUgZXh0cmFpck5vbWVEZVNRTChzcWw6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xyXG4gICAgaWYgKCFzcWwpIHJldHVybiBudWxsO1xyXG4gICAgY29uc3QgbWF0Y2ggPSBzcWwubWF0Y2goLyhbQS1aMC05X10rXFwuW0EtWjAtOV9dKykvaSk7XHJcbiAgICByZXR1cm4gbWF0Y2ggPyBtYXRjaFsxXSA6IG51bGw7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn5qAIE1lbnNhZ2VtIGRlIGJvYXMtdmluZGFzIGRvIHNpc3RlbWFcclxuICAgKi9cclxuICBwdWJsaWMgc2hvd1dlbGNvbWVNZXNzYWdlKCk6IHZvaWQge1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIGAlY1NJU1RFTUEgREUgTU9OSVRPUkFNRU5UTyBERSBQRVJGT1JNQU5DRWAsXHJcbiAgICAgICdjb2xvcjogI2JiYzdjOGZmOyBmb250LXNpemU6IDE4cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyB0ZXh0LXNoYWRvdzogMXB4IDFweCAjMDAwOyBiYWNrZ3JvdW5kLWNvbG9yOiAjNDMyNDhiZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMTBweCAyMHB4OydcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDij7HvuI8gSW5pY2lhIGNyb27DtG1ldHJvIHBhcmEgdW1hIG9wZXJhw6fDo29cclxuICAgKiBAcGFyYW0gb3BlcmF0aW9uTmFtZSBOb21lIGRhIG9wZXJhw6fDo28gYSBzZXIgbW9uaXRvcmFkYVxyXG4gICAqL1xyXG4gIHB1YmxpYyBzdGFydFRpbWVyKG9wZXJhdGlvbk5hbWU6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgaWYgKCF0aGlzLm1vbml0b3JhbWVudG9BdGl2bykgcmV0dXJuO1xyXG4gICAgLy8gU2UgZm9yIGEgcHJpbWVpcmEgb3BlcmHDp8OjbywgYWJyZSBib3JkYSB2aXN1YWxcclxuICAgIGlmIChcclxuICAgICAgdGhpcy5wZXJmb3JtYW5jZVRpbWVycy5zaXplID09PSAwICYmXHJcbiAgICAgIHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LnNpemUgPT09IDBcclxuICAgICkge1xyXG4gICAgICBjb25zdCBib3JkZXIgPSAn4pWQJy5yZXBlYXQoNTYpO1xyXG5cclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgYCVjSU7DjUNJTyBETyBNT05JVE9SQU1FTlRPIERFIFBFUkZPUk1BTkNFYCxcclxuICAgICAgICAnY29sb3I6ICNiYmM3YzhmZjsgZm9udC1zaXplOiAxOHB4OyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1zaGFkb3c6IDFweCAxcHggIzAwMDsgYmFja2dyb3VuZC1jb2xvcjogIzQzMjQ4YmZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDEwcHggMjBweDsnXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgICB0aGlzLnBlcmZvcm1hbmNlVGltZXJzLnNldChvcGVyYXRpb25OYW1lLCBEYXRlLm5vdygpKTtcclxuICAgIC8vIEVtcGlsaGEgY29udGV4dG8gZGUgZnVuw6fDo28gbW9uaXRvcmFkYVxyXG4gICAgdGhpcy5mdW5jdGlvbkNvbnRleHRTdGFjay5wdXNoKG9wZXJhdGlvbk5hbWUpO1xyXG4gICAgLy8gQWRpY2lvbmEgY29udGV4dG8gYW8gc3RhY2sgcGFyYSBtZWRpciB0ZW1wbyBleGNsdXNpdm9cclxuICAgIGNvbnN0IGN1cnJlbnRFdmVudCA9IHRoaXMuZXZlbnRDb250ZXh0U3RhY2subGVuZ3RoXHJcbiAgICAgID8gdGhpcy5ldmVudENvbnRleHRTdGFja1swXSAvLyB1c2EgcsOzdHVsbyByYWl6IGRvIGZsdXhvIGRvIHVzdcOhcmlvXHJcbiAgICAgIDogdW5kZWZpbmVkO1xyXG4gICAgLy8gU2UgYSBvcGVyYcOnw6NvIHZpZXIgY29tbyBwYWNrLm1ldG9kbywgdXNhbW9zIHBhY2sgY29tbyBldmVudG8gZSBtZXRvZG8gY29tbyBub21lXHJcbiAgICBjb25zdCBwYXJ0cyA9IFN0cmluZyhvcGVyYXRpb25OYW1lKS5zcGxpdCgnLicpO1xyXG4gICAgbGV0IGRpc3BsYXlFdmVudDogc3RyaW5nIHwgdW5kZWZpbmVkO1xyXG4gICAgbGV0IGRpc3BsYXlOYW1lOiBzdHJpbmc7XHJcbiAgICBpZiAocGFydHMubGVuZ3RoID49IDIpIHtcclxuICAgICAgZGlzcGxheUV2ZW50ID0gcGFydHNbMF07XHJcbiAgICAgIGRpc3BsYXlOYW1lID0gcGFydHNbcGFydHMubGVuZ3RoIC0gMV07XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAvLyBTZW0gcHJlZml4byAocGFjaykgbm8gbm9tZSDigJQgZXhpYmUgYXBlbmFzIG8gbcOpdG9kb1xyXG4gICAgICBkaXNwbGF5RXZlbnQgPSB1bmRlZmluZWQ7XHJcbiAgICAgIGRpc3BsYXlOYW1lID0gb3BlcmF0aW9uTmFtZTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBmdWxsTmFtZSA9IGRpc3BsYXlFdmVudFxyXG4gICAgICA/IGAke2Rpc3BsYXlFdmVudH0gfCAke2Rpc3BsYXlOYW1lfWBcclxuICAgICAgOiBkaXNwbGF5TmFtZTtcclxuXHJcbiAgICAvLyBSZWdpc3RyYSBhdGl2aWRhZGUgZGUgdGltZXIgcGFyYSBjb250cm9sZSBkZSBqYW5lbGEgZG8gZXZlbnRvXHJcbiAgICBjb25zdCBub3dTdGFydCA9IERhdGUubm93KCk7XHJcbiAgICBpZiAoY3VycmVudEV2ZW50ICYmIHRoaXMuZmlyc3RUaW1lckluRXZlbnRBdCA9PT0gbnVsbCkge1xyXG4gICAgICB0aGlzLmZpcnN0VGltZXJJbkV2ZW50QXQgPSBub3dTdGFydDtcclxuICAgIH1cclxuICAgIHRoaXMubGFzdFRpbWVyQWN0aXZpdHlBdCA9IG5vd1N0YXJ0O1xyXG5cclxuICAgIHRoaXMudGltZXJTdGFjay5wdXNoKHtcclxuICAgICAgbmFtZTogb3BlcmF0aW9uTmFtZSxcclxuICAgICAgc3RhcnQ6IG5vd1N0YXJ0LFxyXG4gICAgICBjaGlsZEVsYXBzZWQ6IDAsXHJcbiAgICAgIGV2ZW50OiBjdXJyZW50RXZlbnQsXHJcbiAgICAgIGZ1bGxOYW1lLFxyXG4gICAgfSk7XHJcbiAgICBjb25zb2xlLmxvZyhg4pWRIFtJTsONQ0lPXSAke2Z1bGxOYW1lfSAtICR7bmV3IERhdGUoKS50b0xvY2FsZVRpbWVTdHJpbmcoKX1gKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfj4EgRmluYWxpemEgY3JvbsO0bWV0cm8gZSBleGliZSByZXN1bHRhZG8gY29tIGVtb2ppXHJcbiAgICogQHBhcmFtIG9wZXJhdGlvbk5hbWUgTm9tZSBkYSBvcGVyYcOnw6NvXHJcbiAgICogQHBhcmFtIGN1c3RvbU1lc3NhZ2UgTWVuc2FnZW0gY3VzdG9taXphZGEgb3BjaW9uYWwgKGV4OiBwYXJhIGVycm9zKVxyXG4gICAqIEBwYXJhbSBjdXN0b21UaHJlc2hvbGRzIFRocmVzaG9sZHMgY3VzdG9taXphZG9zIHBhcmEgZXN0YSBvcGVyYcOnw6NvIGVzcGVjw61maWNhXHJcbiAgICovXHJcbiAgcHVibGljIGVuZFRpbWVyKFxyXG4gICAgb3BlcmF0aW9uTmFtZTogc3RyaW5nLFxyXG4gICAgY3VzdG9tTWVzc2FnZT86IHN0cmluZyxcclxuICAgIGN1c3RvbVRocmVzaG9sZHM/OiB7IG9rPzogbnVtYmVyOyBsZW50bz86IG51bWJlcjsgY3JpdGljbz86IG51bWJlciB9XHJcbiAgKTogdm9pZCB7XHJcbiAgICBpZiAoIXRoaXMubW9uaXRvcmFtZW50b0F0aXZvKSByZXR1cm47XHJcblxyXG4gICAgLy8gVXNhIHBpbGhhIGRlIGNvbnRleHRvIHBhcmEgY2FsY3VsYXIgdGVtcG8gZXhjbHVzaXZvIChleGNsdWkgZmlsaG9zKVxyXG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcclxuICAgIGxldCBjdHggPSB0aGlzLnRpbWVyU3RhY2subGVuZ3RoXHJcbiAgICAgID8gdGhpcy50aW1lclN0YWNrW3RoaXMudGltZXJTdGFjay5sZW5ndGggLSAxXVxyXG4gICAgICA6IHVuZGVmaW5lZDtcclxuXHJcbiAgICBpZiAoIWN0eCB8fCBjdHgubmFtZSAhPT0gb3BlcmF0aW9uTmFtZSkge1xyXG4gICAgICAvLyBCdXNjYSByb2J1c3RhIGNhc28gZW5jZXJyYW1lbnRvIGZvcmEgZGUgb3JkZW1cclxuICAgICAgbGV0IGZvdW5kSW5kZXggPSAtMTtcclxuICAgICAgZm9yIChsZXQgaSA9IHRoaXMudGltZXJTdGFjay5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xyXG4gICAgICAgIGlmICh0aGlzLnRpbWVyU3RhY2tbaV0ubmFtZSA9PT0gb3BlcmF0aW9uTmFtZSkge1xyXG4gICAgICAgICAgZm91bmRJbmRleCA9IGk7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgaWYgKGZvdW5kSW5kZXggPT09IC0xKSB7XHJcbiAgICAgICAgY29uc29sZS53YXJuKGDimqDvuI8gVGltZXIgbsOjbyBlbmNvbnRyYWRvIHBhcmE6ICR7b3BlcmF0aW9uTmFtZX1gKTtcclxuICAgICAgICByZXR1cm47XHJcbiAgICAgIH1cclxuICAgICAgY3R4ID0gdGhpcy50aW1lclN0YWNrW2ZvdW5kSW5kZXhdO1xyXG4gICAgICB0aGlzLnRpbWVyU3RhY2suc3BsaWNlKGZvdW5kSW5kZXgsIDEpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgdGhpcy50aW1lclN0YWNrLnBvcCgpO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEF0dWFsaXphIGF0aXZpZGFkZSBkZSB0aW1lcnMgKHBhcmEgbMOzZ2ljYSBkZSBmZWNoYW1lbnRvIHBvciBzaWzDqm5jaW8pXHJcbiAgICB0aGlzLmxhc3RUaW1lckFjdGl2aXR5QXQgPSBub3c7XHJcblxyXG4gICAgY29uc3QgaW5jbHVzaXZlRWxhcHNlZCA9IG5vdyAtIGN0eC5zdGFydDtcclxuICAgIGNvbnN0IGV4Y2x1c2l2ZUVsYXBzZWQgPSBNYXRoLm1heCgwLCBpbmNsdXNpdmVFbGFwc2VkIC0gY3R4LmNoaWxkRWxhcHNlZCk7XHJcblxyXG4gICAgLy8gUHJvcGFnYSB0ZW1wbyBkbyBmaWxobyBwYXJhIG8gcGFpIChwYXJhIGV4Y2x1aXIgbmEgY29udGFnZW0gZG8gcGFpKVxyXG4gICAgY29uc3QgcGFyZW50ID0gdGhpcy50aW1lclN0YWNrW3RoaXMudGltZXJTdGFjay5sZW5ndGggLSAxXTtcclxuICAgIGlmIChwYXJlbnQpIHtcclxuICAgICAgcGFyZW50LmNoaWxkRWxhcHNlZCArPSBpbmNsdXNpdmVFbGFwc2VkO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIFJlbW92ZSByZWZlcsOqbmNpYSBhbnRpZ2EgYmFzZWFkYSBlbSBNYXAgKG1hbnRpZGEgcGFyYSBjb21wYXRpYmlsaWRhZGUpXHJcbiAgICB0aGlzLnBlcmZvcm1hbmNlVGltZXJzLmRlbGV0ZShvcGVyYXRpb25OYW1lKTtcclxuXHJcbiAgICAvLyBEZXNlbXBpbGhhIGNvbnRleHRvIGRlIGZ1bsOnw6NvIG1vbml0b3JhZGEgKHNvbWVudGUgc2UgdG9wbylcclxuICAgIGlmIChcclxuICAgICAgdGhpcy5mdW5jdGlvbkNvbnRleHRTdGFja1t0aGlzLmZ1bmN0aW9uQ29udGV4dFN0YWNrLmxlbmd0aCAtIDFdID09PVxyXG4gICAgICBvcGVyYXRpb25OYW1lXHJcbiAgICApIHtcclxuICAgICAgdGhpcy5mdW5jdGlvbkNvbnRleHRTdGFjay5wb3AoKTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBEZXRlcm1pbmEgc3RhdHVzIGJhc2VhZG8gbm8gdGVtcG8gZXhjbHVzaXZvXHJcbiAgICBjb25zdCB7IGVtb2ppLCBzdGF0dXMsIGNvbG9yIH0gPSB0aGlzLmdldFBlcmZvcm1hbmNlU3RhdHVzKFxyXG4gICAgICBleGNsdXNpdmVFbGFwc2VkLFxyXG4gICAgICBjdXN0b21UaHJlc2hvbGRzLFxyXG4gICAgICBjdHguZnVsbE5hbWUgPz9cclxuICAgICAgICAoY3R4LmV2ZW50ID8gYCR7Y3R4LmV2ZW50fSB8ICR7b3BlcmF0aW9uTmFtZX1gIDogb3BlcmF0aW9uTmFtZSlcclxuICAgICk7XHJcblxyXG4gICAgLy8gTG9nIGNvbSBlbW9qaSBlIGNvciAoY29tIG1lbnNhZ2VtIGN1c3RvbWl6YWRhIHNlIGZvcm5lY2lkYSlcclxuICAgIGNvbnN0IG1lc3NhZ2UgPSBjdXN0b21NZXNzYWdlXHJcbiAgICAgID8gYCR7XHJcbiAgICAgICAgICBjdHguZnVsbE5hbWUgPz9cclxuICAgICAgICAgIChjdHguZXZlbnQgPyBgJHtjdHguZXZlbnR9IHwgJHtvcGVyYXRpb25OYW1lfWAgOiBvcGVyYXRpb25OYW1lKVxyXG4gICAgICAgIH0gLSAke2N1c3RvbU1lc3NhZ2V9YFxyXG4gICAgICA6IGAke1xyXG4gICAgICAgICAgY3R4LmZ1bGxOYW1lID8/XHJcbiAgICAgICAgICAoY3R4LmV2ZW50ID8gYCR7Y3R4LmV2ZW50fSB8ICR7b3BlcmF0aW9uTmFtZX1gIDogb3BlcmF0aW9uTmFtZSlcclxuICAgICAgICB9YDtcclxuICAgIGNvbnN0IGZpeGVkU3RhdHVzID0gYFske3N0YXR1c31dYC5wYWRFbmQoNSk7XHJcbiAgICBjb25zdCBmaXhlZE1lc3NhZ2UgPSBtZXNzYWdlLnBhZEVuZCgxMCk7XHJcbiAgICBjb25zdCBmaXhlZFRpbWUgPSBleGNsdXNpdmVFbGFwc2VkLnRvU3RyaW5nKCkucGFkU3RhcnQoNSk7XHJcblxyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIGDilZEgJWMke2Vtb2ppfSAke2ZpeGVkU3RhdHVzfSAke2ZpeGVkTWVzc2FnZX0gLSAke2ZpeGVkVGltZX1tcyBgLFxyXG4gICAgICBjb2xvclxyXG4gICAgKTtcclxuXHJcbiAgICAvLyBNb3N0cmEgdGhyZXNob2xkcyBjdXN0b21pemFkb3Mgc2UgZXN0aXZlcmVtIHNlbmRvIHVzYWRvc1xyXG4gICAgaWYgKFxyXG4gICAgICBjdXN0b21UaHJlc2hvbGRzICYmXHJcbiAgICAgIChjdXN0b21UaHJlc2hvbGRzLm9rIHx8XHJcbiAgICAgICAgY3VzdG9tVGhyZXNob2xkcy5sZW50byB8fFxyXG4gICAgICAgIGN1c3RvbVRocmVzaG9sZHMuY3JpdGljbylcclxuICAgICkge1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICBg4pqZ77iPIFRocmVzaG9sZHMgY3VzdG9taXphZG9zOiBPSzwke1xyXG4gICAgICAgICAgY3VzdG9tVGhyZXNob2xkcy5vayA/PyA1MDBcclxuICAgICAgICB9bXMsIExFTlRPPCR7Y3VzdG9tVGhyZXNob2xkcy5sZW50byA/PyAyMDAwfW1zYFxyXG4gICAgICApO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIEF0dWFsaXphIHN1bcOhcmlvIGNvbSB0ZW1wbyBleGNsdXNpdm9cclxuICAgIHRoaXMudXBkYXRlUGVyZm9ybWFuY2VTdW1tYXJ5KFxyXG4gICAgICBjdHguZnVsbE5hbWUgPz9cclxuICAgICAgICAoY3R4LmV2ZW50ID8gYCR7Y3R4LmV2ZW50fSB8ICR7b3BlcmF0aW9uTmFtZX1gIDogb3BlcmF0aW9uTmFtZSksXHJcbiAgICAgIGV4Y2x1c2l2ZUVsYXBzZWQsXHJcbiAgICAgIGN1c3RvbVRocmVzaG9sZHNcclxuICAgICk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn46oIERldGVybWluYSBzdGF0dXMgZGUgcGVyZm9ybWFuY2UgYmFzZWFkbyBubyB0ZW1wb1xyXG4gICAqIEBwYXJhbSBlbGFwc2VkVGltZSBUZW1wbyBkZWNvcnJpZG8gZW0gbWlsaXNzZWd1bmRvc1xyXG4gICAqIEBwYXJhbSBjdXN0b21UaHJlc2hvbGRzIFRocmVzaG9sZHMgY3VzdG9taXphZG9zIHBhcmEgZXN0YSBvcGVyYcOnw6NvIGVzcGVjw61maWNhXHJcbiAgICogQHBhcmFtIG9wZXJhdGlvbk5hbWUgTm9tZSBkYSBvcGVyYcOnw6NvIChwYXJhIGNhbGN1bGFyIHRocmVzaG9sZHMgZGluw6JtaWNvcylcclxuICAgKiBAcmV0dXJucyBPYmpldG8gY29tIGVtb2ppLCBzdGF0dXMgZSBjb3JcclxuICAgKi9cclxuICBwcml2YXRlIGdldFBlcmZvcm1hbmNlU3RhdHVzKFxyXG4gICAgZWxhcHNlZFRpbWU6IG51bWJlcixcclxuICAgIGN1c3RvbVRocmVzaG9sZHM/OiB7IG9rPzogbnVtYmVyOyBsZW50bz86IG51bWJlcjsgY3JpdGljbz86IG51bWJlciB9LFxyXG4gICAgb3BlcmF0aW9uTmFtZT86IHN0cmluZ1xyXG4gICk6IHsgZW1vamk6IHN0cmluZzsgc3RhdHVzOiBzdHJpbmc7IGNvbG9yOiBzdHJpbmcgfSB7XHJcbiAgICBsZXQgb2tUaHJlc2hvbGQ6IG51bWJlcjtcclxuICAgIGxldCBsZW50b1RocmVzaG9sZDogbnVtYmVyO1xyXG4gICAgbGV0IGNyaXRpY29UaHJlc2hvbGQ6IG51bWJlcjtcclxuXHJcbiAgICBpZiAoXHJcbiAgICAgIGN1c3RvbVRocmVzaG9sZHMgJiZcclxuICAgICAgKGN1c3RvbVRocmVzaG9sZHMub2sgfHxcclxuICAgICAgICBjdXN0b21UaHJlc2hvbGRzLmxlbnRvIHx8XHJcbiAgICAgICAgY3VzdG9tVGhyZXNob2xkcy5jcml0aWNvKVxyXG4gICAgKSB7XHJcbiAgICAgIC8vIFVzYSB0aHJlc2hvbGRzIGN1c3RvbWl6YWRvcyBzZSBmb3JuZWNpZG9zXHJcbiAgICAgIG9rVGhyZXNob2xkID0gY3VzdG9tVGhyZXNob2xkcy5vayA/PyA1MDA7XHJcbiAgICAgIGxlbnRvVGhyZXNob2xkID0gY3VzdG9tVGhyZXNob2xkcy5sZW50byA/PyAyMDAwO1xyXG4gICAgICBjcml0aWNvVGhyZXNob2xkID0gY3VzdG9tVGhyZXNob2xkcy5jcml0aWNvID8/IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xyXG4gICAgfSBlbHNlIGlmIChvcGVyYXRpb25OYW1lKSB7XHJcbiAgICAgIC8vIENhbGN1bGEgdGhyZXNob2xkcyBkaW7Dom1pY29zIGJhc2VhZG9zIG5hIHF1YW50aWRhZGUgZGUgZXhlY3XDp8O1ZXNcclxuICAgICAgY29uc3Qgc3RhdHMgPSB0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5nZXQob3BlcmF0aW9uTmFtZSk7XHJcbiAgICAgIC8vIENvcnJpZ2U6IHNvbWEgKzEgcGFyYSBjb25zaWRlcmFyIGEgZXhlY3XDp8OjbyBhdHVhbFxyXG4gICAgICBjb25zdCBleGVjdXRpb25Db3VudCA9IHN0YXRzID8gc3RhdHMuY291bnQgKyAxIDogMTtcclxuICAgICAgY29uc3QgZHluYW1pY1RocmVzaG9sZHMgPSB0aGlzLmNhbGN1bGF0ZUR5bmFtaWNUaHJlc2hvbGRzKGV4ZWN1dGlvbkNvdW50KTtcclxuXHJcbiAgICAgIG9rVGhyZXNob2xkID0gZHluYW1pY1RocmVzaG9sZHMub2s7XHJcbiAgICAgIGxlbnRvVGhyZXNob2xkID0gZHluYW1pY1RocmVzaG9sZHMubGVudG87XHJcbiAgICAgIGNyaXRpY29UaHJlc2hvbGQgPSBkeW5hbWljVGhyZXNob2xkcy5jcml0aWNvO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gVXNhIHRocmVzaG9sZHMgcGFkcsO1ZXNcclxuICAgICAgb2tUaHJlc2hvbGQgPSA1MDA7XHJcbiAgICAgIGxlbnRvVGhyZXNob2xkID0gMjAwMDtcclxuICAgICAgY3JpdGljb1RocmVzaG9sZCA9IE51bWJlci5NQVhfU0FGRV9JTlRFR0VSO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChlbGFwc2VkVGltZSA8IG9rVGhyZXNob2xkKSB7XHJcbiAgICAgIHJldHVybiB7XHJcbiAgICAgICAgZW1vamk6ICfwn5+iJyxcclxuICAgICAgICBzdGF0dXM6ICdPSycsXHJcbiAgICAgICAgY29sb3I6ICdjb2xvcjogZ3JlZW47IGZvbnQtd2VpZ2h0OiBib2xkOycsXHJcbiAgICAgIH07XHJcbiAgICB9IGVsc2UgaWYgKGVsYXBzZWRUaW1lIDwgbGVudG9UaHJlc2hvbGQpIHtcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBlbW9qaTogJ/Cfn6EnLFxyXG4gICAgICAgIHN0YXR1czogJ0xFTlRPJyxcclxuICAgICAgICBjb2xvcjogJ2NvbG9yOiBvcmFuZ2U7IGZvbnQtd2VpZ2h0OiBib2xkOycsXHJcbiAgICAgIH07XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIGVtb2ppOiAn8J+UtCcsXHJcbiAgICAgICAgc3RhdHVzOiAnQ1LDjVRJQ08nLFxyXG4gICAgICAgIGNvbG9yOiAnY29sb3I6IHJlZDsgZm9udC13ZWlnaHQ6IGJvbGQ7JyxcclxuICAgICAgfTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfk4ggQXR1YWxpemEgZXN0YXTDrXN0aWNhcyBkZSBwZXJmb3JtYW5jZSBwYXJhIG8gc3Vtw6FyaW9cclxuICAgKiBAcGFyYW0gb3BlcmF0aW9uTmFtZSBOb21lIGRhIG9wZXJhw6fDo29cclxuICAgKiBAcGFyYW0gZWxhcHNlZFRpbWUgVGVtcG8gZGVjb3JyaWRvIGVtIG1pbGlzc2VndW5kb3NcclxuICAgKiBAcGFyYW0gY3VzdG9tVGhyZXNob2xkcyBUaHJlc2hvbGRzIGN1c3RvbWl6YWRvcyBwYXJhIGFybWF6ZW5hciAob3BjaW9uYWwpXHJcbiAgICovXHJcbiAgcHJpdmF0ZSB1cGRhdGVQZXJmb3JtYW5jZVN1bW1hcnkoXHJcbiAgICBvcGVyYXRpb25OYW1lOiBzdHJpbmcsXHJcbiAgICBlbGFwc2VkVGltZTogbnVtYmVyLFxyXG4gICAgY3VzdG9tVGhyZXNob2xkcz86IHsgb2s/OiBudW1iZXI7IGxlbnRvPzogbnVtYmVyOyBjcml0aWNvPzogbnVtYmVyIH1cclxuICApOiB2b2lkIHtcclxuICAgIGNvbnN0IGV4aXN0aW5nID0gdGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuZ2V0KG9wZXJhdGlvbk5hbWUpO1xyXG5cclxuICAgIGlmIChleGlzdGluZykge1xyXG4gICAgICBleGlzdGluZy5jb3VudCsrO1xyXG4gICAgICBleGlzdGluZy50b3RhbFRpbWUgKz0gZWxhcHNlZFRpbWU7XHJcbiAgICAgIGV4aXN0aW5nLm1pblRpbWUgPSBNYXRoLm1pbihleGlzdGluZy5taW5UaW1lLCBlbGFwc2VkVGltZSk7XHJcbiAgICAgIGV4aXN0aW5nLm1heFRpbWUgPSBNYXRoLm1heChleGlzdGluZy5tYXhUaW1lLCBlbGFwc2VkVGltZSk7XHJcblxyXG4gICAgICAvLyBBdHVhbGl6YSB0aHJlc2hvbGRzIGN1c3RvbWl6YWRvcyBzZSBmb3JuZWNpZG9zXHJcbiAgICAgIGlmIChcclxuICAgICAgICBjdXN0b21UaHJlc2hvbGRzICYmXHJcbiAgICAgICAgKGN1c3RvbVRocmVzaG9sZHMub2sgfHxcclxuICAgICAgICAgIGN1c3RvbVRocmVzaG9sZHMubGVudG8gfHxcclxuICAgICAgICAgIGN1c3RvbVRocmVzaG9sZHMuY3JpdGljbylcclxuICAgICAgKSB7XHJcbiAgICAgICAgZXhpc3RpbmcuY3VzdG9tVGhyZXNob2xkcyA9IGN1c3RvbVRocmVzaG9sZHM7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LnNldChvcGVyYXRpb25OYW1lLCB7XHJcbiAgICAgICAgY291bnQ6IDEsXHJcbiAgICAgICAgdG90YWxUaW1lOiBlbGFwc2VkVGltZSxcclxuICAgICAgICBtaW5UaW1lOiBlbGFwc2VkVGltZSxcclxuICAgICAgICBtYXhUaW1lOiBlbGFwc2VkVGltZSxcclxuICAgICAgICBjdXN0b21UaHJlc2hvbGRzOiBjdXN0b21UaHJlc2hvbGRzLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBOw6NvIGZheiBtYWlzIG5hZGEgYXF1aSwgcmVsYXTDs3JpbyBzw7MgbWFudWFsXHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn5OKIEV4aWJlIHJlbGF0w7NyaW8gcmVzdW1pZG8gZGUgdG9kYXMgYXMgb3BlcmHDp8O1ZXMgKGNvbXBhY3RhZG8pXHJcbiAgICovXHJcbiAgcHVibGljIHNob3dQZXJmb3JtYW5jZVN1bW1hcnkoc2hvd0Z1bGxEZXRhaWxzID0gZmFsc2UpOiB2b2lkIHtcclxuICAgIC8vIEZlY2hhIGJvcmRhIGRvIG1vbml0b3JhbWVudG8gYW8gZ2VyYXIgcmVsYXTDs3Jpb1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgICclYyBSRUxBVMOTUklPIERFIFBFUkZPUk1BTkNFJyxcclxuICAgICAgJ2NvbG9yOiAjYmJjN2M4ZmY7IGZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IHRleHQtc2hhZG93OiAxcHggMXB4ICMwMDA7IGJhY2tncm91bmQtY29sb3I6ICM0MzI0OGJmZjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAxMHB4IDIwcHg7J1xyXG4gICAgKTtcclxuXHJcbiAgICBpZiAodGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuc2l6ZSA9PT0gMCkge1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAnJWMgTmVuaHVtYSBvcGVyYcOnw6NvIG1vbml0b3JhZGEgYWluZGEuJyxcclxuICAgICAgICAnY29sb3I6IGdyYXk7IGZvbnQtc3R5bGU6IGl0YWxpYzsnXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBzb3J0ZWRFbnRyaWVzID0gQXJyYXkuZnJvbSh0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5lbnRyaWVzKCkpLnNvcnQoXHJcbiAgICAgIChhLCBiKSA9PiBiWzFdLm1heFRpbWUgLSBhWzFdLm1heFRpbWVcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgdGFibGUgPSBPYmplY3QuZnJvbUVudHJpZXMoXHJcbiAgICAgIHNvcnRlZEVudHJpZXMubWFwKChbb3BlcmF0aW9uLCBzdGF0c10pID0+IHtcclxuICAgICAgICBjb25zdCBhdmdUaW1lID0gTWF0aC5yb3VuZChzdGF0cy50b3RhbFRpbWUgLyBzdGF0cy5jb3VudCk7XHJcbiAgICAgICAgY29uc3QgeyBlbW9qaSwgc3RhdHVzIH0gPSB0aGlzLmdldFBlcmZvcm1hbmNlU3RhdHVzKFxyXG4gICAgICAgICAgYXZnVGltZSxcclxuICAgICAgICAgIHN0YXRzLmN1c3RvbVRocmVzaG9sZHMsXHJcbiAgICAgICAgICBvcGVyYXRpb25cclxuICAgICAgICApO1xyXG5cclxuICAgICAgICByZXR1cm4gW1xyXG4gICAgICAgICAgb3BlcmF0aW9uLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICBFeGVjdcOnw7Vlczogc3RhdHMuY291bnQsXHJcbiAgICAgICAgICAgICdNw6lkaWEobXMpJzogYCR7YXZnVGltZX1tcyAoJHtzdGF0dXN9KSAke2Vtb2ppfWAudHJpbSgpLFxyXG4gICAgICAgICAgICAnTcOtbmltbyhtcyknOiBzdGF0cy5taW5UaW1lLFxyXG4gICAgICAgICAgICAnTcOheGltbyhtcyknOiBzdGF0cy5tYXhUaW1lLFxyXG4gICAgICAgICAgICAnVG90YWwobXMpJzogc3RhdHMudG90YWxUaW1lLFxyXG4gICAgICAgICAgfSxcclxuICAgICAgICBdO1xyXG4gICAgICB9KVxyXG4gICAgKTtcclxuXHJcbiAgICBjb25zb2xlLnRhYmxlKHRhYmxlKTtcclxuXHJcbiAgICAvLyBSZW1vdmlkbzogYWdvcmEgYSBhbsOhbGlzZSBkZXRhbGhhZGEgw6kgY2hhbWFkYSBhcGVuYXMgcGVsbyBib3TDo29cclxuICB9XHJcbiAgLyoqXHJcbiAgICog8J+TkSBFeGliZSByZWxhdMOzcmlvIGRldGFsaGFkbyBkZSByZXF1aXNpw6fDtWVzIEhUVFAgYWdydXBhZGFzIHBvciBmdW7Dp8Ojb1xyXG4gICAqL1xyXG4gIHB1YmxpYyBzaG93SHR0cFJlcXVlc3RzUmVwb3J0KCk6IHZvaWQge1xyXG4gICAgY29uc29sZS5sb2coXHJcbiAgICAgIGAlY1JFTEFUw5NSSU8gREVUQUxIQURPIERFIFJFUVVJU0nDh8OVRVMgSFRUUGAsXHJcbiAgICAgICdjb2xvcjogI2JiYzdjOGZmOyBmb250LXNpemU6IDE4cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyB0ZXh0LXNoYWRvdzogMXB4IDFweCAjMDAwOyBiYWNrZ3JvdW5kLWNvbG9yOiAjNDMyNDhiZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMTBweCAyMHB4OydcclxuICAgICk7XHJcblxyXG4gICAgaWYgKHRoaXMuaHR0cFJlcXVlc3RzLnNpemUgPT09IDApIHtcclxuICAgICAgY29uc29sZS5sb2coJ05lbmh1bWEgcmVxdWlzacOnw6NvIEhUVFAgcmVnaXN0cmFkYS4nKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IHRhYmVsYSA9IE9iamVjdC5mcm9tRW50cmllcyhcclxuICAgICAgQXJyYXkuZnJvbSh0aGlzLmh0dHBSZXF1ZXN0cy5lbnRyaWVzKCkpXHJcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGJbMV0udGVtcG9Ub3RhbCAtIGFbMV0udGVtcG9Ub3RhbClcclxuICAgICAgICAubWFwKChbbm9tZSwgZGFkb3NdKSA9PiBbXHJcbiAgICAgICAgICBub21lLFxyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICBFeGVjdcOnw7VlczogZGFkb3MuZXhlY3Vjb2VzLFxyXG4gICAgICAgICAgICBNw6lkaWE6IGAke01hdGgucm91bmQoZGFkb3MudGVtcG9Ub3RhbCAvIGRhZG9zLmV4ZWN1Y29lcyl9bXNgLFxyXG4gICAgICAgICAgICAnVGVtcG8gVG90YWwnOiBgJHtNYXRoLnJvdW5kKGRhZG9zLnRlbXBvVG90YWwpfW1zYCxcclxuICAgICAgICAgICAgRXJyb3M6IGRhZG9zLmVycm9ycyxcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgXSlcclxuICAgICk7XHJcblxyXG4gICAgY29uc29sZS50YWJsZSh0YWJlbGEpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+UjSBHZXJhIGFuw6FsaXNlIGRldGFsaGFkYSBkZSBwZXJmb3JtYW5jZVxyXG4gICAqL1xyXG4gIHB1YmxpYyBnZW5lcmF0ZURldGFpbGVkQW5hbHlzaXMoKTogdm9pZCB7XHJcbiAgICAvLyBUw610dWxvIGRpc2NyZXRvXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgJyVjQU7DgUxJU0UgREVUQUxIQURBIERFIFBFUkZPUk1BTkNFJyxcclxuICAgICAgJ2NvbG9yOiAjYmJjN2M4ZmY7IGZvbnQtc2l6ZTogMThweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IHRleHQtc2hhZG93OiAxcHggMXB4ICMwMDA7IGJhY2tncm91bmQtY29sb3I6ICM0MzI0OGJmZjsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAxMHB4IDIwcHg7J1xyXG4gICAgKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuXHJcbiAgICBpZiAodGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuc2l6ZSA9PT0gMCkge1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAnJWNOZW5odW1hIG9wZXJhw6fDo28gcmVnaXN0cmFkYSBwYXJhIGFuw6FsaXNlLicsXHJcbiAgICAgICAgJ2NvbG9yOiAjYmJiOyBmb250LXN0eWxlOiBpdGFsaWM7IGZvbnQtc2l6ZTogMTRweDsgcGFkZGluZzogNHB4IDAgNHB4IDEwcHg7J1xyXG4gICAgICApO1xyXG4gICAgICByZXR1cm47XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3QgeyBjcml0aWNhbE9wZXJhdGlvbnMsIHNsb3dPcGVyYXRpb25zLCBmYXN0T3BlcmF0aW9ucyB9ID1cclxuICAgICAgdGhpcy5jYXRlZ29yaXplT3BlcmF0aW9ucygpO1xyXG5cclxuICAgIC8vIEhlbHBlciBwYXJhIGJsb2NvIGRpc2NyZXRvIGNvbSB0ZXh0byBjbGFyb1xyXG4gICAgY29uc3QgcHJpbnRCbG9jayA9IChcclxuICAgICAgY29sb3I6IHN0cmluZyxcclxuICAgICAgZW1vamk6IHN0cmluZyxcclxuICAgICAgb3BlcmF0aW9uOiBzdHJpbmcsXHJcbiAgICAgIGF2ZzogbnVtYmVyLFxyXG4gICAgICBleHRyYTogc3RyaW5nXHJcbiAgICApID0+IHtcclxuICAgICAgY29uc3Qgc3R5bGUgPSBgYm9yZGVyLWxlZnQ6IDRweCBzb2xpZCAke2NvbG9yfTsgcGFkZGluZzogMnB4IDAgMnB4IDEwcHg7IG1hcmdpbjogMnB4IDAgMnB4IDA7IGZvbnQtc2l6ZTogMTRweDsgY29sb3I6ICNmZmY7IGJhY2tncm91bmQ6IG5vbmU7IGZvbnQtd2VpZ2h0OiBub3JtYWw7YDtcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgYCVjJHtlbW9qaX0gJHtvcGVyYXRpb259ICAtICAke2F2Z31tcyAobcOpZGlhKSAgJHtleHRyYX1gLnRyaW0oKSxcclxuICAgICAgICBzdHlsZVxyXG4gICAgICApO1xyXG4gICAgfTtcclxuXHJcbiAgICBpZiAoY3JpdGljYWxPcGVyYXRpb25zLmxlbmd0aCA+IDApIHtcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgJyVj8J+UtCBDUsONVElDQVMgKCA+IDIwMDBtcyknLFxyXG4gICAgICAgICdjb2xvcjogI2ZmZjsgZm9udC1zaXplOiAxNHB4OyBmb250LXdlaWdodDogYm9sZDsgbWFyZ2luLXRvcDogMTBweDsnXHJcbiAgICAgICk7XHJcbiAgICAgIGNyaXRpY2FsT3BlcmF0aW9ucy5mb3JFYWNoKChbb3BlcmF0aW9uLCBzdGF0c10pID0+IHtcclxuICAgICAgICBjb25zdCBhdmcgPSBNYXRoLnJvdW5kKHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50KTtcclxuICAgICAgICBjb25zdCB7IGVtb2ppIH0gPSB0aGlzLmdldFBlcmZvcm1hbmNlU3RhdHVzKFxyXG4gICAgICAgICAgYXZnLFxyXG4gICAgICAgICAgc3RhdHMuY3VzdG9tVGhyZXNob2xkcyxcclxuICAgICAgICAgIG9wZXJhdGlvblxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcHJpbnRCbG9jaygnI2M1MzAzMCcsIGVtb2ppLCBvcGVyYXRpb24sIGF2ZywgJ3wgb3RpbWl6YXIgdXJnZW50ZScpO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBpZiAoc2xvd09wZXJhdGlvbnMubGVuZ3RoID4gMCkge1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAnJWPwn5+hIExFTlRBUyAoNTAwbXMgLSAyMDAwbXMpJyxcclxuICAgICAgICAnY29sb3I6ICNmZmY7IGZvbnQtc2l6ZTogMTRweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IG1hcmdpbi10b3A6IDEwcHg7J1xyXG4gICAgICApO1xyXG4gICAgICBzbG93T3BlcmF0aW9ucy5mb3JFYWNoKChbb3BlcmF0aW9uLCBzdGF0c10pID0+IHtcclxuICAgICAgICBjb25zdCBhdmcgPSBNYXRoLnJvdW5kKHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50KTtcclxuICAgICAgICBjb25zdCB7IGVtb2ppIH0gPSB0aGlzLmdldFBlcmZvcm1hbmNlU3RhdHVzKFxyXG4gICAgICAgICAgYXZnLFxyXG4gICAgICAgICAgc3RhdHMuY3VzdG9tVGhyZXNob2xkcyxcclxuICAgICAgICAgIG9wZXJhdGlvblxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcHJpbnRCbG9jaygnI2I3NzkxZicsIGVtb2ppLCBvcGVyYXRpb24sIGF2ZywgJ3wgcG9kZSBzZXIgbWVsaG9yYWRhJyk7XHJcbiAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGlmIChmYXN0T3BlcmF0aW9ucy5sZW5ndGggPiAwKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICclY/Cfn6IgUsOBUElEQVMgKCA8IDUwMG1zKScsXHJcbiAgICAgICAgJ2NvbG9yOiAjZmZmOyBmb250LXNpemU6IDE0cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBtYXJnaW4tdG9wOiAxMHB4OydcclxuICAgICAgKTtcclxuICAgICAgZmFzdE9wZXJhdGlvbnMuZm9yRWFjaCgoW29wZXJhdGlvbiwgc3RhdHNdKSA9PiB7XHJcbiAgICAgICAgY29uc3QgYXZnID0gTWF0aC5yb3VuZChzdGF0cy50b3RhbFRpbWUgLyBzdGF0cy5jb3VudCk7XHJcbiAgICAgICAgY29uc3QgeyBlbW9qaSB9ID0gdGhpcy5nZXRQZXJmb3JtYW5jZVN0YXR1cyhcclxuICAgICAgICAgIGF2ZyxcclxuICAgICAgICAgIHN0YXRzLmN1c3RvbVRocmVzaG9sZHMsXHJcbiAgICAgICAgICBvcGVyYXRpb25cclxuICAgICAgICApO1xyXG4gICAgICAgIHByaW50QmxvY2soJyMzOGExNjknLCBlbW9qaSwgb3BlcmF0aW9uLCBhdmcsICd8IGV4Y2VsZW50ZScpO1xyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICAvLyBSZWNvbWVuZGHDp8O1ZXNcclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAnJWNSZWNvbWVuZGHDp8O1ZXM6JyxcclxuICAgICAgJ2NvbG9yOiAjZmZmOyBmb250LXNpemU6IDEzcHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBtYXJnaW4tdG9wOiAxNHB4OyBwYWRkaW5nOiAycHggMCAycHggMDsnXHJcbiAgICApO1xyXG4gICAgaWYgKGNyaXRpY2FsT3BlcmF0aW9ucy5sZW5ndGggPiAwKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICclY+KAoiBQcmlvcml6ZSBhIG90aW1pemHDp8OjbyBkYXMgb3BlcmHDp8O1ZXMgY3LDrXRpY2FzLicsXHJcbiAgICAgICAgJ2NvbG9yOiAjZmZmOyBmb250LXNpemU6IDEzcHg7IGZvbnQtd2VpZ2h0OiBub3JtYWw7J1xyXG4gICAgICApO1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAnJWPigKIgQ29uc2lkZXJlIGltcGxlbWVudGFyIGNhY2hlIG91IG90aW1pemFyIGNvbnN1bHRhcyBTUUwgcGFyYSBlc3NhcyBvcGVyYcOnw7Vlcy4nLFxyXG4gICAgICAgICdjb2xvcjogI2ZmZjsgZm9udC1zaXplOiAxM3B4OyBmb250LXdlaWdodDogbm9ybWFsOydcclxuICAgICAgKTtcclxuICAgIH1cclxuICAgIGlmIChzbG93T3BlcmF0aW9ucy5sZW5ndGggPiAwKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICclY+KAoiBPcGVyYcOnw7VlcyBsZW50YXMgcG9kZW0gc2UgYmVuZWZpY2lhciBkZSDDrW5kaWNlcyBubyBiYW5jbyBkZSBkYWRvcy4nLFxyXG4gICAgICAgICdjb2xvcjogI2ZmZjsgZm9udC1zaXplOiAxM3B4OyBmb250LXdlaWdodDogbm9ybWFsOydcclxuICAgICAgKTtcclxuICAgIH1cclxuICAgIGlmIChjcml0aWNhbE9wZXJhdGlvbnMubGVuZ3RoID09PSAwICYmIHNsb3dPcGVyYXRpb25zLmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAnJWPigKIgVG9kYXMgYXMgb3BlcmHDp8O1ZXMgZXN0w6NvIGNvbSBwZXJmb3JtYW5jZSBleGNlbGVudGUhJyxcclxuICAgICAgICAnY29sb3I6ICNmZmY7IGZvbnQtc2l6ZTogMTNweDsgZm9udC13ZWlnaHQ6IG5vcm1hbDsnXHJcbiAgICAgICk7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gU2NvcmVcclxuICAgIGNvbnN0IHRvdGFsT3BlcmF0aW9ucyA9IHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LnNpemU7XHJcbiAgICBjb25zdCBmYXN0Q291bnQgPSBmYXN0T3BlcmF0aW9ucy5sZW5ndGg7XHJcbiAgICBjb25zdCBwZXJmb3JtYW5jZVNjb3JlID1cclxuICAgICAgdG90YWxPcGVyYXRpb25zID4gMFxyXG4gICAgICAgID8gTWF0aC5yb3VuZCgoZmFzdENvdW50IC8gdG90YWxPcGVyYXRpb25zKSAqIDEwMClcclxuICAgICAgICA6IDEwMDtcclxuICAgIGxldCBzY29yZUNvbG9yID1cclxuICAgICAgcGVyZm9ybWFuY2VTY29yZSA+PSA4MFxyXG4gICAgICAgID8gJyMzOGExNjknXHJcbiAgICAgICAgOiBwZXJmb3JtYW5jZVNjb3JlID49IDYwXHJcbiAgICAgICAgPyAnI2Y2ZTA1ZSdcclxuICAgICAgICA6ICcjYzUzMDMwJztcclxuICAgIGxldCBzY29yZU1zZyA9XHJcbiAgICAgIHBlcmZvcm1hbmNlU2NvcmUgPj0gODBcclxuICAgICAgICA/ICdFeGNlbGVudGUhIEEgbWFpb3JpYSBkYXMgb3BlcmHDp8O1ZXMgZXN0w6EgcGVyZm9ybWFuZG8gYmVtLidcclxuICAgICAgICA6IHBlcmZvcm1hbmNlU2NvcmUgPj0gNjBcclxuICAgICAgICA/ICdNw6lkaW8uIEjDoSBlc3Bhw6dvIHBhcmEgbWVsaG9yaWFzIGRlIHBlcmZvcm1hbmNlLidcclxuICAgICAgICA6ICdDcsOtdGljby4gTXVpdGFzIG9wZXJhw6fDtWVzIHByZWNpc2FtIGRlIG90aW1pemHDp8OjbyB1cmdlbnRlLic7XHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgYCVjU2NvcmUgZGUgcGVyZm9ybWFuY2U6ICR7cGVyZm9ybWFuY2VTY29yZX0lYCxcclxuICAgICAgYGNvbG9yOiAjZmZmOyBiYWNrZ3JvdW5kOiAke3Njb3JlQ29sb3J9OyBmb250LXNpemU6IDE0cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBtYXJnaW4tdG9wOiAxMHB4OyBwYWRkaW5nOiAycHggOHB4OyBib3JkZXItcmFkaXVzOiAzcHg7YFxyXG4gICAgKTtcclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICBgJWMke3Njb3JlTXNnfWAsXHJcbiAgICAgIGBjb2xvcjogI2ZmZjsgZm9udC1zaXplOiAxM3B4OyBmb250LXdlaWdodDogbm9ybWFsOyBtYXJnaW4tYm90dG9tOiA4cHg7YFxyXG4gICAgKTtcclxuICAgIGNvbnNvbGUubG9nKCcnKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfk4sgQ2F0ZWdvcml6YSBvcGVyYcOnw7VlcyBwb3IgcGVyZm9ybWFuY2VcclxuICAgKiBAcmV0dXJucyBPYmpldG8gY29tIGFycmF5cyBkZSBvcGVyYcOnw7VlcyBjYXRlZ29yaXphZGFzXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBjYXRlZ29yaXplT3BlcmF0aW9ucygpIHtcclxuICAgIGNvbnN0IGNyaXRpY2FsT3BlcmF0aW9ucyA9IEFycmF5LmZyb20oXHJcbiAgICAgIHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LmVudHJpZXMoKVxyXG4gICAgKS5maWx0ZXIoKFtfLCBzdGF0c10pID0+IHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50ID4gMjAwMCk7XHJcblxyXG4gICAgY29uc3Qgc2xvd09wZXJhdGlvbnMgPSBBcnJheS5mcm9tKHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LmVudHJpZXMoKSkuZmlsdGVyKFxyXG4gICAgICAoW18sIHN0YXRzXSkgPT4ge1xyXG4gICAgICAgIGNvbnN0IGF2ZyA9IHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50O1xyXG4gICAgICAgIHJldHVybiBhdmcgPj0gNTAwICYmIGF2ZyA8PSAyMDAwO1xyXG4gICAgICB9XHJcbiAgICApO1xyXG5cclxuICAgIGNvbnN0IGZhc3RPcGVyYXRpb25zID0gQXJyYXkuZnJvbSh0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5lbnRyaWVzKCkpLmZpbHRlcihcclxuICAgICAgKFtfLCBzdGF0c10pID0+IHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50IDwgNTAwXHJcbiAgICApO1xyXG5cclxuICAgIHJldHVybiB7IGNyaXRpY2FsT3BlcmF0aW9ucywgc2xvd09wZXJhdGlvbnMsIGZhc3RPcGVyYXRpb25zIH07XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn5KhIEV4aWJlIHJlY29tZW5kYcOnw7VlcyBiYXNlYWRhcyBuYSBhbsOhbGlzZVxyXG4gICAqIEBwYXJhbSBjcml0aWNhbE9wZXJhdGlvbnMgQXJyYXkgZGUgb3BlcmHDp8O1ZXMgY3LDrXRpY2FzXHJcbiAgICogQHBhcmFtIHNsb3dPcGVyYXRpb25zIEFycmF5IGRlIG9wZXJhw6fDtWVzIGxlbnRhc1xyXG4gICAqL1xyXG4gIHByaXZhdGUgc2hvd1JlY29tbWVuZGF0aW9ucyhcclxuICAgIGNyaXRpY2FsT3BlcmF0aW9uczogYW55W10sXHJcbiAgICBzbG93T3BlcmF0aW9uczogYW55W11cclxuICApOiB2b2lkIHtcclxuICAgIGNvbnNvbGUubG9nKCdSRUNPTUVOREHDh8OVRVM6Jyk7XHJcbiAgICBpZiAoY3JpdGljYWxPcGVyYXRpb25zLmxlbmd0aCA+IDApIHtcclxuICAgICAgY29uc29sZS5sb2coJyAgIFByaW9yaXplIGEgb3RpbWl6YcOnw6NvIGRhcyBvcGVyYcOnw7VlcyBjcsOtdGljYXMuJyk7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICcgICBDb25zaWRlcmUgaW1wbGVtZW50YXIgY2FjaGUgb3Ugb3RpbWl6YXIgY29uc3VsdGFzIFNRTCBwYXJhIGVzc2FzIG9wZXJhw6fDtWVzLidcclxuICAgICAgKTtcclxuICAgIH1cclxuICAgIGlmIChzbG93T3BlcmF0aW9ucy5sZW5ndGggPiAwKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICcgICBPcGVyYcOnw7VlcyBsZW50YXMgcG9kZW0gc2UgYmVuZWZpY2lhciBkZSDDrW5kaWNlcyBubyBiYW5jbyBkZSBkYWRvcy4nXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgICBpZiAoY3JpdGljYWxPcGVyYXRpb25zLmxlbmd0aCA9PT0gMCAmJiBzbG93T3BlcmF0aW9ucy5sZW5ndGggPT09IDApIHtcclxuICAgICAgY29uc29sZS5sb2coJyAgIFRvZGFzIGFzIG9wZXJhw6fDtWVzIGVzdMOjbyBjb20gcGVyZm9ybWFuY2UgZXhjZWxlbnRlIScpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+TiiBFeGliZSBzY29yZSBkZSBwZXJmb3JtYW5jZVxyXG4gICAqIEBwYXJhbSBmYXN0T3BlcmF0aW9uc0NvdW50IE7Dum1lcm8gZGUgb3BlcmHDp8O1ZXMgcsOhcGlkYXNcclxuICAgKi9cclxuICBwcml2YXRlIHNob3dQZXJmb3JtYW5jZVNjb3JlKGZhc3RPcGVyYXRpb25zQ291bnQ6IG51bWJlcik6IHZvaWQge1xyXG4gICAgY29uc3QgdG90YWxPcGVyYXRpb25zID0gdGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuc2l6ZTtcclxuICAgIGNvbnN0IHBlcmZvcm1hbmNlU2NvcmUgPSBNYXRoLnJvdW5kKFxyXG4gICAgICAoZmFzdE9wZXJhdGlvbnNDb3VudCAvIHRvdGFsT3BlcmF0aW9ucykgKiAxMDBcclxuICAgICk7XHJcbiAgICBjb25zb2xlLmxvZyhgU0NPUkUgREUgUEVSRk9STUFOQ0U6ICR7cGVyZm9ybWFuY2VTY29yZX0lYCk7XHJcbiAgICBpZiAocGVyZm9ybWFuY2VTY29yZSA+PSA4MCkge1xyXG4gICAgICBjb25zb2xlLmxvZygnRXhjZWxlbnRlISBBIG1haW9yaWEgZGFzIG9wZXJhw6fDtWVzIGVzdMOhIHBlcmZvcm1hbmRvIGJlbS4nKTtcclxuICAgIH0gZWxzZSBpZiAocGVyZm9ybWFuY2VTY29yZSA+PSA2MCkge1xyXG4gICAgICBjb25zb2xlLmxvZygnTcOpZGlvLiBIw6EgZXNwYcOnbyBwYXJhIG1lbGhvcmlhcyBkZSBwZXJmb3JtYW5jZS4nKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKCdDcsOtdGljby4gTXVpdGFzIG9wZXJhw6fDtWVzIHByZWNpc2FtIGRlIG90aW1pemHDp8OjbyB1cmdlbnRlLicpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gPT09PT0gQVBJIFDDmkJMSUNBID09PT09XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfk4ogTcOpdG9kbyBww7pibGljbyBwYXJhIGV4aWJpciByZWxhdMOzcmlvIGRlIHBlcmZvcm1hbmNlXHJcbiAgICogUG9kZSBzZXIgY2hhbWFkbyBubyBjb25zb2xlIGRvIG5hdmVnYWRvciBwYXJhIGFuw6FsaXNlXHJcbiAgICovXHJcbiAgcHVibGljIGdldFBlcmZvcm1hbmNlUmVwb3J0KCk6IHZvaWQge1xyXG4gICAgdGhpcy5zaG93UGVyZm9ybWFuY2VTdW1tYXJ5KCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn4+377iPIENvbnRyb2xhIGNvbnRleHRvIGRlIGV2ZW50byBhdHVhbCAoZXguOiBBcHBJbml0LCBDbGljaylcclxuICAgKi9cclxuICBwdWJsaWMgYmVnaW5FdmVudENvbnRleHQobGFiZWw6IHN0cmluZyk6IHZvaWQge1xyXG4gICAgaWYgKGxhYmVsKSB7XHJcbiAgICAgIC8vIFNlIG7Do28gaMOhIGNvbnRleHRvIGFiZXJ0bywgaW5pY2lhIHVtYSBub3ZhIGphbmVsYSByYWl6XHJcbiAgICAgIGlmICh0aGlzLmV2ZW50Q29udGV4dFN0YWNrLmxlbmd0aCA9PT0gMCkge1xyXG4gICAgICAgIHRoaXMuY3VycmVudEV2ZW50QmVnaW5BdCA9IERhdGUubm93KCk7XHJcbiAgICAgICAgdGhpcy5maXJzdFRpbWVySW5FdmVudEF0ID0gbnVsbDtcclxuICAgICAgICB0aGlzLmxhc3RUaW1lckFjdGl2aXR5QXQgPSBudWxsO1xyXG4gICAgICB9XHJcbiAgICAgIHRoaXMuZXZlbnRDb250ZXh0U3RhY2sucHVzaChsYWJlbCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICBwdWJsaWMgZW5kRXZlbnRDb250ZXh0KCk6IHZvaWQge1xyXG4gICAgaWYgKHRoaXMuZXZlbnRDb250ZXh0U3RhY2subGVuZ3RoID4gMCkge1xyXG4gICAgICB0aGlzLmV2ZW50Q29udGV4dFN0YWNrLnBvcCgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHVibGljIGFzeW5jIHJ1bkluRXZlbnRDb250ZXh0PFQ+KFxyXG4gICAgbGFiZWw6IHN0cmluZyxcclxuICAgIGZuOiAoKSA9PiBQcm9taXNlPFQ+IHwgVFxyXG4gICk6IFByb21pc2U8VD4ge1xyXG4gICAgdGhpcy5iZWdpbkV2ZW50Q29udGV4dChsYWJlbCk7XHJcbiAgICB0cnkge1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBmbigpO1xyXG4gICAgICByZXR1cm4gYXdhaXQgUHJvbWlzZS5yZXNvbHZlKHJlc3VsdCk7XHJcbiAgICB9IGZpbmFsbHkge1xyXG4gICAgICB0aGlzLmVuZEV2ZW50Q29udGV4dCgpO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+nuSBNw6l0b2RvIHDDumJsaWNvIHBhcmEgbGltcGFyIGRhZG9zIGRlIHBlcmZvcm1hbmNlXHJcbiAgICogw5p0aWwgcGFyYSByZXNldGFyIGVzdGF0w61zdGljYXMgZSBjb21lw6dhciBub3ZhIGFuw6FsaXNlXHJcbiAgICovXHJcbiAgcHVibGljIHJlc2V0UGVyZm9ybWFuY2VEYXRhKCk6IHZvaWQge1xyXG4gICAgdGhpcy5wZXJmb3JtYW5jZVRpbWVycy5jbGVhcigpO1xyXG4gICAgdGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuY2xlYXIoKTtcclxuICAgIHRoaXMuaHR0cFJlcXVlc3RzLmNsZWFyKCk7XHJcblxyXG4gICAgLy8gTWVuc2FnZW0gZXN0aWxpemFkYSBkZSBsaW1wZXphXHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgJyVj8J+nuSBbQ0xFQU5VUF0gRGFkb3MgZGUgcGVyZm9ybWFuY2UgbGltcG9zJyxcclxuICAgICAgJ2NvbG9yOiAjZmZmOyBiYWNrZ3JvdW5kOiAjNDMyNDhiZmY7IGZvbnQtc2l6ZTogMTNweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGJvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZzogNHB4IDEycHg7IG1hcmdpbjogNnB4IDA7J1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIOKPsCBDb25maWd1cmEgbyBkZWxheSBwYXJhIHJlbGF0w7NyaW8gcG9yIGluYXRpdmlkYWRlXHJcbiAgICogQHBhcmFtIHNlY29uZHMgTsO6bWVybyBkZSBzZWd1bmRvcyBkZSBpbmF0aXZpZGFkZSBwYXJhIG1vc3RyYXIgcmVsYXTDs3Jpb1xyXG4gICAqL1xyXG4gIHB1YmxpYyBzZXRJbmFjdGl2aXR5RGVsYXkoc2Vjb25kczogbnVtYmVyKTogdm9pZCB7XHJcbiAgICAvLyBEZXNhYmlsaXRhZG9cclxuICAgIGNvbnNvbGUubG9nKCfij7AgUmVsYXTDs3JpbyBwb3IgaW5hdGl2aWRhZGUgZXN0w6EgZGVzYWJpbGl0YWRvLicpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+UhyBEZXNhYmlsaXRhIHJlbGF0w7NyaW8gYXV0b23DoXRpY28gcG9yIGluYXRpdmlkYWRlXHJcbiAgICovXHJcbiAgcHVibGljIGRpc2FibGVJbmFjdGl2aXR5UmVwb3J0KCk6IHZvaWQge1xyXG4gICAgLy8gRGVzYWJpbGl0YWRvXHJcbiAgICBjb25zb2xlLmxvZygn8J+UhyBSZWxhdMOzcmlvIHBvciBpbmF0aXZpZGFkZSBqw6EgZXN0w6EgZGVzYWJpbGl0YWRvJyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDimpnvuI8gQ29uZmlndXJhIG8gdGhyZXNob2xkIHBhcmEgcmVsYXTDs3JpbyBhdXRvbcOhdGljb1xyXG4gICAqIEBwYXJhbSB0aHJlc2hvbGQgTsO6bWVybyBkZSBvcGVyYcOnw7VlcyBwYXJhIG1vc3RyYXIgcmVsYXTDs3JpbyBhdXRvbWF0aWNhbWVudGVcclxuICAgKi9cclxuICBwdWJsaWMgc2V0QXV0b1JlcG9ydFRocmVzaG9sZCh0aHJlc2hvbGQ6IG51bWJlcik6IHZvaWQge1xyXG4gICAgLy8gRGVzYWJpbGl0YWRvXHJcbiAgICBjb25zb2xlLmxvZygn4pqZ77iPIFRocmVzaG9sZCBwYXJhIHJlbGF0w7NyaW8gYXV0b23DoXRpY28gZXN0w6EgZGVzYWJpbGl0YWRvLicpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+UhyBEZXNhYmlsaXRhIHJlbGF0w7NyaW8gYXV0b23DoXRpY29cclxuICAgKi9cclxuICBwdWJsaWMgZGlzYWJsZUF1dG9SZXBvcnQoKTogdm9pZCB7XHJcbiAgICAvLyBEZXNhYmlsaXRhZG9cclxuICAgIGNvbnNvbGUubG9nKCfwn5SHIFJlbGF0w7NyaW8gYXV0b23DoXRpY28gZGVzYWJpbGl0YWRvJyk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn5OIIE9idMOpbSBlc3RhdMOtc3RpY2FzIHJlc3VtaWRhcyBzZW0gZXhpYmlyIG5vIGNvbnNvbGVcclxuICAgKiBAcmV0dXJucyBPYmpldG8gY29tIGVzdGF0w61zdGljYXMgZGUgcGVyZm9ybWFuY2VcclxuICAgKi9cclxuICBwdWJsaWMgZ2V0U3RhdGlzdGljcygpOiB7XHJcbiAgICB0b3RhbE9wZXJhdGlvbnM6IG51bWJlcjtcclxuICAgIGF2ZXJhZ2VUaW1lOiBudW1iZXI7XHJcbiAgICBmYXN0T3BlcmF0aW9uczogbnVtYmVyO1xyXG4gICAgc2xvd09wZXJhdGlvbnM6IG51bWJlcjtcclxuICAgIGNyaXRpY2FsT3BlcmF0aW9uczogbnVtYmVyO1xyXG4gICAgcGVyZm9ybWFuY2VTY29yZTogbnVtYmVyO1xyXG4gIH0ge1xyXG4gICAgY29uc3QgeyBjcml0aWNhbE9wZXJhdGlvbnMsIHNsb3dPcGVyYXRpb25zLCBmYXN0T3BlcmF0aW9ucyB9ID1cclxuICAgICAgdGhpcy5jYXRlZ29yaXplT3BlcmF0aW9ucygpO1xyXG4gICAgY29uc3QgdG90YWxPcGVyYXRpb25zID0gdGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkuc2l6ZTtcclxuXHJcbiAgICBsZXQgdG90YWxUaW1lID0gMDtcclxuICAgIGxldCB0b3RhbENvdW50ID0gMDtcclxuXHJcbiAgICB0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5mb3JFYWNoKChzdGF0cykgPT4ge1xyXG4gICAgICB0b3RhbFRpbWUgKz0gc3RhdHMudG90YWxUaW1lO1xyXG4gICAgICB0b3RhbENvdW50ICs9IHN0YXRzLmNvdW50O1xyXG4gICAgfSk7XHJcblxyXG4gICAgY29uc3QgYXZlcmFnZVRpbWUgPSB0b3RhbENvdW50ID4gMCA/IE1hdGgucm91bmQodG90YWxUaW1lIC8gdG90YWxDb3VudCkgOiAwO1xyXG4gICAgY29uc3QgcGVyZm9ybWFuY2VTY29yZSA9XHJcbiAgICAgIHRvdGFsT3BlcmF0aW9ucyA+IDBcclxuICAgICAgICA/IE1hdGgucm91bmQoKGZhc3RPcGVyYXRpb25zLmxlbmd0aCAvIHRvdGFsT3BlcmF0aW9ucykgKiAxMDApXHJcbiAgICAgICAgOiAxMDA7XHJcblxyXG4gICAgcmV0dXJuIHtcclxuICAgICAgdG90YWxPcGVyYXRpb25zLFxyXG4gICAgICBhdmVyYWdlVGltZSxcclxuICAgICAgZmFzdE9wZXJhdGlvbnM6IGZhc3RPcGVyYXRpb25zLmxlbmd0aCxcclxuICAgICAgc2xvd09wZXJhdGlvbnM6IHNsb3dPcGVyYXRpb25zLmxlbmd0aCxcclxuICAgICAgY3JpdGljYWxPcGVyYXRpb25zOiBjcml0aWNhbE9wZXJhdGlvbnMubGVuZ3RoLFxyXG4gICAgICBwZXJmb3JtYW5jZVNjb3JlLFxyXG4gICAgfTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfjq8gT2J0w6ltIGRhZG9zIGRlIHVtYSBvcGVyYcOnw6NvIGVzcGVjw61maWNhXHJcbiAgICogQHBhcmFtIG9wZXJhdGlvbk5hbWUgTm9tZSBkYSBvcGVyYcOnw6NvXHJcbiAgICogQHJldHVybnMgRXN0YXTDrXN0aWNhcyBkYSBvcGVyYcOnw6NvIG91IG51bGwgc2UgbsOjbyBlbmNvbnRyYWRhXHJcbiAgICovXHJcbiAgcHVibGljIGdldE9wZXJhdGlvblN0YXRzKFxyXG4gICAgb3BlcmF0aW9uTmFtZTogc3RyaW5nXHJcbiAgKTogKFBlcmZvcm1hbmNlU3RhdHMgJiB7IGh0dHBDYWxsczogc3RyaW5nW10gfSkgfCBudWxsIHtcclxuICAgIGNvbnN0IGJhc2VTdGF0cyA9IHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LmdldChvcGVyYXRpb25OYW1lKTtcclxuICAgIGlmICghYmFzZVN0YXRzKSByZXR1cm4gbnVsbDtcclxuXHJcbiAgICAvLyBFeHRyYWkgbm9tZXMgZGFzIHJlcXVpc2nDp8O1ZXMgSFRUUCBjb21vIGFycmF5IGRlIHN0cmluZ3NcclxuICAgIGNvbnN0IGh0dHBSZXF1ZXN0RW50cnkgPSB0aGlzLmh0dHBSZXF1ZXN0cy5nZXQob3BlcmF0aW9uTmFtZSk7XHJcbiAgICBjb25zdCBodHRwQ2FsbHM6IHN0cmluZ1tdID0gaHR0cFJlcXVlc3RFbnRyeSA/IFtvcGVyYXRpb25OYW1lXSA6IFtdO1xyXG4gICAgcmV0dXJuIHsgLi4uYmFzZVN0YXRzLCBodHRwQ2FsbHMgfTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfk4sgTGlzdGEgdG9kYXMgYXMgb3BlcmHDp8O1ZXMgbW9uaXRvcmFkYXNcclxuICAgKiBAcmV0dXJucyBBcnJheSBjb20gbm9tZXMgZGFzIG9wZXJhw6fDtWVzXHJcbiAgICovXHJcbiAgcHVibGljIGdldE1vbml0b3JlZE9wZXJhdGlvbnMoKTogc3RyaW5nW10ge1xyXG4gICAgcmV0dXJuIEFycmF5LmZyb20odGhpcy5wZXJmb3JtYW5jZVN1bW1hcnkua2V5cygpKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCflIQgVmVyaWZpY2Egc2UgaMOhIHRpbWVycyBhdGl2b3NcclxuICAgKiBAcmV0dXJucyB0cnVlIHNlIGjDoSB0aW1lcnMgZW0gZXhlY3XDp8Ojb1xyXG4gICAqL1xyXG4gIHB1YmxpYyBoYXNBY3RpdmVUaW1lcnMoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy50aW1lclN0YWNrLmxlbmd0aCA+IDA7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn5eR77iPIExpbXBhIGFwZW5hcyBvcyB0aW1lcnMgYXRpdm9zIChuw6NvIGFzIGVzdGF0w61zdGljYXMpXHJcbiAgICovXHJcbiAgcHVibGljIGNsZWFyQWN0aXZlVGltZXJzKCk6IHZvaWQge1xyXG4gICAgdGhpcy5wZXJmb3JtYW5jZVRpbWVycy5jbGVhcigpO1xyXG4gICAgdGhpcy50aW1lclN0YWNrID0gW107XHJcbiAgICB0aGlzLmZ1bmN0aW9uQ29udGV4dFN0YWNrID0gW107XHJcbiAgICBjb25zb2xlLmxvZyhcclxuICAgICAgJyVj8J+Xke+4jyBbQ0xFQU5VUF0gVGltZXJzIGF0aXZvcyBsaW1wb3MnLFxyXG4gICAgICAnY29sb3I6ICNmZmY7IGJhY2tncm91bmQ6ICMyMzI3MmY7IGZvbnQtc2l6ZTogMTNweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGJvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZzogNHB4IDEycHg7IG1hcmdpbjogNnB4IDA7J1xyXG4gICAgKTtcclxuICB9XHJcblxyXG4gIC8vID09PT09IEFVVE/igJFJTlNUUlVNRU5UQcOHw4NPIChzZW0gZGVjb3JhdG9ycykgPT09PT1cclxuICAvKipcclxuICAgKiDwn5SMIEluc3RydW1lbnRhIHRvZGFzIGFzIGZ1bsOnw7VlcyBww7pibGljYXMgZGUgdW0gb2JqZXRvL2luc3TDom5jaWEgc2VtIGFsdGVyYXIgYXNzaW5hdHVyYVxyXG4gICAqIEBwYXJhbSBvYmogT2JqZXRvIG91IGluc3TDom5jaWEgYSBpbnN0cnVtZW50YXJcclxuICAgKiBAcGFyYW0gb3B0aW9ucyBGaWx0cm9zIGUgYWp1c3RlcyBkZSBub21lXHJcbiAgICovXHJcbiAgcHVibGljIGluc3RydW1lbnRPYmplY3Qob2JqOiBhbnksIG9wdGlvbnM/OiBJbnN0cnVtZW50T3B0aW9ucyk6IHZvaWQge1xyXG4gICAgaWYgKCFvYmogfHwgdHlwZW9mIG9iaiAhPT0gJ29iamVjdCcpIHJldHVybjtcclxuICAgIGNvbnN0IG9wdDogSW5zdHJ1bWVudE9wdGlvbnMgPSB7XHJcbiAgICAgIHByZXNlcnZlVG9TdHJpbmc6IHRydWUsXHJcbiAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICB9O1xyXG5cclxuICAgIGNvbnN0IHNob3VsZEluY2x1ZGUgPSAobmFtZTogc3RyaW5nKSA9PiB7XHJcbiAgICAgIGNvbnN0IGluYyA9IG9wdC5pbmNsdWRlO1xyXG4gICAgICBjb25zdCBleGMgPSBvcHQuZXhjbHVkZTtcclxuICAgICAgY29uc3QgbWF0Y2ggPSAocDogc3RyaW5nIHwgUmVnRXhwKSA9PlxyXG4gICAgICAgIHR5cGVvZiBwID09PSAnc3RyaW5nJyA/IG5hbWUuaW5jbHVkZXMocCkgOiBwLnRlc3QobmFtZSk7XHJcbiAgICAgIGNvbnN0IG9rSW5jID0gIWluYyB8fCBpbmMuc29tZShtYXRjaCk7XHJcbiAgICAgIGNvbnN0IG9rRXhjID0gZXhjID8gIWV4Yy5zb21lKG1hdGNoKSA6IHRydWU7XHJcbiAgICAgIHJldHVybiBva0luYyAmJiBva0V4YztcclxuICAgIH07XHJcblxyXG4gICAgY29uc3QgaW5zdHJ1bWVudFByb3AgPSAodGFyZ2V0OiBhbnksIGtleTogc3RyaW5nLCBwYXRoOiBzdHJpbmdbXSkgPT4ge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHZhbHVlID0gdGFyZ2V0W2tleV07XHJcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gJ2Z1bmN0aW9uJykgcmV0dXJuO1xyXG4gICAgICAgIGNvbnN0IG9yaWdpbmFsOiBGdW5jdGlvbiA9IHZhbHVlO1xyXG4gICAgICAgIGlmICgob3JpZ2luYWwgYXMgYW55KS5fX3BtX2luc3RydW1lbnRlZCkgcmV0dXJuO1xyXG5cclxuICAgICAgICBpZiAoIXNob3VsZEluY2x1ZGUoa2V5KSkgcmV0dXJuO1xyXG5cclxuICAgICAgICBjb25zdCBvcEJhc2UgPSBrZXk7XHJcbiAgICAgICAgY29uc3Qgb3BOYW1lID0gb3B0Lm5hbWVUcmFuc2Zvcm1cclxuICAgICAgICAgID8gb3B0Lm5hbWVUcmFuc2Zvcm0ob3BCYXNlLCBwYXRoKVxyXG4gICAgICAgICAgOiBvcEJhc2U7XHJcbiAgICAgICAgY29uc3Qgd3JhcHBlZCA9IHRoaXMuY3JlYXRlV3JhcHBlcihcclxuICAgICAgICAgIG9yaWdpbmFsLFxyXG4gICAgICAgICAgb3BOYW1lLFxyXG4gICAgICAgICAgb3B0LnByZXNlcnZlVG9TdHJpbmchXHJcbiAgICAgICAgKTtcclxuXHJcbiAgICAgICAgLy8gU29tYnJhIG5vIG9iamV0byAobsOjbyBhbHRlcmEgcHJvdG90eXBlIGdsb2JhbClcclxuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBrZXksIHtcclxuICAgICAgICAgIHZhbHVlOiB3cmFwcGVkLFxyXG4gICAgICAgICAgd3JpdGFibGU6IHRydWUsXHJcbiAgICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH0gY2F0Y2gge31cclxuICAgIH07XHJcblxyXG4gICAgLy8gSW5zdHJ1bWVudGEgcHLDs3ByaWFzIHByb3ByaWVkYWRlc1xyXG4gICAgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMob2JqKS5mb3JFYWNoKChrKSA9PiBpbnN0cnVtZW50UHJvcChvYmosIGssIFtrXSkpO1xyXG5cclxuICAgIC8vIEluc3RydW1lbnRhIG3DqXRvZG9zIGRvIHByb3RvdHlwZSAoc29icmVzY3JldmVuZG8gbm8gaW5zdGFuY2UpXHJcbiAgICBjb25zdCBwcm90byA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmopO1xyXG4gICAgaWYgKHByb3RvICYmIHByb3RvICE9PSBPYmplY3QucHJvdG90eXBlKSB7XHJcbiAgICAgIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKHByb3RvKVxyXG4gICAgICAgIC5maWx0ZXIoKGspID0+IGsgIT09ICdjb25zdHJ1Y3RvcicpXHJcbiAgICAgICAgLmZvckVhY2goKGspID0+IGluc3RydW1lbnRQcm9wKG9iaiwgaywgW2tdKSk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn4yQIEluc3RydW1lbnRhIGZ1bsOnw7VlcyBlbSB1bSBuYW1lc3BhY2UgKGV4Ljogd2luZG93KSBjb20gZmlsdHJvcyBlIHByb2Z1bmRpZGFkZVxyXG4gICAqIEBwYXJhbSByb290IE9iamV0byByYWl6IChleC46IHdpbmRvdylcclxuICAgKiBAcGFyYW0gb3B0aW9ucyBpbmNsdWRlL2V4Y2x1ZGUsIG5hbWVUcmFuc2Zvcm0sIHByZXNlcnZlVG9TdHJpbmcsIG1heERlcHRoXHJcbiAgICovXHJcbiAgcHVibGljIGluc3RydW1lbnROYW1lc3BhY2Uocm9vdDogYW55LCBvcHRpb25zPzogSW5zdHJ1bWVudE9wdGlvbnMpOiB2b2lkIHtcclxuICAgIGlmICghcm9vdCB8fCB0eXBlb2Ygcm9vdCAhPT0gJ29iamVjdCcpIHJldHVybjtcclxuICAgIGNvbnN0IG9wdDogSW5zdHJ1bWVudE9wdGlvbnMgPSB7XHJcbiAgICAgIHByZXNlcnZlVG9TdHJpbmc6IHRydWUsXHJcbiAgICAgIG1heERlcHRoOiAyLFxyXG4gICAgICAuLi5vcHRpb25zLFxyXG4gICAgfTtcclxuXHJcbiAgICBjb25zdCBzaG91bGRJbmNsdWRlID0gKG5hbWU6IHN0cmluZykgPT4ge1xyXG4gICAgICBjb25zdCBpbmMgPSBvcHQuaW5jbHVkZTtcclxuICAgICAgY29uc3QgZXhjID0gb3B0LmV4Y2x1ZGU7XHJcbiAgICAgIGNvbnN0IG1hdGNoID0gKHA6IHN0cmluZyB8IFJlZ0V4cCkgPT5cclxuICAgICAgICB0eXBlb2YgcCA9PT0gJ3N0cmluZycgPyBuYW1lLmluY2x1ZGVzKHApIDogcC50ZXN0KG5hbWUpO1xyXG4gICAgICBjb25zdCBva0luYyA9ICFpbmMgfHwgaW5jLnNvbWUobWF0Y2gpO1xyXG4gICAgICBjb25zdCBva0V4YyA9IGV4YyA/ICFleGMuc29tZShtYXRjaCkgOiB0cnVlO1xyXG4gICAgICByZXR1cm4gb2tJbmMgJiYgb2tFeGM7XHJcbiAgICB9O1xyXG5cclxuICAgIGNvbnN0IHZpc2l0ZWQgPSBuZXcgV2Vha1NldDxvYmplY3Q+KCk7XHJcblxyXG4gICAgY29uc3Qgd2FsayA9IChub2RlOiBhbnksIGRlcHRoOiBudW1iZXIsIHBhdGg6IHN0cmluZ1tdKSA9PiB7XHJcbiAgICAgIGlmICghbm9kZSB8fCB0eXBlb2Ygbm9kZSAhPT0gJ29iamVjdCcpIHJldHVybjtcclxuICAgICAgaWYgKHZpc2l0ZWQuaGFzKG5vZGUpKSByZXR1cm47XHJcbiAgICAgIHZpc2l0ZWQuYWRkKG5vZGUpO1xyXG5cclxuICAgICAgaWYgKGRlcHRoID4gKG9wdC5tYXhEZXB0aCB8fCAyKSkgcmV0dXJuO1xyXG5cclxuICAgICAgY29uc3Qga2V5czogc3RyaW5nW10gPSBbXTtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBrZXlzLnB1c2goLi4uT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMobm9kZSkpO1xyXG4gICAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBrZXlzKSB7XHJcbiAgICAgICAgbGV0IHZhbHVlOiBhbnk7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIHZhbHVlID0gKG5vZGUgYXMgYW55KVtrZXldO1xyXG4gICAgICAgIH0gY2F0Y2gge1xyXG4gICAgICAgICAgY29udGludWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgICAgICBjb25zdCBvcmlnaW5hbDogRnVuY3Rpb24gPSB2YWx1ZTtcclxuICAgICAgICAgIGlmICgob3JpZ2luYWwgYXMgYW55KS5fX3BtX2luc3RydW1lbnRlZCkgY29udGludWU7XHJcbiAgICAgICAgICBpZiAoIXNob3VsZEluY2x1ZGUoa2V5KSkgY29udGludWU7XHJcblxyXG4gICAgICAgICAgY29uc3Qgb3BCYXNlID0ga2V5O1xyXG4gICAgICAgICAgY29uc3Qgb3BOYW1lID0gb3B0Lm5hbWVUcmFuc2Zvcm1cclxuICAgICAgICAgICAgPyBvcHQubmFtZVRyYW5zZm9ybShvcEJhc2UsIHBhdGguY29uY2F0KGtleSkpXHJcbiAgICAgICAgICAgIDogb3BCYXNlO1xyXG4gICAgICAgICAgY29uc3Qgd3JhcHBlZCA9IHRoaXMuY3JlYXRlV3JhcHBlcihcclxuICAgICAgICAgICAgb3JpZ2luYWwsXHJcbiAgICAgICAgICAgIG9wTmFtZSxcclxuICAgICAgICAgICAgb3B0LnByZXNlcnZlVG9TdHJpbmchXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgICAgLy8gVGVudGEgc29icmVzY3JldmVyIGZ1bsOnw6NvIG5vIG5hbWVzcGFjZVxyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KG5vZGUsIGtleSwge1xyXG4gICAgICAgICAgICAgIHZhbHVlOiB3cmFwcGVkLFxyXG4gICAgICAgICAgICAgIHdyaXRhYmxlOiB0cnVlLFxyXG4gICAgICAgICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICB9IGNhdGNoIHt9XHJcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnICYmIHZhbHVlKSB7XHJcbiAgICAgICAgICAvLyBSZWN1cnNlXHJcbiAgICAgICAgICB3YWxrKHZhbHVlLCBkZXB0aCArIDEsIHBhdGguY29uY2F0KGtleSkpO1xyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICB3YWxrKHJvb3QsIDAsIFtdKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfqp0gRW52b2x2ZSBhIGZ1bsOnw6NvIHNldFNlcnZpY2UodGFyZ2V0LnNldFNlcnZpY2UpIHBhcmEgaW5zdHJ1bWVudGFyIG8gc2VydmnDp28gcGFzc2Fkb1xyXG4gICAqIEBwYXJhbSB0YXJnZXQgT2JqZXRvIHF1ZSBwb3NzdWkgc2V0U2VydmljZShzZXJ2aWNlKVxyXG4gICAqIEBwYXJhbSBsYWJlbCBSw7N0dWxvIHVzYWRvIG5vIG5vbWUgZGFzIG9wZXJhw6fDtWVzIChleC46IFwicGFja1RlbGFcIilcclxuICAgKiBAcGFyYW0gb3B0aW9ucyBPcMOnw7VlcyBkZSBpbnN0cnVtZW50YcOnw6NvIChuYW1lVHJhbnNmb3JtLCBleGNsdWRlLCBldGMuKVxyXG4gICAqL1xyXG4gIHB1YmxpYyBob29rU2V0U2VydmljZShcclxuICAgIHRhcmdldDogYW55LFxyXG4gICAgbGFiZWw/OiBzdHJpbmcsXHJcbiAgICBvcHRpb25zPzogSW5zdHJ1bWVudE9wdGlvbnNcclxuICApOiB2b2lkIHtcclxuICAgIGlmICghdGFyZ2V0IHx8IHR5cGVvZiB0YXJnZXQgIT09ICdvYmplY3QnKSByZXR1cm47XHJcbiAgICBjb25zdCBmbiA9ICh0YXJnZXQgYXMgYW55KS5zZXRTZXJ2aWNlO1xyXG4gICAgaWYgKHR5cGVvZiBmbiAhPT0gJ2Z1bmN0aW9uJykgcmV0dXJuO1xyXG5cclxuICAgIGNvbnN0IGFscmVhZHlIb29rZWQgPSAoZm4gYXMgYW55KS5fX3BtX3NldHNlcnZpY2VfaG9va2VkO1xyXG4gICAgaWYgKGFscmVhZHlIb29rZWQpIHJldHVybjtcclxuXHJcbiAgICBjb25zdCBvcHQ6IEluc3RydW1lbnRPcHRpb25zID0ge1xyXG4gICAgICBwcmVzZXJ2ZVRvU3RyaW5nOiB0cnVlLFxyXG4gICAgICBleGNsdWRlOiBbJ2NvbnN0cnVjdG9yJ10sXHJcbiAgICAgIC4uLm9wdGlvbnMsXHJcbiAgICB9O1xyXG5cclxuICAgIGNvbnN0IGxhYmVsRmluYWwgPVxyXG4gICAgICBsYWJlbCB8fFxyXG4gICAgICAodGFyZ2V0Py5jb25zdHJ1Y3Rvcj8ubmFtZSA/IFN0cmluZyh0YXJnZXQuY29uc3RydWN0b3IubmFtZSkgOiAnc2VydmljZScpO1xyXG4gICAgLy8gR2FyYW50ZSBxdWUgbyBsYWJlbCAocGFjaykgZW50cmUgbmEgcGF0aFswXSBxdWFuZG8gaG91dmVyIG5hbWVUcmFuc2Zvcm0gY3VzdG9taXphZG9cclxuICAgIGNvbnN0IG5hbWVUcmFuc2Zvcm0gPSBvcHQubmFtZVRyYW5zZm9ybVxyXG4gICAgICA/IChuOiBzdHJpbmcsIHBhdGg/OiBzdHJpbmdbXSkgPT5cclxuICAgICAgICAgIG9wdC5uYW1lVHJhbnNmb3JtIShuLCBbbGFiZWxGaW5hbF0uY29uY2F0KHBhdGggfHwgW10pKVxyXG4gICAgICA6IChuOiBzdHJpbmcpID0+IGAke2xhYmVsRmluYWx9LiR7bn1gO1xyXG5cclxuICAgIGNvbnN0IG9yaWdpbmFsID0gZm4gYXMgRnVuY3Rpb247XHJcbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzO1xyXG4gICAgY29uc3Qgd3JhcHBlZCA9IGZ1bmN0aW9uICh0aGlzOiBhbnksIHNlcnZpY2VJbnN0YW5jZTogYW55LCAuLi5yZXN0OiBhbnlbXSkge1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIC8vIEluc3RydW1lbnRhIG8gcHLDs3ByaW8gb2JqZXRvIGFsdm8gKHBhY2spLCBwb2lzIHPDo28gc2V1cyBtw6l0b2RvcyBxdWUgcXVlcmVtb3MgbWVkaXJcclxuICAgICAgICBjb25zdCBwYWNrT2JqID0gdGhpcyB8fCB0YXJnZXQ7XHJcbiAgICAgICAgaWYgKHBhY2tPYmogJiYgdHlwZW9mIHBhY2tPYmogPT09ICdvYmplY3QnKSB7XHJcbiAgICAgICAgICBjb25zdCBhbHJlYWR5SW5zdHJ1bWVudGVkUGFjayA9IChwYWNrT2JqIGFzIGFueSlcclxuICAgICAgICAgICAgLl9fcG1fcGFja19pbnN0cnVtZW50ZWQ7XHJcbiAgICAgICAgICBpZiAoIWFscmVhZHlJbnN0cnVtZW50ZWRQYWNrKSB7XHJcbiAgICAgICAgICAgIGxvZ2dlci5pbnN0cnVtZW50T2JqZWN0KHBhY2tPYmosIHtcclxuICAgICAgICAgICAgICAuLi5vcHQsXHJcbiAgICAgICAgICAgICAgbmFtZVRyYW5zZm9ybSxcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHBhY2tPYmosICdfX3BtX3BhY2tfaW5zdHJ1bWVudGVkJywge1xyXG4gICAgICAgICAgICAgICAgdmFsdWU6IHRydWUsXHJcbiAgICAgICAgICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXHJcbiAgICAgICAgICAgICAgICBlbnVtZXJhYmxlOiBmYWxzZSxcclxuICAgICAgICAgICAgICAgIHdyaXRhYmxlOiBmYWxzZSxcclxuICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfSBjYXRjaCB7fVxyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSBjYXRjaCB7fVxyXG4gICAgICByZXR1cm4gb3JpZ2luYWwuYXBwbHkodGhpcywgW3NlcnZpY2VJbnN0YW5jZSwgLi4ucmVzdF0pO1xyXG4gICAgfSBhcyBGdW5jdGlvbjtcclxuXHJcbiAgICAvLyBtYXJjYSBjb21vIGhvb2thZG8gcGFyYSBldml0YXIgZHVwbGljYcOnw6NvXHJcbiAgICAod3JhcHBlZCBhcyBhbnkpLl9fcG1fc2V0c2VydmljZV9ob29rZWQgPSB0cnVlO1xyXG4gICAgdHJ5IHtcclxuICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgJ3NldFNlcnZpY2UnLCB7XHJcbiAgICAgICAgdmFsdWU6IHdyYXBwZWQsXHJcbiAgICAgICAgd3JpdGFibGU6IHRydWUsXHJcbiAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxyXG4gICAgICB9KTtcclxuICAgIH0gY2F0Y2gge31cclxuXHJcbiAgICAvLyBJbnN0cnVtZW50YSBpbWVkaWF0YW1lbnRlIG8gcGFjayAoY29icmUgY2Fzb3Mgb25kZSBzZXRTZXJ2aWNlIGrDoSBmb2kgY2hhbWFkbyBhbnRlcyBkbyBob29rKVxyXG4gICAgdHJ5IHtcclxuICAgICAgY29uc3QgcGFja09iaiA9IHRhcmdldDtcclxuICAgICAgY29uc3QgYWxyZWFkeUluc3RydW1lbnRlZFBhY2sgPSAocGFja09iaiBhcyBhbnkpLl9fcG1fcGFja19pbnN0cnVtZW50ZWQ7XHJcbiAgICAgIGlmICghYWxyZWFkeUluc3RydW1lbnRlZFBhY2spIHtcclxuICAgICAgICB0aGlzLmluc3RydW1lbnRPYmplY3QocGFja09iaiwge1xyXG4gICAgICAgICAgLi4ub3B0LFxyXG4gICAgICAgICAgbmFtZVRyYW5zZm9ybSxcclxuICAgICAgICB9KTtcclxuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkocGFja09iaiwgJ19fcG1fcGFja19pbnN0cnVtZW50ZWQnLCB7XHJcbiAgICAgICAgICB2YWx1ZTogdHJ1ZSxcclxuICAgICAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcclxuICAgICAgICAgIGVudW1lcmFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgd3JpdGFibGU6IGZhbHNlLFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9XHJcbiAgICB9IGNhdGNoIHt9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiDwn6qdIEhvb2sgZGUgc2V0U2VydmljZSBwYXJhIHbDoXJpb3MgYWx2b3MgZW0gdW1hIMO6bmljYSBjaGFtYWRhXHJcbiAgICogQHBhcmFtIHRhcmdldHMgTWFwYSBkZSBsYWJlbCAtPiBvYmpldG8gcXVlIGNvbnTDqW0gc2V0U2VydmljZVxyXG4gICAqIEBwYXJhbSBvcHRpb25zIE9ww6fDtWVzIGRlIGluc3RydW1lbnRhw6fDo29cclxuICAgKi9cclxuICBwdWJsaWMgaG9va1NldFNlcnZpY2VCdWxrKFxyXG4gICAgdGFyZ2V0czogUmVjb3JkPHN0cmluZywgYW55PixcclxuICAgIG9wdGlvbnM/OiBJbnN0cnVtZW50T3B0aW9uc1xyXG4gICk6IHZvaWQge1xyXG4gICAgaWYgKCF0YXJnZXRzIHx8IHR5cGVvZiB0YXJnZXRzICE9PSAnb2JqZWN0JykgcmV0dXJuO1xyXG4gICAgZm9yIChjb25zdCBbbGFiZWwsIG9ial0gb2YgT2JqZWN0LmVudHJpZXModGFyZ2V0cykpIHtcclxuICAgICAgY29uc3QgaGFzU2V0U2VydmljZSA9ICEhKFxyXG4gICAgICAgIG9iaiAmJiB0eXBlb2YgKG9iaiBhcyBhbnkpLnNldFNlcnZpY2UgPT09ICdmdW5jdGlvbidcclxuICAgICAgKTtcclxuICAgICAgaWYgKGhhc1NldFNlcnZpY2UpIHtcclxuICAgICAgICB0aGlzLmhvb2tTZXRTZXJ2aWNlKG9iaiwgbGFiZWwsIG9wdGlvbnMpO1xyXG4gICAgICB9IGVsc2UgaWYgKG9iaiAmJiB0eXBlb2Ygb2JqID09PSAnb2JqZWN0Jykge1xyXG4gICAgICAgIC8vIEluc3RydW1lbnRhIG9iamV0b3Mgc2VtIHNldFNlcnZpY2UgdXNhbmRvIG8gbGFiZWwgY29tbyBwcmVmaXhvXHJcbiAgICAgICAgY29uc3Qgb3B0ID0geyAuLi5vcHRpb25zIH07XHJcbiAgICAgICAgY29uc3QgbmFtZVRyYW5zZm9ybSA9IG9wdC5uYW1lVHJhbnNmb3JtXHJcbiAgICAgICAgICA/IChuOiBzdHJpbmcsIHBhdGg/OiBzdHJpbmdbXSkgPT5cclxuICAgICAgICAgICAgICBvcHQubmFtZVRyYW5zZm9ybSEobiwgW1N0cmluZyhsYWJlbCldLmNvbmNhdChwYXRoIHx8IFtdKSlcclxuICAgICAgICAgIDogKG46IHN0cmluZykgPT4gYCR7U3RyaW5nKGxhYmVsKX0uJHtufWA7XHJcbiAgICAgICAgdGhpcy5pbnN0cnVtZW50T2JqZWN0KG9iaiwge1xyXG4gICAgICAgICAgLi4ub3B0LFxyXG4gICAgICAgICAgbmFtZVRyYW5zZm9ybSxcclxuICAgICAgICAgIGV4Y2x1ZGU6IFsnY29uc3RydWN0b3InLCAuLi4ob3B0LmV4Y2x1ZGUgfHwgW10pXSxcclxuICAgICAgICB9KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICog8J+MkCBWYXJyZSB1bSBuYW1lc3BhY2UgZSBhcGxpY2EgaG9vayBlbSBxdWFscXVlciBmdW7Dp8OjbyBzZXRTZXJ2aWNlIGVuY29udHJhZGFcclxuICAgKiBAcGFyYW0gcm9vdCBPYmpldG8gcmFpeiAoZXguOiB3aW5kb3csIG1vZHVsZSBzY29wZSB3cmFwcGVyLCBldGMuKVxyXG4gICAqIEBwYXJhbSBvcHRpb25zIEZpbHRyb3MgZSByZWdyYXMgZGUgbm9tZWHDp8Ojb1xyXG4gICAqL1xyXG4gIHB1YmxpYyBob29rU2V0U2VydmljZU5hbWVzcGFjZShcclxuICAgIHJvb3Q6IGFueSxcclxuICAgIG9wdGlvbnM/OiBIb29rU2V0U2VydmljZU9wdGlvbnNcclxuICApOiB2b2lkIHtcclxuICAgIGlmICghcm9vdCB8fCB0eXBlb2Ygcm9vdCAhPT0gJ29iamVjdCcpIHJldHVybjtcclxuICAgIGNvbnN0IG9wdDogSG9va1NldFNlcnZpY2VPcHRpb25zID0ge1xyXG4gICAgICBwcmVzZXJ2ZVRvU3RyaW5nOiB0cnVlLFxyXG4gICAgICBleGNsdWRlOiBbJ2NvbnN0cnVjdG9yJ10sXHJcbiAgICAgIG1heFdhbGtEZXB0aDogMixcclxuICAgICAgLi4ub3B0aW9ucyxcclxuICAgIH07XHJcblxyXG4gICAgY29uc3Qgc2hvdWxkSW5jbHVkZU9iaiA9IChwYXRoU3RyOiBzdHJpbmcpID0+IHtcclxuICAgICAgY29uc3QgaW5jID0gb3B0LmluY2x1ZGVPYmplY3RzO1xyXG4gICAgICBjb25zdCBleGMgPSBvcHQuZXhjbHVkZU9iamVjdHM7XHJcbiAgICAgIGNvbnN0IG1hdGNoID0gKHA6IHN0cmluZyB8IFJlZ0V4cCkgPT5cclxuICAgICAgICB0eXBlb2YgcCA9PT0gJ3N0cmluZycgPyBwYXRoU3RyLmluY2x1ZGVzKHApIDogcC50ZXN0KHBhdGhTdHIpO1xyXG4gICAgICBjb25zdCBva0luYyA9ICFpbmMgfHwgaW5jLnNvbWUobWF0Y2gpO1xyXG4gICAgICBjb25zdCBva0V4YyA9IGV4YyA/ICFleGMuc29tZShtYXRjaCkgOiB0cnVlO1xyXG4gICAgICByZXR1cm4gb2tJbmMgJiYgb2tFeGM7XHJcbiAgICB9O1xyXG5cclxuICAgIGNvbnN0IHZpc2l0ZWQgPSBuZXcgV2Vha1NldDxvYmplY3Q+KCk7XHJcblxyXG4gICAgY29uc3Qgd2FsayA9IChub2RlOiBhbnksIGRlcHRoOiBudW1iZXIsIHBhdGg6IHN0cmluZ1tdKSA9PiB7XHJcbiAgICAgIGlmICghbm9kZSB8fCB0eXBlb2Ygbm9kZSAhPT0gJ29iamVjdCcpIHJldHVybjtcclxuICAgICAgaWYgKHZpc2l0ZWQuaGFzKG5vZGUpKSByZXR1cm47XHJcbiAgICAgIHZpc2l0ZWQuYWRkKG5vZGUpO1xyXG4gICAgICBpZiAoZGVwdGggPiAob3B0Lm1heFdhbGtEZXB0aCB8fCAyKSkgcmV0dXJuO1xyXG5cclxuICAgICAgY29uc3Qga2V5czogc3RyaW5nW10gPSBbXTtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBrZXlzLnB1c2goLi4uT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMobm9kZSkpO1xyXG4gICAgICB9IGNhdGNoIHt9XHJcblxyXG4gICAgICBmb3IgKGNvbnN0IGtleSBvZiBrZXlzKSB7XHJcbiAgICAgICAgbGV0IHZhbHVlOiBhbnk7XHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgIHZhbHVlID0gKG5vZGUgYXMgYW55KVtrZXldO1xyXG4gICAgICAgIH0gY2F0Y2gge1xyXG4gICAgICAgICAgY29udGludWU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBjb25zdCBwYXRoTm93ID0gcGF0aC5jb25jYXQoa2V5KTtcclxuICAgICAgICBjb25zdCBwYXRoU3RyID0gcGF0aE5vdy5qb2luKCcuJyk7XHJcblxyXG4gICAgICAgIGlmICh2YWx1ZSAmJiB0eXBlb2YgdmFsdWUgPT09ICdvYmplY3QnKSB7XHJcbiAgICAgICAgICAvLyBTZSBvIG9iamV0byB0aXZlciBzZXRTZXJ2aWNlLCBhcGxpY2EgaG9va1xyXG4gICAgICAgICAgY29uc3QgZm4gPSAodmFsdWUgYXMgYW55KS5zZXRTZXJ2aWNlO1xyXG4gICAgICAgICAgaWYgKHR5cGVvZiBmbiA9PT0gJ2Z1bmN0aW9uJyAmJiBzaG91bGRJbmNsdWRlT2JqKHBhdGhTdHIpKSB7XHJcbiAgICAgICAgICAgIGNvbnN0IGxhYmVsRnJvbVBhdGggPVxyXG4gICAgICAgICAgICAgIG9wdC5sYWJlbEZyb21QYXRoIHx8ICgocDogc3RyaW5nW10pID0+IHAuam9pbignLicpKTtcclxuICAgICAgICAgICAgdGhpcy5ob29rU2V0U2VydmljZSh2YWx1ZSwgbGFiZWxGcm9tUGF0aChwYXRoTm93KSwgb3B0KTtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAvLyBDb250aW51YSBhIHZhcnJlZHVyYVxyXG4gICAgICAgICAgd2Fsayh2YWx1ZSwgZGVwdGggKyAxLCBwYXRoTm93KTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgd2Fsayhyb290LCAwLCBbXSk7XHJcbiAgfVxyXG5cclxuICAvLyA9PT09PSBIZWxwZXJzID09PT09XHJcbiAgLyoqXHJcbiAgICogQ3JpYSB3cmFwcGVyIHF1ZSBpbmljaWEvZW5jZXJyYSB0aW1lciBwcmVzZXJ2YW5kbyBjb250ZXh0byBlIHRvU3RyaW5nKClcclxuICAgKi9cclxuICBwcml2YXRlIGNyZWF0ZVdyYXBwZXIoXHJcbiAgICBvcmlnaW5hbDogRnVuY3Rpb24sXHJcbiAgICBvcE5hbWU6IHN0cmluZyxcclxuICAgIHByZXNlcnZlVG9TdHJpbmc6IGJvb2xlYW5cclxuICApOiBGdW5jdGlvbiB7XHJcbiAgICAvLyBJZGVtcG90w6puY2lhOiByZXV0aWxpemEgd3JhcHBlciBzZSBqw6EgZXhpc3RpclxyXG4gICAgY29uc3QgZXhpc3RpbmcgPSB0aGlzLmluc3RydW1lbnRlZEZucy5nZXQob3JpZ2luYWwpO1xyXG4gICAgaWYgKGV4aXN0aW5nKSByZXR1cm4gZXhpc3Rpbmc7XHJcblxyXG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcztcclxuICAgIGNvbnN0IHdyYXBwZWQgPSBmdW5jdGlvbiAodGhpczogYW55LCAuLi5hcmdzOiBhbnlbXSkge1xyXG4gICAgICAvLyBEZXRlcm1pbmEgbm9tZSBmaW5hbCBjb20gZmFsbGJhY2sgYW8gbm9tZSBkbyBhcnF1aXZvIChwYWNrKSB2aWEgc3RhY2t0cmFjZVxyXG4gICAgICBsZXQgZmluYWxOYW1lID0gb3BOYW1lO1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHBhcnRzID0gU3RyaW5nKG9wTmFtZSkuc3BsaXQoJy4nKTtcclxuICAgICAgICBjb25zdCBtZXRob2ROYW1lID0gcGFydHNbcGFydHMubGVuZ3RoIC0gMV07XHJcbiAgICAgICAgY29uc3QgcHJlZml4ID0gcGFydHMubGVuZ3RoID4gMSA/IHBhcnRzWzBdIDogdW5kZWZpbmVkO1xyXG4gICAgICAgIGlmICghcHJlZml4IHx8IHByZWZpeC50b0xvd2VyQ2FzZSgpID09PSBtZXRob2ROYW1lLnRvTG93ZXJDYXNlKCkpIHtcclxuICAgICAgICAgIGNvbnN0IGZpbGVMYWJlbCA9IGxvZ2dlci5ndWVzc0ZpbGVMYWJlbEZyb21TdGFjaygpO1xyXG4gICAgICAgICAgaWYgKGZpbGVMYWJlbCkge1xyXG4gICAgICAgICAgICBmaW5hbE5hbWUgPSBgJHtmaWxlTGFiZWx9LiR7bWV0aG9kTmFtZX1gO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgfSBjYXRjaCB7fVxyXG5cclxuICAgICAgLy8gSGVyZGEgbGFiZWwgZG8gZXZlbnRvIGF0aXZvIChqw6EgZ2FyYW50aWRvIHBlbG8gbG9nZ2VyKVxyXG4gICAgICBsb2dnZXIuc3RhcnRUaW1lcihmaW5hbE5hbWUpO1xyXG4gICAgICB0cnkge1xyXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IG9yaWdpbmFsLmFwcGx5KHRoaXMsIGFyZ3MpO1xyXG4gICAgICAgIGlmIChyZXN1bHQgJiYgdHlwZW9mIChyZXN1bHQgYXMgYW55KS50aGVuID09PSAnZnVuY3Rpb24nKSB7XHJcbiAgICAgICAgICAvLyBQcm9taXNlcy9hc3luY1xyXG4gICAgICAgICAgcmV0dXJuIChyZXN1bHQgYXMgUHJvbWlzZTxhbnk+KS5maW5hbGx5KCgpID0+IHtcclxuICAgICAgICAgICAgbG9nZ2VyLmVuZFRpbWVyKGZpbmFsTmFtZSk7XHJcbiAgICAgICAgICB9KTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgbG9nZ2VyLmVuZFRpbWVyKGZpbmFsTmFtZSk7XHJcbiAgICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgICAgIH1cclxuICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgIGxvZ2dlci5lbmRUaW1lcihmaW5hbE5hbWUsICdlcnJvJyk7XHJcbiAgICAgICAgdGhyb3cgZTtcclxuICAgICAgfVxyXG4gICAgfSBhcyBGdW5jdGlvbjtcclxuXHJcbiAgICAvLyBNYXJjYSBpbnN0cnVtZW50YWRvXHJcbiAgICAob3JpZ2luYWwgYXMgYW55KS5fX3BtX2luc3RydW1lbnRlZCA9IHRydWU7XHJcbiAgICAod3JhcHBlZCBhcyBhbnkpLl9fcG1faW5zdHJ1bWVudGVkID0gdHJ1ZTtcclxuXHJcbiAgICAvLyBQcmVzZXJ2YSB0b1N0cmluZygpIHBhcmEgY29tcGF0aWJpbGlkYWRlIGNvbSByZWdleFxyXG4gICAgaWYgKHByZXNlcnZlVG9TdHJpbmcpIHtcclxuICAgICAgdHJ5IHtcclxuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkod3JhcHBlZCwgJ3RvU3RyaW5nJywge1xyXG4gICAgICAgICAgdmFsdWU6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIG9yaWdpbmFsLnRvU3RyaW5nKCk7XHJcbiAgICAgICAgICB9LFxyXG4gICAgICAgICAgd3JpdGFibGU6IGZhbHNlLFxyXG4gICAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxyXG4gICAgICAgIH0pO1xyXG4gICAgICB9IGNhdGNoIHt9XHJcbiAgICB9XHJcblxyXG4gICAgLy8gVGVudGEgaWd1YWxhciBvIG5vbWUgKG5lbSBzZW1wcmUgw6kgcG9zc8OtdmVsKVxyXG4gICAgdHJ5IHtcclxuICAgICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHdyYXBwZWQsICduYW1lJywge1xyXG4gICAgICAgIHZhbHVlOiBvcmlnaW5hbC5uYW1lIHx8IG9wTmFtZSxcclxuICAgICAgICBjb25maWd1cmFibGU6IHRydWUsXHJcbiAgICAgIH0pO1xyXG4gICAgfSBjYXRjaCB7fVxyXG5cclxuICAgIHRoaXMuaW5zdHJ1bWVudGVkRm5zLnNldChvcmlnaW5hbCwgd3JhcHBlZCk7XHJcbiAgICByZXR1cm4gd3JhcHBlZDtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCflI4gVGVudGEgaW5mZXJpciBvIG5vbWUgZG8gYXJxdWl2byAocGFjaykgYSBwYXJ0aXIgZG8gc3RhY2t0cmFjZVxyXG4gICAqIFByZWZlcsOqbmNpYSBwb3Igbm9tZXMgcXVlIGNvbWXDp2FtIGNvbSBcInBhY2tcIlxyXG4gICAqL1xyXG4gIHByaXZhdGUgZ3Vlc3NGaWxlTGFiZWxGcm9tU3RhY2soKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcclxuICAgIHRyeSB7XHJcbiAgICAgIGNvbnN0IGVyciA9IG5ldyBFcnJvcigpO1xyXG4gICAgICBjb25zdCBzdGFjayA9IFN0cmluZygoZXJyIGFzIGFueSkuc3RhY2sgfHwgJycpO1xyXG4gICAgICBjb25zdCBsaW5lcyA9IHN0YWNrLnNwbGl0KCdcXG4nKTtcclxuICAgICAgY29uc3QgY2FuZGlkYXRlczogc3RyaW5nW10gPSBbXTtcclxuICAgICAgZm9yIChjb25zdCBsaW5lIG9mIGxpbmVzKSB7XHJcbiAgICAgICAgY29uc3QgbSA9IGxpbmUubWF0Y2goXHJcbiAgICAgICAgICAvW1xcXFxcXC9dKFtBLVphLXowLTlfLV0rKVxcLig/OnRzfGpzKSg/OjpcXGQrOlxcZCt8XFw/fCQpL1xyXG4gICAgICAgICk7XHJcbiAgICAgICAgaWYgKG0gJiYgbVsxXSkge1xyXG4gICAgICAgICAgY29uc3QgYmFzZSA9IG1bMV07XHJcbiAgICAgICAgICBpZiAoL15wYWNrL2kudGVzdChiYXNlKSkge1xyXG4gICAgICAgICAgICByZXR1cm4gYmFzZTtcclxuICAgICAgICAgIH1cclxuICAgICAgICAgIGNhbmRpZGF0ZXMucHVzaChiYXNlKTtcclxuICAgICAgICB9XHJcbiAgICAgIH1cclxuICAgICAgcmV0dXJuIGNhbmRpZGF0ZXNbMF07XHJcbiAgICB9IGNhdGNoIHtcclxuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfkr4gRXhwb3J0YSBkYWRvcyBkZSBwZXJmb3JtYW5jZSBwYXJhIEpTT05cclxuICAgKiBAcmV0dXJucyBTdHJpbmcgSlNPTiBjb20gdG9kb3Mgb3MgZGFkb3NcclxuICAgKi9cclxuICBwdWJsaWMgZXhwb3J0RGF0YSgpOiBzdHJpbmcge1xyXG4gICAgY29uc3QgZGF0YSA9IHtcclxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXHJcbiAgICAgIHNlcnZpY2VOYW1lOiB0aGlzLnNlcnZpY2VOYW1lLFxyXG4gICAgICBzdGF0aXN0aWNzOiB0aGlzLmdldFN0YXRpc3RpY3MoKSxcclxuICAgICAgb3BlcmF0aW9uczogQXJyYXkuZnJvbSh0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5lbnRyaWVzKCkpLm1hcChcclxuICAgICAgICAoW25hbWUsIHN0YXRzXSkgPT4gKHtcclxuICAgICAgICAgIG5hbWUsXHJcbiAgICAgICAgICAuLi5zdGF0cyxcclxuICAgICAgICAgIGF2ZXJhZ2VUaW1lOiBNYXRoLnJvdW5kKHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50KSxcclxuICAgICAgICB9KVxyXG4gICAgICApLFxyXG4gICAgfTtcclxuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAyKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIPCfjq8gQ2FsY3VsYSB0aHJlc2hvbGRzIGRpbsOibWljb3MgYmFzZWFkb3MgbmEgcXVhbnRpZGFkZSBkZSBleGVjdcOnw7Vlc1xyXG4gICAqIEBwYXJhbSBleGVjdXRpb25Db3VudCBOw7ptZXJvIGRlIGV4ZWN1w6fDtWVzIGRhIG9wZXJhw6fDo29cclxuICAgKiBAcmV0dXJucyBPYmpldG8gY29tIHRocmVzaG9sZHMgY2FsY3VsYWRvcyBkaW5hbWljYW1lbnRlXHJcbiAgICovXHJcbiAgcHJpdmF0ZSBjYWxjdWxhdGVEeW5hbWljVGhyZXNob2xkcyhcclxuICAgIGV4ZWN1dGlvbkNvdW50OiBudW1iZXJcclxuICApOiBEeW5hbWljVGhyZXNob2xkcyB7XHJcbiAgICBpZiAoZXhlY3V0aW9uQ291bnQgPT09IDEpIHtcclxuICAgICAgLy8gUHJpbWVpcmEgZXhlY3XDp8OjbyAtIGV4cGVjdGF0aXZhcyBtYWlzIHJlbGF4YWRhc1xyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIG9rOiAxMDAwLCAvLyAxIHNlZ3VuZG9cclxuICAgICAgICBsZW50bzogMjAwMCwgLy8gMiBzZWd1bmRvc1xyXG4gICAgICAgIGNyaXRpY286IDIwMDAsIC8vIDIgc2VndW5kb3MgKGxlbnRvIGUgY3JpdGljbyBpZ3VhaXMgcGFyYSBtYW50ZXIgc8OzIDMgZXN0YWRvcylcclxuICAgICAgfTtcclxuICAgIH0gZWxzZSBpZiAoZXhlY3V0aW9uQ291bnQgPD0gNSkge1xyXG4gICAgICAvLyBBdMOpIDUgZXhlY3XDp8O1ZXMgLSBzaXN0ZW1hIGFpbmRhIGFxdWVjZW5kb1xyXG4gICAgICByZXR1cm4ge1xyXG4gICAgICAgIG9rOiA1MDAsIC8vIDUwMG1zXHJcbiAgICAgICAgbGVudG86IDEwMDAsIC8vIDEgc2VndW5kb1xyXG4gICAgICAgIGNyaXRpY286IDEwMDAsIC8vIDEgc2VndW5kb1xyXG4gICAgICB9O1xyXG4gICAgfSBlbHNlIGlmIChleGVjdXRpb25Db3VudCA8PSAxMCkge1xyXG4gICAgICAvLyBBdMOpIDEwIGV4ZWN1w6fDtWVzIC0gc2lzdGVtYSBvdGltaXphZG9cclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBvazogMTAwLCAvLyAxMDBtc1xyXG4gICAgICAgIGxlbnRvOiAyNTAsIC8vIDI1MG1zXHJcbiAgICAgICAgY3JpdGljbzogMjUwLCAvLyAyNTBtc1xyXG4gICAgICB9O1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgLy8gQWNpbWEgZGUgMTAgZXhlY3XDp8O1ZXMgLSBleHBlY3RhdGl2YXMgYWx0YXNcclxuICAgICAgcmV0dXJuIHtcclxuICAgICAgICBvazogMjAsIC8vIDIwbXNcclxuICAgICAgICBsZW50bzogNjAsIC8vIDYwbXNcclxuICAgICAgICBjcml0aWNvOiA2MCwgLy8gNjBtc1xyXG4gICAgICB9O1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHVibGljIGluamVjdE1vbml0b3JCdXR0b24oKTogdm9pZCB7XHJcbiAgICBpZiAoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2J0bi1tb25pdG9yYXItcGVyZm9ybWFuY2UnKSkgcmV0dXJuO1xyXG5cclxuICAgIC8vID09PT09PSBFU1RJTE8gTU9ERVJOTyBFIEJPVMODTyBERSBPTEhPID09PT09PVxyXG4gICAgY29uc3QgQlVUVE9OX0NPTE9SID0gJyMyMzI3MmYnOyAvLyBjaW56YSBlc2N1cm8gZWxlZ2FudGVcclxuICAgIGNvbnN0IEJVVFRPTl9URVhUX0NPTE9SID0gJyNmM2Y0ZjYnO1xyXG4gICAgY29uc3QgQlVUVE9OX1JBRElVUyA9ICc4cHgnO1xyXG4gICAgY29uc3QgQlVUVE9OX0ZPTlRfU0laRSA9ICcxM3B4JztcclxuICAgIGNvbnN0IEJVVFRPTl9QQURESU5HID0gJzEwcHggNXB4JztcclxuICAgIGNvbnN0IEJVVFRPTl9XSURUSCA9ICcyMTBweCc7XHJcbiAgICBjb25zdCBCVVRUT05fSEVJR0hUID0gJzQ0cHgnO1xyXG4gICAgY29uc3QgQlVUVE9OX0JPWF9TSEFET1cgPSAnMCAycHggMTJweCAjMDAwMic7XHJcbiAgICBjb25zdCBCVVRUT05fQk9SREVSID0gJzFweCBzb2xpZCAjMzUzYTQwJztcclxuXHJcbiAgICAvLyBDb250YWluZXIgcGFyYSBhbmltYcOnw6NvXHJcbiAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuICAgIGNvbnRhaW5lci5pZCA9ICdwZXJmb3JtYW5jZS1tb25pdG9yLWNvbnRhaW5lcic7XHJcbiAgICBPYmplY3QuYXNzaWduKGNvbnRhaW5lci5zdHlsZSwge1xyXG4gICAgICBwb3NpdGlvbjogJ2ZpeGVkJyxcclxuICAgICAgYm90dG9tOiAnNXB4JyxcclxuICAgICAgbGVmdDogJzUwcHgnLFxyXG4gICAgICB6SW5kZXg6ICc5OTk5JyxcclxuICAgICAgZGlzcGxheTogJ2ZsZXgnLFxyXG4gICAgICBmbGV4RGlyZWN0aW9uOiAnY29sdW1uJyxcclxuICAgICAgYWxpZ25JdGVtczogJ2NlbnRlcicsXHJcbiAgICAgIGp1c3RpZnlDb250ZW50OiAnZmxleC1lbmQnLFxyXG4gICAgICB0cmFuc2l0aW9uOiAndHJhbnNmb3JtIDAuNXMgY3ViaWMtYmV6aWVyKC40LDIsLjYsMSknLFxyXG4gICAgICBnYXA6ICcxMHB4JyxcclxuICAgIH0pO1xyXG5cclxuICAgIC8vIEZ1bsOnw6NvIHBhcmEgY3JpYXIgYm90w7VlcyBwYWRyb25pemFkb3NcclxuICAgIGNvbnN0IGNyaWFyQm90YW8gPSAoaWQ6IHN0cmluZywgdGV4dG86IHN0cmluZykgPT4ge1xyXG4gICAgICBjb25zdCBidG4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdidXR0b24nKTtcclxuICAgICAgYnRuLmlkID0gaWQ7XHJcbiAgICAgIGJ0bi5pbm5lclRleHQgPSB0ZXh0bztcclxuICAgICAgT2JqZWN0LmFzc2lnbihidG4uc3R5bGUsIHtcclxuICAgICAgICB3aWR0aDogQlVUVE9OX1dJRFRILFxyXG4gICAgICAgIGhlaWdodDogQlVUVE9OX0hFSUdIVCxcclxuICAgICAgICBiYWNrZ3JvdW5kOiBCVVRUT05fQ09MT1IsXHJcbiAgICAgICAgY29sb3I6IEJVVFRPTl9URVhUX0NPTE9SLFxyXG4gICAgICAgIGJvcmRlclJhZGl1czogQlVUVE9OX1JBRElVUyxcclxuICAgICAgICBib3JkZXI6IEJVVFRPTl9CT1JERVIsXHJcbiAgICAgICAgcGFkZGluZzogQlVUVE9OX1BBRERJTkcsXHJcbiAgICAgICAgZm9udFNpemU6IEJVVFRPTl9GT05UX1NJWkUsXHJcbiAgICAgICAgZm9udFdlaWdodDogJzYwMCcsXHJcbiAgICAgICAgY3Vyc29yOiAncG9pbnRlcicsXHJcbiAgICAgICAgYm94U2hhZG93OiBCVVRUT05fQk9YX1NIQURPVyxcclxuICAgICAgICBsZXR0ZXJTcGFjaW5nOiAnMC4wMWVtJyxcclxuICAgICAgICB0ZXh0QWxpZ246ICdjZW50ZXInLFxyXG4gICAgICAgIHRyYW5zaXRpb246XHJcbiAgICAgICAgICAnYmFja2dyb3VuZCAwLjJzLCBjb2xvciAwLjJzLCB0cmFuc2Zvcm0gMC41cyBjdWJpYy1iZXppZXIoLjQsMiwuNiwxKScsXHJcbiAgICAgIH0pO1xyXG4gICAgICBidG4ub25tb3VzZWVudGVyID0gKCkgPT4ge1xyXG4gICAgICAgIGJ0bi5zdHlsZS5iYWNrZ3JvdW5kID0gJyMzNTNhNDAnO1xyXG4gICAgICAgIGJ0bi5zdHlsZS5jb2xvciA9ICcjZmZmJztcclxuICAgICAgfTtcclxuICAgICAgYnRuLm9ubW91c2VsZWF2ZSA9ICgpID0+IHtcclxuICAgICAgICBidG4uc3R5bGUuYmFja2dyb3VuZCA9IEJVVFRPTl9DT0xPUjtcclxuICAgICAgICBidG4uc3R5bGUuY29sb3IgPSBCVVRUT05fVEVYVF9DT0xPUjtcclxuICAgICAgfTtcclxuICAgICAgcmV0dXJuIGJ0bjtcclxuICAgIH07XHJcblxyXG4gICAgLy8gQm90w7VlcyBwcmluY2lwYWlzXHJcbiAgICBjb25zdCBidG5NYWluID0gY3JpYXJCb3RhbyhcclxuICAgICAgJ2J0bi1tb25pdG9yYXItcGVyZm9ybWFuY2UnLFxyXG4gICAgICAnUGFyYXIgbW9uaXRvcmFtZW50bydcclxuICAgICk7XHJcbiAgICBjb25zdCBidG5GaW5hbGl6YXIgPSBjcmlhckJvdGFvKFxyXG4gICAgICAnYnRuLWZpbmFsaXphci1tb25pdG9yYW1lbnRvJyxcclxuICAgICAgJ0ZpbmFsaXphciBtb25pdG9yYW1lbnRvJ1xyXG4gICAgKTtcclxuICAgIGNvbnN0IGJ0blJlcXVpc2ljb2VzRGV0YWxoYWRhcyA9IGNyaWFyQm90YW8oXHJcbiAgICAgICdidG4tcmVxLWRldGFsaGFkYXMtcGVyZm9ybWFuY2UnLFxyXG4gICAgICAnUmVxdWlzacOnw7VlcyBkZXRhbGhhZGFzJ1xyXG4gICAgKTtcclxuICAgIGNvbnN0IGJ0bkFuYWxpc2VEZXRhbGhhZGEgPSBjcmlhckJvdGFvKFxyXG4gICAgICAnYnRuLWFuYWxpc2UtZGV0YWxoYWRhLXBlcmZvcm1hbmNlJyxcclxuICAgICAgJ0Fuw6FsaXNlIGRldGFsaGFkYSdcclxuICAgICk7XHJcbiAgICBjb25zdCBidG5SZWxhdG9yaW9FdmVudG8gPSBjcmlhckJvdGFvKFxyXG4gICAgICAnYnRuLXJlbGF0b3Jpby1ldmVudG8tcGVyZm9ybWFuY2UnLFxyXG4gICAgICAnUmVsYXTDs3JpbyBzZXBhcmFkbydcclxuICAgICk7XHJcblxyXG4gICAgLy8gSW5pY2lhbG1lbnRlIGVzY29uZGlkb3NcclxuICAgIGJ0blJlcXVpc2ljb2VzRGV0YWxoYWRhcy5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG4gICAgYnRuQW5hbGlzZURldGFsaGFkYS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG4gICAgYnRuRmluYWxpemFyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcbiAgICBidG5SZWxhdG9yaW9FdmVudG8uc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuXHJcbiAgICAvLyBCb3TDo28gZGUgZXNjb25kZXIvbW9zdHJhciBjb20gw61jb25lIGRlIG9saG8gKGZvcmEgZG8gY29udGFpbmVyKVxyXG4gICAgY29uc3QgYnRuVG9nZ2xlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnYnV0dG9uJyk7XHJcbiAgICBidG5Ub2dnbGUuaWQgPSAnYnRuLXRvZ2dsZS1wZXJmb3JtYW5jZSc7XHJcbiAgICBidG5Ub2dnbGUuaW5uZXJIVE1MID0gYFxyXG4gICAgPGltZ1xyXG4gICAgICBzcmM9XCJodHRwczovL2Nkbi1pY29ucy1wbmcuZmxhdGljb24uY29tLzUxMi81NjUvNTY1NjU1LnBuZ1wiXHJcbiAgICAgIGFsdD1cImVzY29uZGVyXCJcclxuICAgICAgc3R5bGU9XCJ3aWR0aDoxNXB4OyBoZWlnaHQ6MTVweDsgdmVydGljYWwtYWxpZ246bWlkZGxlOyBmaWx0ZXI6IGludmVydCgxKTtcIlxyXG4gICAgLz5cclxuICAgIGA7XHJcblxyXG4gICAgT2JqZWN0LmFzc2lnbihidG5Ub2dnbGUuc3R5bGUsIHtcclxuICAgICAgd2lkdGg6ICczOHB4JyxcclxuICAgICAgaGVpZ2h0OiAnMzhweCcsXHJcbiAgICAgIGJhY2tncm91bmQ6ICcjMjMyNzJmJyxcclxuICAgICAgY29sb3I6ICcjZjNmNGY2JyxcclxuICAgICAgYm9yZGVyUmFkaXVzOiAnNTAlJyxcclxuICAgICAgYm9yZGVyOiBCVVRUT05fQk9SREVSLFxyXG4gICAgICBmb250U2l6ZTogJzIwcHgnLFxyXG4gICAgICBmb250V2VpZ2h0OiAnYm9sZCcsXHJcbiAgICAgIGN1cnNvcjogJ3BvaW50ZXInLFxyXG4gICAgICBib3hTaGFkb3c6IEJVVFRPTl9CT1hfU0hBRE9XLFxyXG4gICAgICBwb3NpdGlvbjogJ2ZpeGVkJyxcclxuICAgICAgbGVmdDogJzVweCcsXHJcbiAgICAgIGJvdHRvbTogJzVweCcsXHJcbiAgICAgIGRpc3BsYXk6ICdmbGV4JyxcclxuICAgICAgYWxpZ25JdGVtczogJ2NlbnRlcicsXHJcbiAgICAgIGp1c3RpZnlDb250ZW50OiAnY2VudGVyJyxcclxuICAgICAgekluZGV4OiAnMTAwMDAnLFxyXG4gICAgfSk7XHJcbiAgICBidG5Ub2dnbGUub25tb3VzZWVudGVyID0gKCkgPT4ge1xyXG4gICAgICBidG5Ub2dnbGUuc3R5bGUuYmFja2dyb3VuZCA9ICcjMzUzYTQwJztcclxuICAgICAgYnRuVG9nZ2xlLnN0eWxlLmNvbG9yID0gJyNmZmYnO1xyXG4gICAgfTtcclxuICAgIGJ0blRvZ2dsZS5vbm1vdXNlbGVhdmUgPSAoKSA9PiB7XHJcbiAgICAgIGJ0blRvZ2dsZS5zdHlsZS5iYWNrZ3JvdW5kID0gJyMyMzI3MmYnO1xyXG4gICAgICBidG5Ub2dnbGUuc3R5bGUuY29sb3IgPSAnI2YzZjRmNic7XHJcbiAgICB9O1xyXG5cclxuICAgIGxldCBoaWRkZW4gPSBmYWxzZTtcclxuICAgIGJ0blRvZ2dsZS5vbmNsaWNrID0gKCkgPT4ge1xyXG4gICAgICBoaWRkZW4gPSAhaGlkZGVuO1xyXG4gICAgICBpZiAoaGlkZGVuKSB7XHJcbiAgICAgICAgY29udGFpbmVyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcbiAgICAgICAgYnRuVG9nZ2xlLmlubmVySFRNTCA9IGBcclxuICAgIDxpbWdcclxuICAgICAgc3JjPVwiaHR0cHM6Ly9jZG4taWNvbnMtcG5nLmZsYXRpY29uLmNvbS81MTIvMTU5LzE1OTYwNC5wbmdcIlxyXG4gICAgICBhbHQ9XCJtb3N0cmFyXCJcclxuICAgICAgc3R5bGU9XCJ3aWR0aDoxNXB4OyBoZWlnaHQ6MTVweDsgdmVydGljYWwtYWxpZ246bWlkZGxlOyBmaWx0ZXI6IGludmVydCgxKTtcIlxyXG4gICAgLz5cclxuICBgO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGNvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gJ2ZsZXgnO1xyXG4gICAgICAgIGJ0blRvZ2dsZS5pbm5lckhUTUwgPSBgXHJcbiAgICA8aW1nXHJcbiAgICAgIHNyYz1cImh0dHBzOi8vY2RuLWljb25zLXBuZy5mbGF0aWNvbi5jb20vNTEyLzU2NS81NjU2NTUucG5nXCJcclxuICAgICAgYWx0PVwiZXNjb25kZXJcIlxyXG4gICAgICBzdHlsZT1cIndpZHRoOjE1cHg7IGhlaWdodDoxNXB4OyB2ZXJ0aWNhbC1hbGlnbjptaWRkbGU7IGZpbHRlcjogaW52ZXJ0KDEpO1wiXHJcbiAgICAvPlxyXG4gIGA7XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gRXN0YWRvIGRvcyBib3TDtWVzXHJcbiAgICBsZXQgZXN0YWRvOlxyXG4gICAgICB8ICdpbmF0aXZvJ1xyXG4gICAgICB8ICdtb25pdG9yYW5kbydcclxuICAgICAgfCAncmVsYXRvcmlvJ1xyXG4gICAgICB8ICdyZWxhdG9yaW9fZ2VyYWRvJ1xyXG4gICAgICB8ICdyZXF1aXNpY2FvJyA9ICdtb25pdG9yYW5kbyc7XHJcblxyXG4gICAgY29uc3QgYXR1YWxpemFyQm90b2VzID0gKG5vdm9Fc3RhZG86IHR5cGVvZiBlc3RhZG8pID0+IHtcclxuICAgICAgZXN0YWRvID0gbm92b0VzdGFkbztcclxuICAgICAgc3dpdGNoIChlc3RhZG8pIHtcclxuICAgICAgICBjYXNlICdpbmF0aXZvJzpcclxuICAgICAgICAgIGJ0bk1haW4uaW5uZXJUZXh0ID0gJ0luaWNpYXIgbW9uaXRvcmFtZW50byc7XHJcbiAgICAgICAgICB0aGlzLm1vbml0b3JhbWVudG9BdGl2byA9IGZhbHNlO1xyXG4gICAgICAgICAgYnRuRmluYWxpemFyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcbiAgICAgICAgICBidG5SZXF1aXNpY29lc0RldGFsaGFkYXMuc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuICAgICAgICAgIGJ0bkFuYWxpc2VEZXRhbGhhZGEuc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuICAgICAgICAgIGJ0blJlbGF0b3Jpb0V2ZW50by5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAnbW9uaXRvcmFuZG8nOlxyXG4gICAgICAgICAgYnRuTWFpbi5pbm5lclRleHQgPSAnUGFyYXIgbW9uaXRvcmFtZW50byc7XHJcbiAgICAgICAgICB0aGlzLm1vbml0b3JhbWVudG9BdGl2byA9IHRydWU7XHJcbiAgICAgICAgICBidG5GaW5hbGl6YXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuICAgICAgICAgIGJ0blJlcXVpc2ljb2VzRGV0YWxoYWRhcy5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG4gICAgICAgICAgYnRuQW5hbGlzZURldGFsaGFkYS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG4gICAgICAgICAgYnRuUmVsYXRvcmlvRXZlbnRvLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcbiAgICAgICAgICBicmVhaztcclxuICAgICAgICBjYXNlICdyZWxhdG9yaW8nOlxyXG4gICAgICAgICAgYnRuTWFpbi5pbm5lclRleHQgPSAnR2VyYXIgcmVsYXTDs3Jpbyc7XHJcbiAgICAgICAgICB0aGlzLm1vbml0b3JhbWVudG9BdGl2byA9IGZhbHNlO1xyXG4gICAgICAgICAgYnRuRmluYWxpemFyLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7XHJcbiAgICAgICAgICBidG5SZXF1aXNpY29lc0RldGFsaGFkYXMuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XHJcbiAgICAgICAgICBidG5BbmFsaXNlRGV0YWxoYWRhLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xyXG4gICAgICAgICAgYnRuUmVsYXRvcmlvRXZlbnRvLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgY2FzZSAncmVsYXRvcmlvX2dlcmFkbyc6XHJcbiAgICAgICAgICBidG5NYWluLmlubmVyVGV4dCA9ICdHZXJhciByZWxhdMOzcmlvJztcclxuICAgICAgICAgIHRoaXMubW9uaXRvcmFtZW50b0F0aXZvID0gZmFsc2U7XHJcbiAgICAgICAgICBidG5GaW5hbGl6YXIuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XHJcbiAgICAgICAgICBidG5SZXF1aXNpY29lc0RldGFsaGFkYXMuc3R5bGUuZGlzcGxheSA9ICdibG9jayc7XHJcbiAgICAgICAgICBidG5BbmFsaXNlRGV0YWxoYWRhLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xyXG4gICAgICAgICAgYnRuUmVsYXRvcmlvRXZlbnRvLnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xyXG4gICAgICAgICAgYnJlYWs7XHJcbiAgICAgIH1cclxuICAgIH07XHJcblxyXG4gICAgLy8gQcOnw7VlcyBkb3MgYm90w7Vlc1xyXG4gICAgYnRuTWFpbi5vbmNsaWNrID0gKCkgPT4ge1xyXG4gICAgICBpZiAoZXN0YWRvID09PSAnaW5hdGl2bycpIHtcclxuICAgICAgICB0aGlzLnJlc2V0UGVyZm9ybWFuY2VEYXRhKCk7XHJcbiAgICAgICAgdGhpcy5zaG93V2VsY29tZU1lc3NhZ2UoKTtcclxuICAgICAgICBhdHVhbGl6YXJCb3RvZXMoJ21vbml0b3JhbmRvJyk7XHJcbiAgICAgIH0gZWxzZSBpZiAoZXN0YWRvID09PSAnbW9uaXRvcmFuZG8nKSB7XHJcbiAgICAgICAgYXR1YWxpemFyQm90b2VzKCdyZWxhdG9yaW8nKTtcclxuICAgICAgfSBlbHNlIGlmIChlc3RhZG8gPT09ICdyZWxhdG9yaW8nIHx8IGVzdGFkbyA9PT0gJ3JlbGF0b3Jpb19nZXJhZG8nKSB7XHJcbiAgICAgICAgdGhpcy5zaG93UGVyZm9ybWFuY2VTdW1tYXJ5KGZhbHNlKTtcclxuICAgICAgICBhdHVhbGl6YXJCb3RvZXMoJ3JlbGF0b3Jpb19nZXJhZG8nKTtcclxuICAgICAgfVxyXG4gICAgfTtcclxuXHJcbiAgICBidG5GaW5hbGl6YXIub25jbGljayA9ICgpID0+IHtcclxuICAgICAgdGhpcy5yZXNldFBlcmZvcm1hbmNlRGF0YSgpO1xyXG4gICAgICBhdHVhbGl6YXJCb3RvZXMoJ2luYXRpdm8nKTtcclxuICAgIH07XHJcblxyXG4gICAgLy8gQXDDqm5kaWNlXHJcbiAgICB0aGlzLnNob3dXZWxjb21lTWVzc2FnZSgpO1xyXG4gICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGJ0bk1haW4pO1xyXG4gICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGJ0blJlcXVpc2ljb2VzRGV0YWxoYWRhcyk7XHJcbiAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoYnRuUmVsYXRvcmlvRXZlbnRvKTtcclxuICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChidG5BbmFsaXNlRGV0YWxoYWRhKTtcclxuICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChidG5GaW5hbGl6YXIpO1xyXG4gICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChjb250YWluZXIpO1xyXG4gICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChidG5Ub2dnbGUpO1xyXG5cclxuICAgIC8vIEHDp8OjbyBkbyBib3TDo28gZGUgcmVxdWlzacOnw7VlcyBkZXRhbGhhZGFzXHJcbiAgICBidG5SZXF1aXNpY29lc0RldGFsaGFkYXMub25jbGljayA9ICgpID0+IHtcclxuICAgICAgdGhpcy5zaG93SHR0cFJlcXVlc3RzUmVwb3J0KCk7XHJcbiAgICB9O1xyXG4gICAgLy8gQcOnw6NvIGRvIGJvdMOjbyBkZSBhbsOhbGlzZSBkZXRhbGhhZGFcclxuICAgIGJ0bkFuYWxpc2VEZXRhbGhhZGEub25jbGljayA9ICgpID0+IHtcclxuICAgICAgdGhpcy5nZW5lcmF0ZURldGFpbGVkQW5hbHlzaXMoKTtcclxuICAgIH07XHJcbiAgICAvLyBBw6fDo28gZG8gcmVsYXTDs3JpbyBwb3IgZXZlbnRvXHJcbiAgICBidG5SZWxhdG9yaW9FdmVudG8ub25jbGljayA9ICgpID0+IHtcclxuICAgICAgdGhpcy5zaG93RXZlbnRSZXBvcnQodHJ1ZSk7XHJcbiAgICB9O1xyXG5cclxuICAgIC8vID09PT09PSBDYXB0dXJhIGF1dG9tw6F0aWNhIGRlIGV2ZW50b3MgZGUgVUkgcGFyYSBzZXBhcmFyIHBvciBldmVudG8gPT09PT09XHJcbiAgICB0cnkge1xyXG4gICAgICAvLyBFdml0YSBhZGljaW9uYXIgbcO6bHRpcGxvcyBsaXN0ZW5lcnNcclxuICAgICAgY29uc3QgYW55V2luZG93ID0gd2luZG93IGFzIGFueTtcclxuICAgICAgaWYgKCFhbnlXaW5kb3cuX19wbV9hdXRvX3VpX2xpc3RlbmVyc19hZGRlZCkge1xyXG4gICAgICAgIGFueVdpbmRvdy5fX3BtX2F1dG9fdWlfbGlzdGVuZXJzX2FkZGVkID0gdHJ1ZTtcclxuXHJcbiAgICAgICAgY29uc3QgZGVzY3JpYmVUYXJnZXQgPSAoZWw6IGFueSwgZXY/OiBFdmVudCkgPT4ge1xyXG4gICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgY29uc3QgaXNLZXlib2FyZCA9XHJcbiAgICAgICAgICAgICAgdHlwZW9mIEtleWJvYXJkRXZlbnQgIT09ICd1bmRlZmluZWQnICYmXHJcbiAgICAgICAgICAgICAgZXYgaW5zdGFuY2VvZiBLZXlib2FyZEV2ZW50O1xyXG4gICAgICAgICAgICBjb25zdCBrZXlQYXJ0ID1cclxuICAgICAgICAgICAgICBpc0tleWJvYXJkICYmIChldiBhcyBLZXlib2FyZEV2ZW50KS5rZXlcclxuICAgICAgICAgICAgICAgID8gYFskeyhldiBhcyBLZXlib2FyZEV2ZW50KS5rZXl9XWBcclxuICAgICAgICAgICAgICAgIDogJyc7XHJcbiAgICAgICAgICAgIGlmICghZWwgfHwgdHlwZW9mIGVsICE9PSAnb2JqZWN0JykgcmV0dXJuIGB1aSR7a2V5UGFydH1gO1xyXG4gICAgICAgICAgICBjb25zdCB0YWcgPVxyXG4gICAgICAgICAgICAgIChlbC50YWdOYW1lIHx8IGVsLm5vZGVOYW1lIHx8ICcnKS50b1N0cmluZygpLnRvTG93ZXJDYXNlKCkgfHxcclxuICAgICAgICAgICAgICAnZWxlbWVudCc7XHJcbiAgICAgICAgICAgIGNvbnN0IGlkID0gZWwuaWQgPyBgIyR7ZWwuaWR9YCA6ICcnO1xyXG4gICAgICAgICAgICBjb25zdCByYXdDbGFzcyA9XHJcbiAgICAgICAgICAgICAgdHlwZW9mIGVsLmNsYXNzTmFtZSA9PT0gJ3N0cmluZydcclxuICAgICAgICAgICAgICAgID8gZWwuY2xhc3NOYW1lXHJcbiAgICAgICAgICAgICAgICA6IGVsLmNsYXNzTGlzdCAmJlxyXG4gICAgICAgICAgICAgICAgICB0eXBlb2YgZWwuY2xhc3NMaXN0ID09PSAnb2JqZWN0JyAmJlxyXG4gICAgICAgICAgICAgICAgICB0eXBlb2YgZWwuY2xhc3NMaXN0LnRvU3RyaW5nID09PSAnZnVuY3Rpb24nXHJcbiAgICAgICAgICAgICAgICA/IGVsLmNsYXNzTGlzdC50b1N0cmluZygpXHJcbiAgICAgICAgICAgICAgICA6ICcnO1xyXG4gICAgICAgICAgICBjb25zdCBmaWx0ZXJlZCA9IHJhd0NsYXNzXHJcbiAgICAgICAgICAgICAgLnNwbGl0KCcgJylcclxuICAgICAgICAgICAgICAuZmlsdGVyKFxyXG4gICAgICAgICAgICAgICAgKGM6IHN0cmluZykgPT5cclxuICAgICAgICAgICAgICAgICAgYyAmJiAhYy5zdGFydHNXaXRoKCduZy0nKSAmJiAhYy5zdGFydHNXaXRoKCdjZGstJylcclxuICAgICAgICAgICAgICApXHJcbiAgICAgICAgICAgICAgLnNsaWNlKDAsIDIpXHJcbiAgICAgICAgICAgICAgLmpvaW4oJy4nKTtcclxuICAgICAgICAgICAgY29uc3QgY2xzID0gZmlsdGVyZWQgPyBgLiR7ZmlsdGVyZWR9YCA6ICcnO1xyXG4gICAgICAgICAgICBjb25zdCBpbmNsdWRlVGV4dCA9IHRhZyA9PT0gJ2J1dHRvbicgfHwgdGFnID09PSAnYSc7XHJcbiAgICAgICAgICAgIGNvbnN0IHR4dCA9XHJcbiAgICAgICAgICAgICAgaW5jbHVkZVRleHQgJiYgdHlwZW9mIGVsLmlubmVyVGV4dCA9PT0gJ3N0cmluZydcclxuICAgICAgICAgICAgICAgID8gZWwuaW5uZXJUZXh0LnRyaW0oKS5zbGljZSgwLCAzMClcclxuICAgICAgICAgICAgICAgIDogJyc7XHJcbiAgICAgICAgICAgIHJldHVybiBgJHt0YWd9JHtpZH0ke2Nsc30ke2tleVBhcnR9JHt0eHQgPyBgOlwiJHt0eHR9XCJgIDogJyd9YDtcclxuICAgICAgICAgIH0gY2F0Y2gge1xyXG4gICAgICAgICAgICByZXR1cm4gJ3VpJztcclxuICAgICAgICAgIH1cclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBsZXQgdWlFdmVudEVuZFRpbWVyOiBhbnkgPSBudWxsO1xyXG4gICAgICAgIGxldCB1aUV2ZW50RW5kSW50ZXJ2YWw6IGFueSA9IG51bGw7XHJcbiAgICAgICAgY29uc3Qgc3RhcnRVaUV2ZW50Q29udGV4dCA9IChsYWJlbDogc3RyaW5nKSA9PiB7XHJcbiAgICAgICAgICBpZiAodWlFdmVudEVuZFRpbWVyKSB7XHJcbiAgICAgICAgICAgIGNsZWFyVGltZW91dCh1aUV2ZW50RW5kVGltZXIpO1xyXG4gICAgICAgICAgICB1aUV2ZW50RW5kVGltZXIgPSBudWxsO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgaWYgKHVpRXZlbnRFbmRJbnRlcnZhbCkge1xyXG4gICAgICAgICAgICBjbGVhckludGVydmFsKHVpRXZlbnRFbmRJbnRlcnZhbCk7XHJcbiAgICAgICAgICAgIHVpRXZlbnRFbmRJbnRlcnZhbCA9IG51bGw7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgICB0aGlzLmJlZ2luRXZlbnRDb250ZXh0KGxhYmVsKTtcclxuICAgICAgICAgIC8vIFNlIGrDoSBow6EgdW0gY29udGV4dG8gYWJlcnRvLCBuw6NvIGNyaWEgbm92YSBqYW5lbGEgZGUgZW5jZXJyYW1lbnRvIChldmVudG8gYW5pbmhhZG8pXHJcbiAgICAgICAgICBpZiAodGhpcy5ldmVudENvbnRleHRTdGFjay5sZW5ndGggPiAxKSB7XHJcbiAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAvLyBKYW5lbGEgaW50ZWxpZ2VudGU6IGVzcGVyYSBwcmltZWlybyB0aW1lciBlIGZlY2hhIGFww7NzIHBlcsOtb2RvIGRlIHNpbMOqbmNpb1xyXG4gICAgICAgICAgY29uc3QgTUFYX1dJTkRPV19NUyA9IDYwMDAwO1xyXG4gICAgICAgICAgY29uc3QgR1JBQ0VfTVMgPSAyMDAwOyAvLyB0ZW1wbyBwYXJhIHBlcm1pdGlyIHRpbWVycyBxdWUgY29tZcOnYW0gZGVwb2lzXHJcbiAgICAgICAgICBjb25zdCBRVUlFVF9NUyA9IDYwMDsgLy8gdGVtcG8gc2VtIGF0aXZpZGFkZSBwYXJhIGNvbnNpZGVyYXIgZmx1eG8gZW5jZXJyYWRvXHJcblxyXG4gICAgICAgICAgdWlFdmVudEVuZEludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xyXG4gICAgICAgICAgICBjb25zdCBhY3RpdmUgPSB0aGlzLmhhc0FjdGl2ZVRpbWVycygpO1xyXG4gICAgICAgICAgICBjb25zdCBiZWdpbkF0ID0gdGhpcy5jdXJyZW50RXZlbnRCZWdpbkF0ID8/IG5vdztcclxuICAgICAgICAgICAgY29uc3QgaGFzU3RhcnRlZCA9IHRoaXMuZmlyc3RUaW1lckluRXZlbnRBdCAhPT0gbnVsbDtcclxuICAgICAgICAgICAgY29uc3QgZ3JhY2VFeGNlZWRlZCA9IG5vdyAtIGJlZ2luQXQgPiBHUkFDRV9NUztcclxuICAgICAgICAgICAgY29uc3QgbGFzdEFjdGl2aXR5ID0gdGhpcy5sYXN0VGltZXJBY3Rpdml0eUF0ID8/IGJlZ2luQXQ7XHJcbiAgICAgICAgICAgIGNvbnN0IHF1aWV0RW5vdWdoID1cclxuICAgICAgICAgICAgICBoYXNTdGFydGVkICYmICFhY3RpdmUgJiYgbm93IC0gbGFzdEFjdGl2aXR5ID4gUVVJRVRfTVM7XHJcbiAgICAgICAgICAgIGNvbnN0IGV4Y2VlZGVkTWF4ID0gbm93IC0gYmVnaW5BdCA+IE1BWF9XSU5ET1dfTVM7XHJcblxyXG4gICAgICAgICAgICBpZiAocXVpZXRFbm91Z2ggfHwgKCFoYXNTdGFydGVkICYmIGdyYWNlRXhjZWVkZWQpIHx8IGV4Y2VlZGVkTWF4KSB7XHJcbiAgICAgICAgICAgICAgdGhpcy5lbmRFdmVudENvbnRleHQoKTtcclxuICAgICAgICAgICAgICBjbGVhckludGVydmFsKHVpRXZlbnRFbmRJbnRlcnZhbCk7XHJcbiAgICAgICAgICAgICAgdWlFdmVudEVuZEludGVydmFsID0gbnVsbDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSwgMjUwKTtcclxuXHJcbiAgICAgICAgICAvLyBGYWxsYmFjazogZmVjaGEgbm8gbcOheGltbyBhcMOzcyBNQVhfV0lORE9XX01TXHJcbiAgICAgICAgICB1aUV2ZW50RW5kVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcclxuICAgICAgICAgICAgaWYgKHVpRXZlbnRFbmRJbnRlcnZhbCkge1xyXG4gICAgICAgICAgICAgIGNsZWFySW50ZXJ2YWwodWlFdmVudEVuZEludGVydmFsKTtcclxuICAgICAgICAgICAgICB1aUV2ZW50RW5kSW50ZXJ2YWwgPSBudWxsO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHRoaXMuZW5kRXZlbnRDb250ZXh0KCk7XHJcbiAgICAgICAgICAgIHVpRXZlbnRFbmRUaW1lciA9IG51bGw7XHJcbiAgICAgICAgICB9LCBNQVhfV0lORE9XX01TKTtcclxuICAgICAgICB9O1xyXG5cclxuICAgICAgICBbJ2NsaWNrJywgJ3N1Ym1pdCcsICdjaGFuZ2UnLCAna2V5ZG93biddLmZvckVhY2goKHR5cGUpID0+IHtcclxuICAgICAgICAgIGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoXHJcbiAgICAgICAgICAgIHR5cGUsXHJcbiAgICAgICAgICAgIChldjogRXZlbnQpID0+IHtcclxuICAgICAgICAgICAgICBjb25zdCB0YXJnZXQgPSBldi50YXJnZXQgYXMgYW55O1xyXG4gICAgICAgICAgICAgIGNvbnN0IGxhYmVsID0gYFVzZXJJbnB1dDoke3R5cGV9ICR7ZGVzY3JpYmVUYXJnZXQodGFyZ2V0LCBldil9YDtcclxuICAgICAgICAgICAgICBzdGFydFVpRXZlbnRDb250ZXh0KGxhYmVsKTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgdHJ1ZSAvLyBjYXB0dXJhIGNlZG8gbmEgZmFzZSBkZSBjYXB0dXJhXHJcbiAgICAgICAgICApO1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICAvLyBDb250ZXh0byBpbmljaWFsIHBhcmEgb3BlcmHDp8O1ZXMgZGUgaW5pY2lhbGl6YcOnw6NvIGRhIHRlbGFcclxuICAgICAgICB0aGlzLmJlZ2luRXZlbnRDb250ZXh0KCdBcHBJbml0Jyk7XHJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmVuZEV2ZW50Q29udGV4dCgpLCAzMDAwKTtcclxuICAgICAgfVxyXG4gICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAvLyBFdml0YSBxdWVicmFyIGNhc28gYW1iaWVudGUgbsOjbyBwZXJtaXRhIGFjZXNzbyBhbyBkb2N1bWVudFxyXG4gICAgICBjb25zb2xlLndhcm4oXHJcbiAgICAgICAgJ1tQZXJmb3JtYW5jZU1vbml0b3JdIEZhbGhhIGFvIGF0aXZhciBjYXB0dXJhIGF1dG9tw6F0aWNhIGRlIFVJOicsXHJcbiAgICAgICAgZVxyXG4gICAgICApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcHVibGljIHNob3dFdmVudFJlcG9ydChpbmNsdWRlSHR0cDogYm9vbGVhbiA9IHRydWUpOiB2b2lkIHtcclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAnJWNSRUxBVMOTUklPIFBPUiBFVkVOVE8nLFxyXG4gICAgICAnY29sb3I6ICNiYmM3YzhmZjsgZm9udC1zaXplOiAxOHB4OyBmb250LXdlaWdodDogYm9sZDsgdGV4dC1zaGFkb3c6IDFweCAxcHggIzAwMDsgYmFja2dyb3VuZC1jb2xvcjogIzQzMjQ4YmZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDEwcHggMjBweDsnXHJcbiAgICApO1xyXG5cclxuICAgIGlmICh0aGlzLnBlcmZvcm1hbmNlU3VtbWFyeS5zaXplID09PSAwKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKCdOZW5odW1hIG9wZXJhw6fDo28gbW9uaXRvcmFkYSBhaW5kYS4nKTtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IGV2ZW50cyA9IG5ldyBNYXA8XHJcbiAgICAgIHN0cmluZyxcclxuICAgICAge1xyXG4gICAgICAgIHRvdGFsVGltZTogbnVtYmVyO1xyXG4gICAgICAgIHRvdGFsRXhlYzogbnVtYmVyO1xyXG4gICAgICAgIG9wZXJhdGlvbnM6IEFycmF5PHsgbmFtZTogc3RyaW5nOyBzdGF0czogUGVyZm9ybWFuY2VTdGF0cyB9PjtcclxuICAgICAgfVxyXG4gICAgPigpO1xyXG5cclxuICAgIHRoaXMucGVyZm9ybWFuY2VTdW1tYXJ5LmZvckVhY2goKHN0YXRzLCBmdWxsTmFtZSkgPT4ge1xyXG4gICAgICBjb25zdCBwYXJ0cyA9IGZ1bGxOYW1lLnNwbGl0KCcgfCAnKTtcclxuICAgICAgY29uc3QgZXZlbnQgPSBwYXJ0cy5sZW5ndGggPiAxID8gcGFydHNbMF0gOiAnU2VtRXZlbnRvJztcclxuICAgICAgY29uc3Qgb3BOYW1lID0gcGFydHMubGVuZ3RoID4gMSA/IHBhcnRzLnNsaWNlKDEpLmpvaW4oJyB8ICcpIDogZnVsbE5hbWU7XHJcblxyXG4gICAgICBpZiAoIWV2ZW50cy5oYXMoZXZlbnQpKSB7XHJcbiAgICAgICAgZXZlbnRzLnNldChldmVudCwgeyB0b3RhbFRpbWU6IDAsIHRvdGFsRXhlYzogMCwgb3BlcmF0aW9uczogW10gfSk7XHJcbiAgICAgIH1cclxuICAgICAgY29uc3QgZ3JvdXAgPSBldmVudHMuZ2V0KGV2ZW50KSE7XHJcbiAgICAgIGdyb3VwLnRvdGFsVGltZSArPSBzdGF0cy50b3RhbFRpbWU7XHJcbiAgICAgIGdyb3VwLnRvdGFsRXhlYyArPSBzdGF0cy5jb3VudDtcclxuICAgICAgZ3JvdXAub3BlcmF0aW9ucy5wdXNoKHsgbmFtZTogb3BOYW1lLCBzdGF0cyB9KTtcclxuICAgIH0pO1xyXG5cclxuICAgIGxldCBvdmVyYWxsVGltZSA9IDA7XHJcbiAgICBsZXQgb3ZlcmFsbEV4ZWMgPSAwO1xyXG5cclxuICAgIEFycmF5LmZyb20oZXZlbnRzLmVudHJpZXMoKSlcclxuICAgICAgLnNvcnQoKGEsIGIpID0+IGJbMV0udG90YWxUaW1lIC0gYVsxXS50b3RhbFRpbWUpXHJcbiAgICAgIC5mb3JFYWNoKChbZXZlbnQsIGdyb3VwXSkgPT4ge1xyXG4gICAgICAgIG92ZXJhbGxUaW1lICs9IGdyb3VwLnRvdGFsVGltZTtcclxuICAgICAgICBvdmVyYWxsRXhlYyArPSBncm91cC50b3RhbEV4ZWM7XHJcblxyXG4gICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgYCVjRVZFTlRPOiAke2V2ZW50fSAgfCAgVGVtcG86ICR7TWF0aC5yb3VuZChcclxuICAgICAgICAgICAgZ3JvdXAudG90YWxUaW1lXHJcbiAgICAgICAgICApfW1zICB8ICBFeGVjdcOnw7VlczogJHtncm91cC50b3RhbEV4ZWN9YCxcclxuICAgICAgICAgICdjb2xvcjogI2ZmZjsgYmFja2dyb3VuZDogIzI4NjQ3YjsgZm9udC1zaXplOiAxM3B4OyBmb250LXdlaWdodDogYm9sZDsgYm9yZGVyLXJhZGl1czogNHB4OyBwYWRkaW5nOiAzcHggOHB4OydcclxuICAgICAgICApO1xyXG5cclxuICAgICAgICBjb25zdCB0YWJsZSA9IE9iamVjdC5mcm9tRW50cmllcyhcclxuICAgICAgICAgIGdyb3VwLm9wZXJhdGlvbnNcclxuICAgICAgICAgICAgLnNvcnQoKGEsIGIpID0+IGIuc3RhdHMudG90YWxUaW1lIC0gYS5zdGF0cy50b3RhbFRpbWUpXHJcbiAgICAgICAgICAgIC5tYXAoKHsgbmFtZSwgc3RhdHMgfSkgPT4ge1xyXG4gICAgICAgICAgICAgIGNvbnN0IGF2Z1RpbWUgPSBNYXRoLnJvdW5kKHN0YXRzLnRvdGFsVGltZSAvIHN0YXRzLmNvdW50KTtcclxuICAgICAgICAgICAgICBjb25zdCB7IGVtb2ppLCBzdGF0dXMgfSA9IHRoaXMuZ2V0UGVyZm9ybWFuY2VTdGF0dXMoXHJcbiAgICAgICAgICAgICAgICBhdmdUaW1lLFxyXG4gICAgICAgICAgICAgICAgc3RhdHMuY3VzdG9tVGhyZXNob2xkcyxcclxuICAgICAgICAgICAgICAgIG5hbWVcclxuICAgICAgICAgICAgICApO1xyXG4gICAgICAgICAgICAgIHJldHVybiBbXHJcbiAgICAgICAgICAgICAgICBuYW1lLFxyXG4gICAgICAgICAgICAgICAge1xyXG4gICAgICAgICAgICAgICAgICBFeGVjdcOnw7Vlczogc3RhdHMuY291bnQsXHJcbiAgICAgICAgICAgICAgICAgICdNw6lkaWEobXMpJzogYCR7YXZnVGltZX1tcyAoJHtzdGF0dXN9KSAke2Vtb2ppfWAudHJpbSgpLFxyXG4gICAgICAgICAgICAgICAgICAnTcOtbmltbyhtcyknOiBzdGF0cy5taW5UaW1lLFxyXG4gICAgICAgICAgICAgICAgICAnTcOheGltbyhtcyknOiBzdGF0cy5tYXhUaW1lLFxyXG4gICAgICAgICAgICAgICAgICAnVG90YWwobXMpJzogTWF0aC5yb3VuZChzdGF0cy50b3RhbFRpbWUpLFxyXG4gICAgICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgICBdO1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgICk7XHJcblxyXG4gICAgICAgIGNvbnNvbGUudGFibGUodGFibGUpO1xyXG5cclxuICAgICAgICBpZiAoaW5jbHVkZUh0dHApIHtcclxuICAgICAgICAgIGNvbnN0IGh0dHBUYWJsZTogUmVjb3JkPHN0cmluZywgYW55PiA9IHt9O1xyXG4gICAgICAgICAgdGhpcy5odHRwUmVxdWVzdHMuZm9yRWFjaCgoZGFkb3MsIGtleSkgPT4ge1xyXG4gICAgICAgICAgICBjb25zdCBwYXJ0cyA9IGtleS5zcGxpdCgnIHwgJyk7XHJcbiAgICAgICAgICAgIGNvbnN0IGh0dHBFdmVudCA9IHBhcnRzLmxlbmd0aCA+IDEgPyBwYXJ0c1swXSA6ICdTZW1FdmVudG8nO1xyXG4gICAgICAgICAgICBjb25zdCBodHRwTmFtZSA9XHJcbiAgICAgICAgICAgICAgcGFydHMubGVuZ3RoID4gMSA/IHBhcnRzLnNsaWNlKDEpLmpvaW4oJyB8ICcpIDoga2V5O1xyXG4gICAgICAgICAgICBpZiAoaHR0cEV2ZW50ID09PSBldmVudCkge1xyXG4gICAgICAgICAgICAgIGh0dHBUYWJsZVtodHRwTmFtZV0gPSB7XHJcbiAgICAgICAgICAgICAgICBFeGVjdcOnw7VlczogZGFkb3MuZXhlY3Vjb2VzLFxyXG4gICAgICAgICAgICAgICAgTcOpZGlhOiBgJHtNYXRoLnJvdW5kKFxyXG4gICAgICAgICAgICAgICAgICBkYWRvcy50ZW1wb1RvdGFsIC8gTWF0aC5tYXgoZGFkb3MuZXhlY3Vjb2VzLCAxKVxyXG4gICAgICAgICAgICAgICAgKX1tc2AsXHJcbiAgICAgICAgICAgICAgICAnVGVtcG8gVG90YWwnOiBgJHtNYXRoLnJvdW5kKGRhZG9zLnRlbXBvVG90YWwpfW1zYCxcclxuICAgICAgICAgICAgICAgIEVycm9zOiBkYWRvcy5lcnJvcnMsXHJcbiAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgY29uc3QgaGFzSHR0cFJvd3MgPSBPYmplY3Qua2V5cyhodHRwVGFibGUpLmxlbmd0aCA+IDA7XHJcbiAgICAgICAgICBpZiAoaGFzSHR0cFJvd3MpIHtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICAgICAgYCVjSFRUUCAoJHtldmVudH0pYCxcclxuICAgICAgICAgICAgICAnY29sb3I6ICNmZmY7IGJhY2tncm91bmQ6ICMyODMzNmI7IGZvbnQtc2l6ZTogMTJweDsgZm9udC13ZWlnaHQ6IGJvbGQ7IGJvcmRlci1yYWRpdXM6IDRweDsgcGFkZGluZzogMnB4IDhweDsnXHJcbiAgICAgICAgICAgICk7XHJcbiAgICAgICAgICAgIGNvbnNvbGUudGFibGUoaHR0cFRhYmxlKTtcclxuICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICAgIH0pO1xyXG5cclxuICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICBgJWNUT1RBTCBHRVJBTDogJHtNYXRoLnJvdW5kKFxyXG4gICAgICAgIG92ZXJhbGxUaW1lXHJcbiAgICAgICl9bXMgIHwgIEV4ZWN1w6fDtWVzOiAke292ZXJhbGxFeGVjfWAsXHJcbiAgICAgICdjb2xvcjogI2ZmZjsgYmFja2dyb3VuZDogIzQzMjQ4YmZmOyBmb250LXNpemU6IDE0cHg7IGZvbnQtd2VpZ2h0OiBib2xkOyBib3JkZXItcmFkaXVzOiA0cHg7IHBhZGRpbmc6IDRweCAxMnB4OyBtYXJnaW4tdG9wOiA4cHg7J1xyXG4gICAgKTtcclxuICB9XHJcbn1cclxuXHJcbi8qKlxyXG4gKiDwn4+tIEZhY3RvcnkgZnVuY3Rpb24gcGFyYSBjcmlhciBpbnN0w6JuY2lhIGRvIExvZ3NQZXJmb3JtYXRpY29cclxuICogQHBhcmFtIHNlcnZpY2VOYW1lIE5vbWUgZG8gc2VydmnDp28gKHBhcmEgbG9ncyBwZXJzb25hbGl6YWRvcylcclxuICogQHJldHVybnMgTm92YSBpbnN0w6JuY2lhIGRlIExvZ3NQZXJmb3JtYXRpY29cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVQZXJmb3JtYW5jZUxvZ2dlcihcclxuICBzZXJ2aWNlTmFtZTogc3RyaW5nID0gJ1NlcnZpY2UnXHJcbik6IExvZ3NQZXJmb3JtYXRpY28ge1xyXG4gIHJldHVybiBuZXcgTG9nc1BlcmZvcm1hdGljbyhzZXJ2aWNlTmFtZSk7XHJcbn1cclxuIl19