UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

376 lines 50.6 kB
/** * Comprehensive Performance Monitoring System * Tracks search times, memory usage, cache performance, and system metrics */ import { logger } from './logger.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; import { freemem, totalmem, loadavg } from 'node:os'; export class PerformanceMonitor { searchMetrics = []; slowQueries = []; memorySnapshots = []; cacheMetrics = new Map(); // Configuration maxMetricsHistory = 1000; slowQueryThreshold = 100; // ms memorySnapshotInterval = 30000; // 30 seconds maxSlowQueries = 100; // Timers and intervals memoryMonitorInterval; isMonitoring = false; logListener; addLogListener(fn) { this.logListener = fn; return () => { this.logListener = undefined; }; } constructor() { } /** * Start performance monitoring */ startMonitoring() { if (this.isMonitoring) { return; } this.isMonitoring = true; this.startMemoryMonitoring(); logger.info('Performance monitoring started', { slowQueryThreshold: this.slowQueryThreshold, maxMetricsHistory: this.maxMetricsHistory }); } /** * Stop performance monitoring */ stopMonitoring() { this.isMonitoring = false; if (this.memoryMonitorInterval) { clearInterval(this.memoryMonitorInterval); this.memoryMonitorInterval = undefined; } logger.info('Performance monitoring stopped'); } /** * Dispose monitoring state and timers. */ dispose() { this.stopMonitoring(); this.reset(); } /** * Record search performance metrics */ recordSearch(metrics) { if (!this.isMonitoring) { return; } // Normalize query string to prevent Unicode-based attacks const validationResult = UnicodeValidator.normalize(metrics.query); const normalizedMetrics = { ...metrics, query: validationResult.normalizedContent }; this.searchMetrics.push(normalizedMetrics); this.logListener?.('debug', 'Record search', { query: normalizedMetrics.query.substring(0, 50), duration: normalizedMetrics.duration, resultCount: normalizedMetrics.resultCount, cacheHit: normalizedMetrics.cacheHit, }); // Check if it's a slow query (use normalized metrics) if (normalizedMetrics.duration > this.slowQueryThreshold) { this.recordSlowQuery({ query: normalizedMetrics.query, duration: normalizedMetrics.duration, threshold: this.slowQueryThreshold, sources: normalizedMetrics.sources, resultCount: normalizedMetrics.resultCount, memoryUsage: normalizedMetrics.memoryAfter, timestamp: normalizedMetrics.timestamp }); } // Trim history if needed if (this.searchMetrics.length > this.maxMetricsHistory) { this.searchMetrics = this.searchMetrics.slice(-this.maxMetricsHistory); } // Notify listener about slow queries if (normalizedMetrics.duration > this.slowQueryThreshold) { this.logListener?.('warn', 'Detect slow query', { query: normalizedMetrics.query.substring(0, 50), duration: normalizedMetrics.duration, threshold: this.slowQueryThreshold, }); } // Log significant performance events (use normalized metrics) if (normalizedMetrics.duration > this.slowQueryThreshold * 2) { logger.warn('Very slow search detected', { query: normalizedMetrics.query.substring(0, 50), duration: normalizedMetrics.duration, resultCount: normalizedMetrics.resultCount, sources: normalizedMetrics.sources }); } } /** * Record cache performance metrics */ recordCachePerformance(cacheName, stats) { if (!this.isMonitoring) { return; } // Normalize cache name to prevent Unicode-based attacks const validationResult = UnicodeValidator.normalize(cacheName); const normalizedCacheName = validationResult.normalizedContent; this.cacheMetrics.set(normalizedCacheName, stats); // Notify listener about low cache hit rate if (stats.hitRate < 0.5) { this.logListener?.('warn', 'Detect low cache hit rate', { cache: normalizedCacheName, hitRate: stats.hitRate, totalOperations: stats.totalHits + stats.totalMisses, }); } // Log cache performance warnings (use normalized cache name) if (stats.hitRate < 0.5) { logger.warn('Low cache hit rate detected', { cache: normalizedCacheName, hitRate: stats.hitRate, totalOperations: stats.totalHits + stats.totalMisses }); } } /** * Get comprehensive performance metrics */ getMetrics() { return { searchTimes: this.searchMetrics.map(m => m.duration), memoryUsage: this.memorySnapshots.slice(-100), // Last 100 snapshots cacheStats: this.aggregateCacheStats(), systemStats: this.getSystemStats(), timestamp: new Date() }; } /** * Get search performance statistics */ getSearchStats() { if (this.searchMetrics.length === 0) { return { totalSearches: 0, averageTime: 0, medianTime: 0, p95Time: 0, p99Time: 0, slowQueries: 0, cacheHitRate: 0 }; } const times = this.searchMetrics.map(m => m.duration).sort((a, b) => a - b); const cacheHits = this.searchMetrics.filter(m => m.cacheHit).length; return { totalSearches: this.searchMetrics.length, averageTime: times.reduce((sum, time) => sum + time, 0) / times.length, medianTime: times[Math.floor(times.length / 2)], p95Time: times[Math.floor(times.length * 0.95)], p99Time: times[Math.floor(times.length * 0.99)], slowQueries: this.slowQueries.length, cacheHitRate: cacheHits / this.searchMetrics.length }; } /** * Get memory usage statistics */ getMemoryStats() { if (this.memorySnapshots.length === 0) { const current = this.takeMemorySnapshot(); return { currentUsage: current, peakUsage: current, averageUsage: current, growthRate: 0 }; } const current = this.memorySnapshots[this.memorySnapshots.length - 1]; const peak = this.memorySnapshots.reduce((max, snapshot) => snapshot.heapUsed > max.heapUsed ? snapshot : max, this.memorySnapshots[0]); const totalHeap = this.memorySnapshots.reduce((sum, snapshot) => sum + snapshot.heapUsed, 0); const totalRss = this.memorySnapshots.reduce((sum, snapshot) => sum + snapshot.rss, 0); const average = { heapUsed: totalHeap / this.memorySnapshots.length, heapTotal: this.memorySnapshots.reduce((sum, s) => sum + s.heapTotal, 0) / this.memorySnapshots.length, rss: totalRss / this.memorySnapshots.length, external: this.memorySnapshots.reduce((sum, s) => sum + s.external, 0) / this.memorySnapshots.length, timestamp: new Date() }; // Calculate growth rate (MB per minute) let growthRate = 0; if (this.memorySnapshots.length > 1) { const oldest = this.memorySnapshots[0]; const timeDiff = (current.timestamp.getTime() - oldest.timestamp.getTime()) / 60000; // minutes const memoryDiff = (current.heapUsed - oldest.heapUsed) / (1024 * 1024); // MB growthRate = timeDiff > 0 ? memoryDiff / timeDiff : 0; } return { currentUsage: current, peakUsage: peak, averageUsage: average, growthRate }; } /** * Get slow queries with analysis */ getSlowQueries(limit = 10) { return this.slowQueries .sort((a, b) => b.duration - a.duration) .slice(0, limit); } /** * Analyze performance trends */ analyzeTrends() { const recommendations = []; // Analyze search performance trend let performanceTrend = 'stable'; if (this.searchMetrics.length > 10) { const recent = this.searchMetrics.slice(-10).map(m => m.duration); const older = this.searchMetrics.slice(-20, -10).map(m => m.duration); if (recent.length > 0 && older.length > 0) { const recentAvg = recent.reduce((sum, t) => sum + t, 0) / recent.length; const olderAvg = older.reduce((sum, t) => sum + t, 0) / older.length; if (recentAvg > olderAvg * 1.2) { performanceTrend = 'degrading'; recommendations.push('Search performance is degrading. Consider cache optimization or index rebuilding.'); } else if (recentAvg < olderAvg * 0.8) { performanceTrend = 'improving'; } } } // Analyze memory trend const memoryStats = this.getMemoryStats(); let memoryTrend = 'stable'; if (memoryStats.growthRate > 1) { // Growing by more than 1MB/minute memoryTrend = 'growing'; recommendations.push('Memory usage is growing rapidly. Consider cache cleanup or memory limits.'); } else if (memoryStats.growthRate < -1) { memoryTrend = 'shrinking'; } // Cache performance recommendations const cacheStats = this.aggregateCacheStats(); if (cacheStats.hitRate < 0.6) { recommendations.push('Cache hit rate is low. Consider adjusting cache size or TTL settings.'); } // Slow query recommendations if (this.slowQueries.length > 10) { recommendations.push('Multiple slow queries detected. Consider query optimization or increased caching.'); } return { performanceTrend, memoryTrend, recommendations }; } /** * Reset all performance metrics */ reset() { this.searchMetrics = []; this.slowQueries = []; this.memorySnapshots = []; this.cacheMetrics.clear(); logger.info('Performance metrics reset'); } /** * Export metrics for external analysis */ exportMetrics() { const data = { searchMetrics: this.searchMetrics, slowQueries: this.slowQueries, memorySnapshots: this.memorySnapshots, cacheMetrics: Object.fromEntries(this.cacheMetrics), exportTimestamp: new Date().toISOString() }; return JSON.stringify(data, null, 2); } // Private methods recordSlowQuery(query) { this.slowQueries.push(query); // Trim slow queries history if (this.slowQueries.length > this.maxSlowQueries) { this.slowQueries = this.slowQueries.slice(-this.maxSlowQueries); } } startMemoryMonitoring() { if (this.memoryMonitorInterval) { clearInterval(this.memoryMonitorInterval); } this.memoryMonitorInterval = setInterval(() => { if (this.isMonitoring) { const snapshot = this.takeMemorySnapshot(); this.memorySnapshots.push(snapshot); // Trim memory snapshots (keep last 200) if (this.memorySnapshots.length > 200) { this.memorySnapshots = this.memorySnapshots.slice(-200); } } }, this.memorySnapshotInterval); // Do not keep the Node.js event loop alive solely for monitoring if (typeof this.memoryMonitorInterval.unref === 'function') { this.memoryMonitorInterval.unref(); } } takeMemorySnapshot() { const memUsage = process.memoryUsage(); return { heapUsed: memUsage.heapUsed, heapTotal: memUsage.heapTotal, rss: memUsage.rss, external: memUsage.external, timestamp: new Date() }; } aggregateCacheStats() { if (this.cacheMetrics.size === 0) { return { hitRate: 0, avgHitTime: 0, avgMissTime: 0, totalHits: 0, totalMisses: 0, evictions: 0 }; } let totalHits = 0; let totalMisses = 0; let totalEvictions = 0; let weightedHitTime = 0; let weightedMissTime = 0; for (const stats of this.cacheMetrics.values()) { totalHits += stats.totalHits; totalMisses += stats.totalMisses; totalEvictions += stats.evictions; weightedHitTime += stats.avgHitTime * stats.totalHits; weightedMissTime += stats.avgMissTime * stats.totalMisses; } return { hitRate: totalHits + totalMisses > 0 ? totalHits / (totalHits + totalMisses) : 0, avgHitTime: totalHits > 0 ? weightedHitTime / totalHits : 0, avgMissTime: totalMisses > 0 ? weightedMissTime / totalMisses : 0, totalHits, totalMisses, evictions: totalEvictions }; } getSystemStats() { return { cpuUsage: process.cpuUsage().user / 1000000, // Convert to seconds loadAverage: loadavg(), freeMemory: freemem(), totalMemory: totalmem(), uptime: process.uptime() }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGVyZm9ybWFuY2VNb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL1BlcmZvcm1hbmNlTW9uaXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3JDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxNQUFNLFNBQVMsQ0FBQztBQXdEckQsTUFBTSxPQUFPLGtCQUFrQjtJQUNyQixhQUFhLEdBQW9CLEVBQUUsQ0FBQztJQUNwQyxXQUFXLEdBQWdCLEVBQUUsQ0FBQztJQUM5QixlQUFlLEdBQWtCLEVBQUUsQ0FBQztJQUNwQyxZQUFZLEdBQWtDLElBQUksR0FBRyxFQUFFLENBQUM7SUFFaEUsZ0JBQWdCO0lBQ0MsaUJBQWlCLEdBQUcsSUFBSSxDQUFDO0lBQ3pCLGtCQUFrQixHQUFHLEdBQUcsQ0FBQyxDQUFDLEtBQUs7SUFDL0Isc0JBQXNCLEdBQUcsS0FBSyxDQUFDLENBQUMsYUFBYTtJQUM3QyxjQUFjLEdBQUcsR0FBRyxDQUFDO0lBRXRDLHVCQUF1QjtJQUNmLHFCQUFxQixDQUFrQjtJQUN2QyxZQUFZLEdBQUcsS0FBSyxDQUFDO0lBRXJCLFdBQVcsQ0FBeUc7SUFFNUgsY0FBYyxDQUFDLEVBQXlHO1FBQ3RILElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDO1FBQ3RCLE9BQU8sR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLFdBQVcsR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVELGdCQUFlLENBQUM7SUFFaEI7O09BRUc7SUFDSCxlQUFlO1FBQ2IsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7WUFDdEIsT0FBTztRQUNULENBQUM7UUFFRCxJQUFJLENBQUMs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Q0osQ0FBQztJQUVPLGNBQWM7UUFDcEIsT0FBTztZQUNMLFFBQVEsRUFBRSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxHQUFHLE9BQU8sRUFBRSxxQkFBcUI7WUFDbEUsV0FBVyxFQUFFLE9BQU8sRUFBRTtZQUN0QixVQUFVLEVBQUUsT0FBTyxFQUFFO1lBQ3JCLFdBQVcsRUFBRSxRQUFRLEVBQUU7WUFDdkIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNLEVBQUU7U0FDekIsQ0FBQztJQUNKLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29tcHJlaGVuc2l2ZSBQZXJmb3JtYW5jZSBNb25pdG9yaW5nIFN5c3RlbVxuICogVHJhY2tzIHNlYXJjaCB0aW1lcywgbWVtb3J5IHVzYWdlLCBjYWNoZSBwZXJmb3JtYW5jZSwgYW5kIHN5c3RlbSBtZXRyaWNzXG4gKi9cblxuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBmcmVlbWVtLCB0b3RhbG1lbSwgbG9hZGF2ZyB9IGZyb20gJ25vZGU6b3MnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFBlcmZvcm1hbmNlTWV0cmljcyB7XG4gIHNlYXJjaFRpbWVzOiBudW1iZXJbXTtcbiAgbWVtb3J5VXNhZ2U6IE1lbW9yeVVzYWdlW107XG4gIGNhY2hlU3RhdHM6IENhY2hlUGVyZm9ybWFuY2U7XG4gIHN5c3RlbVN0YXRzOiBTeXN0ZW1TdGF0cztcbiAgdGltZXN0YW1wOiBEYXRlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIE1lbW9yeVVzYWdlIHtcbiAgaGVhcFVzZWQ6IG51bWJlcjtcbiAgaGVhcFRvdGFsOiBudW1iZXI7XG4gIHJzczogbnVtYmVyO1xuICBleHRlcm5hbDogbnVtYmVyO1xuICB0aW1lc3RhbXA6IERhdGU7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2FjaGVQZXJmb3JtYW5jZSB7XG4gIGhpdFJhdGU6IG51bWJlcjtcbiAgYXZnSGl0VGltZTogbnVtYmVyO1xuICBhdmdNaXNzVGltZTogbnVtYmVyO1xuICB0b3RhbEhpdHM6IG51bWJlcjtcbiAgdG90YWxNaXNzZXM6IG51bWJlcjtcbiAgZXZpY3Rpb25zOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3lzdGVtU3RhdHMge1xuICBjcHVVc2FnZTogbnVtYmVyO1xuICBsb2FkQXZlcmFnZTogbnVtYmVyW107XG4gIGZyZWVNZW1vcnk6IG51bWJlcjtcbiAgdG90YWxNZW1vcnk6IG51bWJlcjtcbiAgdXB0aW1lOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2VhcmNoTWV0cmljcyB7XG4gIHF1ZXJ5OiBzdHJpbmc7XG4gIGR1cmF0aW9uOiBudW1iZXI7XG4gIHJlc3VsdENvdW50OiBudW1iZXI7XG4gIHNvdXJjZXM6IHN0cmluZ1tdO1xuICBjYWNoZUhpdDogYm9vbGVhbjtcbiAgbWVtb3J5QmVmb3JlOiBudW1iZXI7XG4gIG1lbW9yeUFmdGVyOiBudW1iZXI7XG4gIHRpbWVzdGFtcDogRGF0ZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTbG93UXVlcnkge1xuICBxdWVyeTogc3RyaW5nO1xuICBkdXJhdGlvbjogbnVtYmVyO1xuICB0aHJlc2hvbGQ6IG51bWJlcjtcbiAgc291cmNlczogc3RyaW5nW107XG4gIHJlc3VsdENvdW50OiBudW1iZXI7XG4gIG1lbW9yeVVzYWdlOiBudW1iZXI7XG4gIHRpbWVzdGFtcDogRGF0ZTtcbn1cblxuZXhwb3J0IGNsYXNzIFBlcmZvcm1hbmNlTW9uaXRvciB7XG4gIHByaXZhdGUgc2VhcmNoTWV0cmljczogU2VhcmNoTWV0cmljc1tdID0gW107XG4gIHByaXZhdGUgc2xvd1F1ZXJpZXM6IFNsb3dRdWVyeVtdID0gW107XG4gIHByaXZhdGUgbWVtb3J5U25hcHNob3RzOiBNZW1vcnlVc2FnZVtdID0gW107XG4gIHByaXZhdGUgY2FjaGVNZXRyaWNzOiBNYXA8c3RyaW5nLCBDYWNoZVBlcmZvcm1hbmNlPiA9IG5ldyBNYXAoKTtcblxuICAvLyBDb25maWd1cmF0aW9uXG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4TWV0cmljc0hpc3RvcnkgPSAxMDAwO1xuICBwcml2YXRlIHJlYWRvbmx5IHNsb3dRdWVyeVRocmVzaG9sZCA9IDEwMDsgLy8gbXNcbiAgcHJpdmF0ZSByZWFkb25seSBtZW1vcnlTbmFwc2hvdEludGVydmFsID0gMzAwMDA7IC8vIDMwIHNlY29uZHNcbiAgcHJpdmF0ZSByZWFkb25seSBtYXhTbG93UXVlcmllcyA9IDEwMDtcblxuICAvLyBUaW1lcnMgYW5kIGludGVydmFsc1xuICBwcml2YXRlIG1lbW9yeU1vbml0b3JJbnRlcnZhbD86IE5vZGVKUy5UaW1lb3V0O1xuICBwcml2YXRlIGlzTW9uaXRvcmluZyA9IGZhbHNlO1xuXG4gIHByaXZhdGUgbG9nTGlzdGVuZXI/OiAobGV2ZWw6ICdkZWJ1ZycgfCAnaW5mbycgfCAnd2FybicgfCAnZXJyb3InLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gdm9pZDtcblxuICBhZGRMb2dMaXN0ZW5lcihmbjogKGxldmVsOiAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJywgbWVzc2FnZTogc3RyaW5nLCBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pID0+IHZvaWQpOiAoKSA9PiB2b2lkIHtcbiAgICB0aGlzLmxvZ0xpc3RlbmVyID0gZm47XG4gICAgcmV0dXJuICgpID0+IHsgdGhpcy5sb2dMaXN0ZW5lciA9IHVuZGVmaW5lZDsgfTtcbiAgfVxuXG4gIGNvbnN0cnVjdG9yKCkge31cblxuICAvKipcbiAgICogU3RhcnQgcGVyZm9ybWFuY2UgbW9uaXRvcmluZ1xuICAgKi9cbiAgc3RhcnRNb25pdG9yaW5nKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuaXNNb25pdG9yaW5nID0gdHJ1ZTtcbiAgICB0aGlzLnN0YXJ0TWVtb3J5TW9uaXRvcmluZygpO1xuICAgIFxuICAgIGxvZ2dlci5pbmZvKCdQZXJmb3JtYW5jZSBtb25pdG9yaW5nIHN0YXJ0ZWQnLCB7XG4gICAgICBzbG93UXVlcnlUaHJlc2hvbGQ6IHRoaXMuc2xvd1F1ZXJ5VGhyZXNob2xkLFxuICAgICAgbWF4TWV0cmljc0hpc3Rvcnk6IHRoaXMubWF4TWV0cmljc0hpc3RvcnlcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wIHBlcmZvcm1hbmNlIG1vbml0b3JpbmdcbiAgICovXG4gIHN0b3BNb25pdG9yaW5nKCk6IHZvaWQge1xuICAgIHRoaXMuaXNNb25pdG9yaW5nID0gZmFsc2U7XG4gICAgXG4gICAgaWYgKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsKTtcbiAgICAgIHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsID0gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIGxvZ2dlci5pbmZvKCdQZXJmb3JtYW5jZSBtb25pdG9yaW5nIHN0b3BwZWQnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNwb3NlIG1vbml0b3Jpbmcgc3RhdGUgYW5kIHRpbWVycy5cbiAgICovXG4gIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5zdG9wTW9uaXRvcmluZygpO1xuICAgIHRoaXMucmVzZXQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWNvcmQgc2VhcmNoIHBlcmZvcm1hbmNlIG1ldHJpY3NcbiAgICovXG4gIHJlY29yZFNlYXJjaChtZXRyaWNzOiBTZWFyY2hNZXRyaWNzKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIE5vcm1hbGl6ZSBxdWVyeSBzdHJpbmcgdG8gcHJldmVudCBVbmljb2RlLWJhc2VkIGF0dGFja3NcbiAgICBjb25zdCB2YWxpZGF0aW9uUmVzdWx0ID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobWV0cmljcy5xdWVyeSk7XG4gICAgY29uc3Qgbm9ybWFsaXplZE1ldHJpY3MgPSB7XG4gICAgICAuLi5tZXRyaWNzLFxuICAgICAgcXVlcnk6IHZhbGlkYXRpb25SZXN1bHQubm9ybWFsaXplZENvbnRlbnRcbiAgICB9O1xuXG4gICAgdGhpcy5zZWFyY2hNZXRyaWNzLnB1c2gobm9ybWFsaXplZE1ldHJpY3MpO1xuICAgIHRoaXMubG9nTGlzdGVuZXI/LignZGVidWcnLCAnUmVjb3JkIHNlYXJjaCcsIHtcbiAgICAgIHF1ZXJ5OiBub3JtYWxpemVkTWV0cmljcy5xdWVyeS5zdWJzdHJpbmcoMCwgNTApLFxuICAgICAgZHVyYXRpb246IG5vcm1hbGl6ZWRNZXRyaWNzLmR1cmF0aW9uLFxuICAgICAgcmVzdWx0Q291bnQ6IG5vcm1hbGl6ZWRNZXRyaWNzLnJlc3VsdENvdW50LFxuICAgICAgY2FjaGVIaXQ6IG5vcm1hbGl6ZWRNZXRyaWNzLmNhY2hlSGl0LFxuICAgIH0pO1xuXG4gICAgLy8gQ2hlY2sgaWYgaXQncyBhIHNsb3cgcXVlcnkgKHVzZSBub3JtYWxpemVkIG1ldHJpY3MpXG4gICAgaWYgKG5vcm1hbGl6ZWRNZXRyaWNzLmR1cmF0aW9uID4gdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQpIHtcbiAgICAgIHRoaXMucmVjb3JkU2xvd1F1ZXJ5KHtcbiAgICAgICAgcXVlcnk6IG5vcm1hbGl6ZWRNZXRyaWNzLnF1ZXJ5LFxuICAgICAgICBkdXJhdGlvbjogbm9ybWFsaXplZE1ldHJpY3MuZHVyYXRpb24sXG4gICAgICAgIHRocmVzaG9sZDogdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQsXG4gICAgICAgIHNvdXJjZXM6IG5vcm1hbGl6ZWRNZXRyaWNzLnNvdXJjZXMsXG4gICAgICAgIHJlc3VsdENvdW50OiBub3JtYWxpemVkTWV0cmljcy5yZXN1bHRDb3VudCxcbiAgICAgICAgbWVtb3J5VXNhZ2U6IG5vcm1hbGl6ZWRNZXRyaWNzLm1lbW9yeUFmdGVyLFxuICAgICAgICB0aW1lc3RhbXA6IG5vcm1hbGl6ZWRNZXRyaWNzLnRpbWVzdGFtcFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gVHJpbSBoaXN0b3J5IGlmIG5lZWRlZFxuICAgIGlmICh0aGlzLnNlYXJjaE1ldHJpY3MubGVuZ3RoID4gdGhpcy5tYXhNZXRyaWNzSGlzdG9yeSkge1xuICAgICAgdGhpcy5zZWFyY2hNZXRyaWNzID0gdGhpcy5zZWFyY2hNZXRyaWNzLnNsaWNlKC10aGlzLm1heE1ldHJpY3NIaXN0b3J5KTtcbiAgICB9XG5cbiAgICAvLyBOb3RpZnkgbGlzdGVuZXIgYWJvdXQgc2xvdyBxdWVyaWVzXG4gICAgaWYgKG5vcm1hbGl6ZWRNZXRyaWNzLmR1cmF0aW9uID4gdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQpIHtcbiAgICAgIHRoaXMubG9nTGlzdGVuZXI/Lignd2FybicsICdEZXRlY3Qgc2xvdyBxdWVyeScsIHtcbiAgICAgICAgcXVlcnk6IG5vcm1hbGl6ZWRNZXRyaWNzLnF1ZXJ5LnN1YnN0cmluZygwLCA1MCksXG4gICAgICAgIGR1cmF0aW9uOiBub3JtYWxpemVkTWV0cmljcy5kdXJhdGlvbixcbiAgICAgICAgdGhyZXNob2xkOiB0aGlzLnNsb3dRdWVyeVRocmVzaG9sZCxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIExvZyBzaWduaWZpY2FudCBwZXJmb3JtYW5jZSBldmVudHMgKHVzZSBub3JtYWxpemVkIG1ldHJpY3MpXG4gICAgaWYgKG5vcm1hbGl6ZWRNZXRyaWNzLmR1cmF0aW9uID4gdGhpcy5zbG93UXVlcnlUaHJlc2hvbGQgKiAyKSB7XG4gICAgICBsb2dnZXIud2FybignVmVyeSBzbG93IHNlYXJjaCBkZXRlY3RlZCcsIHtcbiAgICAgICAgcXVlcnk6IG5vcm1hbGl6ZWRNZXRyaWNzLnF1ZXJ5LnN1YnN0cmluZygwLCA1MCksXG4gICAgICAgIGR1cmF0aW9uOiBub3JtYWxpemVkTWV0cmljcy5kdXJhdGlvbixcbiAgICAgICAgcmVzdWx0Q291bnQ6IG5vcm1hbGl6ZWRNZXRyaWNzLnJlc3VsdENvdW50LFxuICAgICAgICBzb3VyY2VzOiBub3JtYWxpemVkTWV0cmljcy5zb3VyY2VzXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVjb3JkIGNhY2hlIHBlcmZvcm1hbmNlIG1ldHJpY3NcbiAgICovXG4gIHJlY29yZENhY2hlUGVyZm9ybWFuY2UoY2FjaGVOYW1lOiBzdHJpbmcsIHN0YXRzOiBDYWNoZVBlcmZvcm1hbmNlKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzTW9uaXRvcmluZykge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIE5vcm1hbGl6ZSBjYWNoZSBuYW1lIHRvIHByZXZlbnQgVW5pY29kZS1iYXNlZCBhdHRhY2tzXG4gICAgY29uc3QgdmFsaWRhdGlvblJlc3VsdCA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKGNhY2hlTmFtZSk7XG4gICAgY29uc3Qgbm9ybWFsaXplZENhY2hlTmFtZSA9IHZhbGlkYXRpb25SZXN1bHQubm9ybWFsaXplZENvbnRlbnQ7XG5cbiAgICB0aGlzLmNhY2hlTWV0cmljcy5zZXQobm9ybWFsaXplZENhY2hlTmFtZSwgc3RhdHMpO1xuXG4gICAgLy8gTm90aWZ5IGxpc3RlbmVyIGFib3V0IGxvdyBjYWNoZSBoaXQgcmF0ZVxuICAgIGlmIChzdGF0cy5oaXRSYXRlIDwgMC41KSB7XG4gICAgICB0aGlzLmxvZ0xpc3RlbmVyPy4oJ3dhcm4nLCAnRGV0ZWN0IGxvdyBjYWNoZSBoaXQgcmF0ZScsIHtcbiAgICAgICAgY2FjaGU6IG5vcm1hbGl6ZWRDYWNoZU5hbWUsXG4gICAgICAgIGhpdFJhdGU6IHN0YXRzLmhpdFJhdGUsXG4gICAgICAgIHRvdGFsT3BlcmF0aW9uczogc3RhdHMudG90YWxIaXRzICsgc3RhdHMudG90YWxNaXNzZXMsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBMb2cgY2FjaGUgcGVyZm9ybWFuY2Ugd2FybmluZ3MgKHVzZSBub3JtYWxpemVkIGNhY2hlIG5hbWUpXG4gICAgaWYgKHN0YXRzLmhpdFJhdGUgPCAwLjUpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdMb3cgY2FjaGUgaGl0IHJhdGUgZGV0ZWN0ZWQnLCB7XG4gICAgICAgIGNhY2hlOiBub3JtYWxpemVkQ2FjaGVOYW1lLFxuICAgICAgICBoaXRSYXRlOiBzdGF0cy5oaXRSYXRlLFxuICAgICAgICB0b3RhbE9wZXJhdGlvbnM6IHN0YXRzLnRvdGFsSGl0cyArIHN0YXRzLnRvdGFsTWlzc2VzXG4gICAgICB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGNvbXByZWhlbnNpdmUgcGVyZm9ybWFuY2UgbWV0cmljc1xuICAgKi9cbiAgZ2V0TWV0cmljcygpOiBQZXJmb3JtYW5jZU1ldHJpY3Mge1xuICAgIHJldHVybiB7XG4gICAgICBzZWFyY2hUaW1lczogdGhpcy5zZWFyY2hNZXRyaWNzLm1hcChtID0+IG0uZHVyYXRpb24pLFxuICAgICAgbWVtb3J5VXNhZ2U6IHRoaXMubWVtb3J5U25hcHNob3RzLnNsaWNlKC0xMDApLCAvLyBMYXN0IDEwMCBzbmFwc2hvdHNcbiAgICAgIGNhY2hlU3RhdHM6IHRoaXMuYWdncmVnYXRlQ2FjaGVTdGF0cygpLFxuICAgICAgc3lzdGVtU3RhdHM6IHRoaXMuZ2V0U3lzdGVtU3RhdHMoKSxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKVxuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogR2V0IHNlYXJjaCBwZXJmb3JtYW5jZSBzdGF0aXN0aWNzXG4gICAqL1xuICBnZXRTZWFyY2hTdGF0cygpOiB7XG4gICAgdG90YWxTZWFyY2hlczogbnVtYmVyO1xuICAgIGF2ZXJhZ2VUaW1lOiBudW1iZXI7XG4gICAgbWVkaWFuVGltZTogbnVtYmVyO1xuICAgIHA5NVRpbWU6IG51bWJlcjtcbiAgICBwOTlUaW1lOiBudW1iZXI7XG4gICAgc2xvd1F1ZXJpZXM6IG51bWJlcjtcbiAgICBjYWNoZUhpdFJhdGU6IG51bWJlcjtcbiAgfSB7XG4gICAgaWYgKHRoaXMuc2VhcmNoTWV0cmljcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHRvdGFsU2VhcmNoZXM6IDAsXG4gICAgICAgIGF2ZXJhZ2VUaW1lOiAwLFxuICAgICAgICBtZWRpYW5UaW1lOiAwLFxuICAgICAgICBwOTVUaW1lOiAwLFxuICAgICAgICBwOTlUaW1lOiAwLFxuICAgICAgICBzbG93UXVlcmllczogMCxcbiAgICAgICAgY2FjaGVIaXRSYXRlOiAwXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IHRpbWVzID0gdGhpcy5zZWFyY2hNZXRyaWNzLm1hcChtID0+IG0uZHVyYXRpb24pLnNvcnQoKGEsIGIpID0+IGEgLSBiKTtcbiAgICBjb25zdCBjYWNoZUhpdHMgPSB0aGlzLnNlYXJjaE1ldHJpY3MuZmlsdGVyKG0gPT4gbS5jYWNoZUhpdCkubGVuZ3RoO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIHRvdGFsU2VhcmNoZXM6IHRoaXMuc2VhcmNoTWV0cmljcy5sZW5ndGgsXG4gICAgICBhdmVyYWdlVGltZTogdGltZXMucmVkdWNlKChzdW0sIHRpbWUpID0+IHN1bSArIHRpbWUsIDApIC8gdGltZXMubGVuZ3RoLFxuICAgICAgbWVkaWFuVGltZTogdGltZXNbTWF0aC5mbG9vcih0aW1lcy5sZW5ndGggLyAyKV0sXG4gICAgICBwOTVUaW1lOiB0aW1lc1tNYXRoLmZsb29yKHRpbWVzLmxlbmd0aCAqIDAuOTUpXSxcbiAgICAgIHA5OVRpbWU6IHRpbWVzW01hdGguZmxvb3IodGltZXMubGVuZ3RoICogMC45OSldLFxuICAgICAgc2xvd1F1ZXJpZXM6IHRoaXMuc2xvd1F1ZXJpZXMubGVuZ3RoLFxuICAgICAgY2FjaGVIaXRSYXRlOiBjYWNoZUhpdHMgLyB0aGlzLnNlYXJjaE1ldHJpY3MubGVuZ3RoXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgbWVtb3J5IHVzYWdlIHN0YXRpc3RpY3NcbiAgICovXG4gIGdldE1lbW9yeVN0YXRzKCk6IHtcbiAgICBjdXJyZW50VXNhZ2U6IE1lbW9yeVVzYWdlO1xuICAgIHBlYWtVc2FnZTogTWVtb3J5VXNhZ2U7XG4gICAgYXZlcmFnZVVzYWdlOiBNZW1vcnlVc2FnZTtcbiAgICBncm93dGhSYXRlOiBudW1iZXI7IC8vIE1CIHBlciBtaW51dGVcbiAgfSB7XG4gICAgaWYgKHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgY29uc3QgY3VycmVudCA9IHRoaXMudGFrZU1lbW9yeVNuYXBzaG90KCk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBjdXJyZW50VXNhZ2U6IGN1cnJlbnQsXG4gICAgICAgIHBlYWtVc2FnZTogY3VycmVudCxcbiAgICAgICAgYXZlcmFnZVVzYWdlOiBjdXJyZW50LFxuICAgICAgICBncm93dGhSYXRlOiAwXG4gICAgICB9O1xuICAgIH1cblxuICAgIGNvbnN0IGN1cnJlbnQgPSB0aGlzLm1lbW9yeVNuYXBzaG90c1t0aGlzLm1lbW9yeVNuYXBzaG90cy5sZW5ndGggLSAxXTtcbiAgICBjb25zdCBwZWFrID0gdGhpcy5tZW1vcnlTbmFwc2hvdHMucmVkdWNlKChtYXgsIHNuYXBzaG90KSA9PlxuICAgICAgc25hcHNob3QuaGVhcFVzZWQgPiBtYXguaGVhcFVzZWQgPyBzbmFwc2hvdCA6IG1heCxcbiAgICAgIHRoaXMubWVtb3J5U25hcHNob3RzWzBdXG4gICAgKTtcblxuICAgIGNvbnN0IHRvdGFsSGVhcCA9IHRoaXMubWVtb3J5U25hcHNob3RzLnJlZHVjZSgoc3VtLCBzbmFwc2hvdCkgPT4gc3VtICsgc25hcHNob3QuaGVhcFVzZWQsIDApO1xuICAgIGNvbnN0IHRvdGFsUnNzID0gdGhpcy5tZW1vcnlTbmFwc2hvdHMucmVkdWNlKChzdW0sIHNuYXBzaG90KSA9PiBzdW0gKyBzbmFwc2hvdC5yc3MsIDApO1xuICAgIGNvbnN0IGF2ZXJhZ2U6IE1lbW9yeVVzYWdlID0ge1xuICAgICAgaGVhcFVzZWQ6IHRvdGFsSGVhcCAvIHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCxcbiAgICAgIGhlYXBUb3RhbDogdGhpcy5tZW1vcnlTbmFwc2hvdHMucmVkdWNlKChzdW0sIHMpID0+IHN1bSArIHMuaGVhcFRvdGFsLCAwKSAvIHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCxcbiAgICAgIHJzczogdG90YWxSc3MgLyB0aGlzLm1lbW9yeVNuYXBzaG90cy5sZW5ndGgsXG4gICAgICBleHRlcm5hbDogdGhpcy5tZW1vcnlTbmFwc2hvdHMucmVkdWNlKChzdW0sIHMpID0+IHN1bSArIHMuZXh0ZXJuYWwsIDApIC8gdGhpcy5tZW1vcnlTbmFwc2hvdHMubGVuZ3RoLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgfTtcblxuICAgIC8vIENhbGN1bGF0ZSBncm93dGggcmF0ZSAoTUIgcGVyIG1pbnV0ZSlcbiAgICBsZXQgZ3Jvd3RoUmF0ZSA9IDA7XG4gICAgaWYgKHRoaXMubWVtb3J5U25hcHNob3RzLmxlbmd0aCA+IDEpIHtcbiAgICAgIGNvbnN0IG9sZGVzdCA9IHRoaXMubWVtb3J5U25hcHNob3RzWzBdO1xuICAgICAgY29uc3QgdGltZURpZmYgPSAoY3VycmVudC50aW1lc3RhbXAuZ2V0VGltZSgpIC0gb2xkZXN0LnRpbWVzdGFtcC5nZXRUaW1lKCkpIC8gNjAwMDA7IC8vIG1pbnV0ZXNcbiAgICAgIGNvbnN0IG1lbW9yeURpZmYgPSAoY3VycmVudC5oZWFwVXNlZCAtIG9sZGVzdC5oZWFwVXNlZCkgLyAoMTAyNCAqIDEwMjQpOyAvLyBNQlxuICAgICAgZ3Jvd3RoUmF0ZSA9IHRpbWVEaWZmID4gMCA/IG1lbW9yeURpZmYgLyB0aW1lRGlmZiA6IDA7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGN1cnJlbnRVc2FnZTogY3VycmVudCxcbiAgICAgIHBlYWtVc2FnZTogcGVhayxcbiAgICAgIGF2ZXJhZ2VVc2FnZTogYXZlcmFnZSxcbiAgICAgIGdyb3d0aFJhdGVcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBzbG93IHF1ZXJpZXMgd2l0aCBhbmFseXNpc1xuICAgKi9cbiAgZ2V0U2xvd1F1ZXJpZXMobGltaXQ6IG51bWJlciA9IDEwKTogU2xvd1F1ZXJ5W10ge1xuICAgIHJldHVybiB0aGlzLnNsb3dRdWVyaWVzXG4gICAgICAuc29ydCgoYSwgYikgPT4gYi5kdXJhdGlvbiAtIGEuZHVyYXRpb24pXG4gICAgICAuc2xpY2UoMCwgbGltaXQpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFuYWx5emUgcGVyZm9ybWFuY2UgdHJlbmRzXG4gICAqL1xuICBhbmFseXplVHJlbmRzKCk6IHtcbiAgICBwZXJmb3JtYW5jZVRyZW5kOiAnaW1wcm92aW5nJyB8ICdkZWdyYWRpbmcnIHwgJ3N0YWJsZSc7XG4gICAgbWVtb3J5VHJlbmQ6ICdncm93aW5nJyB8ICdzaHJpbmtpbmcnIHwgJ3N0YWJsZSc7XG4gICAgcmVjb21tZW5kYXRpb25zOiBzdHJpbmdbXTtcbiAgfSB7XG4gICAgY29uc3QgcmVjb21tZW5kYXRpb25zOiBzdHJpbmdbXSA9IFtdO1xuICAgIFxuICAgIC8vIEFuYWx5emUgc2VhcmNoIHBlcmZvcm1hbmNlIHRyZW5kXG4gICAgbGV0IHBlcmZvcm1hbmNlVHJlbmQ6ICdpbXByb3ZpbmcnIHwgJ2RlZ3JhZGluZycgfCAnc3RhYmxlJyA9ICdzdGFibGUnO1xuICAgIGlmICh0aGlzLnNlYXJjaE1ldHJpY3MubGVuZ3RoID4gMTApIHtcbiAgICAgIGNvbnN0IHJlY2VudCA9IHRoaXMuc2VhcmNoTWV0cmljcy5zbGljZSgtMTApLm1hcChtID0+IG0uZHVyYXRpb24pO1xuICAgICAgY29uc3Qgb2xkZXIgPSB0aGlzLnNlYXJjaE1ldHJpY3Muc2xpY2UoLTIwLCAtMTApLm1hcChtID0+IG0uZHVyYXRpb24pO1xuICAgICAgXG4gICAgICBpZiAocmVjZW50Lmxlbmd0aCA+IDAgJiYgb2xkZXIubGVuZ3RoID4gMCkge1xuICAgICAgICBjb25zdCByZWNlbnRBdmcgPSByZWNlbnQucmVkdWNlKChzdW0sIHQpID0+IHN1bSArIHQsIDApIC8gcmVjZW50Lmxlbmd0aDtcbiAgICAgICAgY29uc3Qgb2xkZXJBdmcgPSBvbGRlci5yZWR1Y2UoKHN1bSwgdCkgPT4gc3VtICsgdCwgMCkgLyBvbGRlci5sZW5ndGg7XG4gICAgICAgIFxuICAgICAgICBpZiAocmVjZW50QXZnID4gb2xkZXJBdmcgKiAxLjIpIHtcbiAgICAgICAgICBwZXJmb3JtYW5jZVRyZW5kID0gJ2RlZ3JhZGluZyc7XG4gICAgICAgICAgcmVjb21tZW5kYXRpb25zLnB1c2goJ1NlYXJjaCBwZXJmb3JtYW5jZSBpcyBkZWdyYWRpbmcuIENvbnNpZGVyIGNhY2hlIG9wdGltaXphdGlvbiBvciBpbmRleCByZWJ1aWxkaW5nLicpO1xuICAgICAgICB9IGVsc2UgaWYgKHJlY2VudEF2ZyA8IG9sZGVyQXZnICogMC44KSB7XG4gICAgICAgICAgcGVyZm9ybWFuY2VUcmVuZCA9ICdpbXByb3ZpbmcnO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQW5hbHl6ZSBtZW1vcnkgdHJlbmRcbiAgICBjb25zdCBtZW1vcnlTdGF0cyA9IHRoaXMuZ2V0TWVtb3J5U3RhdHMoKTtcbiAgICBsZXQgbWVtb3J5VHJlbmQ6ICdncm93aW5nJyB8ICdzaHJpbmtpbmcnIHwgJ3N0YWJsZScgPSAnc3RhYmxlJztcbiAgICBcbiAgICBpZiAobWVtb3J5U3RhdHMuZ3Jvd3RoUmF0ZSA+IDEpIHsgLy8gR3Jvd2luZyBieSBtb3JlIHRoYW4gMU1CL21pbnV0ZVxuICAgICAgbWVtb3J5VHJlbmQgPSAnZ3Jvd2luZyc7XG4gICAgICByZWNvbW1lbmRhdGlvbnMucHVzaCgnTWVtb3J5IHVzYWdlIGlzIGdyb3dpbmcgcmFwaWRseS4gQ29uc2lkZXIgY2FjaGUgY2xlYW51cCBvciBtZW1vcnkgbGltaXRzLicpO1xuICAgIH0gZWxzZSBpZiAobWVtb3J5U3RhdHMuZ3Jvd3RoUmF0ZSA8IC0xKSB7XG4gICAgICBtZW1vcnlUcmVuZCA9ICdzaHJpbmtpbmcnO1xuICAgIH1cblxuICAgIC8vIENhY2hlIHBlcmZvcm1hbmNlIHJlY29tbWVuZGF0aW9uc1xuICAgIGNvbnN0IGNhY2hlU3RhdHMgPSB0aGlzLmFnZ3JlZ2F0ZUNhY2hlU3RhdHMoKTtcbiAgICBpZiAoY2FjaGVTdGF0cy5oaXRSYXRlIDwgMC42KSB7XG4gICAgICByZWNvbW1lbmRhdGlvbnMucHVzaCgnQ2FjaGUgaGl0IHJhdGUgaXMgbG93LiBDb25zaWRlciBhZGp1c3RpbmcgY2FjaGUgc2l6ZSBvciBUVEwgc2V0dGluZ3MuJyk7XG4gICAgfVxuXG4gICAgLy8gU2xvdyBxdWVyeSByZWNvbW1lbmRhdGlvbnNcbiAgICBpZiAodGhpcy5zbG93UXVlcmllcy5sZW5ndGggPiAxMCkge1xuICAgICAgcmVjb21tZW5kYXRpb25zLnB1c2goJ011bHRpcGxlIHNsb3cgcXVlcmllcyBkZXRlY3RlZC4gQ29uc2lkZXIgcXVlcnkgb3B0aW1pemF0aW9uIG9yIGluY3JlYXNlZCBjYWNoaW5nLicpO1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBwZXJmb3JtYW5jZVRyZW5kLFxuICAgICAgbWVtb3J5VHJlbmQsXG4gICAgICByZWNvbW1lbmRhdGlvbnNcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0IGFsbCBwZXJmb3JtYW5jZSBtZXRyaWNzXG4gICAqL1xuICByZXNldCgpOiB2b2lkIHtcbiAgICB0aGlzLnNlYXJjaE1ldHJpY3MgPSBbXTtcbiAgICB0aGlzLnNsb3dRdWVyaWVzID0gW107XG4gICAgdGhpcy5tZW1vcnlTbmFwc2hvdHMgPSBbXTtcbiAgICB0aGlzLmNhY2hlTWV0cmljcy5jbGVhcigpO1xuICAgIFxuICAgIGxvZ2dlci5pbmZvKCdQZXJmb3JtYW5jZSBtZXRyaWNzIHJlc2V0Jyk7XG4gIH1cblxuICAvKipcbiAgICogRXhwb3J0IG1ldHJpY3MgZm9yIGV4dGVybmFsIGFuYWx5c2lzXG4gICAqL1xuICBleHBvcnRNZXRyaWNzKCk6IHN0cmluZyB7XG4gICAgY29uc3QgZGF0YSA9IHtcbiAgICAgIHNlYXJjaE1ldHJpY3M6IHRoaXMuc2VhcmNoTWV0cmljcyxcbiAgICAgIHNsb3dRdWVyaWVzOiB0aGlzLnNsb3dRdWVyaWVzLFxuICAgICAgbWVtb3J5U25hcHNob3RzOiB0aGlzLm1lbW9yeVNuYXBzaG90cyxcbiAgICAgIGNhY2hlTWV0cmljczogT2JqZWN0LmZyb21FbnRyaWVzKHRoaXMuY2FjaGVNZXRyaWNzKSxcbiAgICAgIGV4cG9ydFRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpXG4gICAgfTtcblxuICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShkYXRhLCBudWxsLCAyKTtcbiAgfVxuXG4gIC8vIFByaXZhdGUgbWV0aG9kc1xuXG4gIHByaXZhdGUgcmVjb3JkU2xvd1F1ZXJ5KHF1ZXJ5OiBTbG93UXVlcnkpOiB2b2lkIHtcbiAgICB0aGlzLnNsb3dRdWVyaWVzLnB1c2gocXVlcnkpO1xuXG4gICAgLy8gVHJpbSBzbG93IHF1ZXJpZXMgaGlzdG9yeVxuICAgIGlmICh0aGlzLnNsb3dRdWVyaWVzLmxlbmd0aCA+IHRoaXMubWF4U2xvd1F1ZXJpZXMpIHtcbiAgICAgIHRoaXMuc2xvd1F1ZXJpZXMgPSB0aGlzLnNsb3dRdWVyaWVzLnNsaWNlKC10aGlzLm1heFNsb3dRdWVyaWVzKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0TWVtb3J5TW9uaXRvcmluZygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5tZW1vcnlNb25pdG9ySW50ZXJ2YWwpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5tZW1vcnlNb25pdG9ySW50ZXJ2YWwpO1xuICAgIH1cblxuICAgIHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuaXNNb25pdG9yaW5nKSB7XG4gICAgICAgIGNvbnN0IHNuYXBzaG90ID0gdGhpcy50YWtlTWVtb3J5U25hcHNob3QoKTtcbiAgICAgICAgdGhpcy5tZW1vcnlTbmFwc2hvdHMucHVzaChzbmFwc2hvdCk7XG5cbiAgICAgICAgLy8gVHJpbSBtZW1vcnkgc25hcHNob3RzIChrZWVwIGxhc3QgMjAwKVxuICAgICAgICBpZiAodGhpcy5tZW1vcnlTbmFwc2hvdHMubGVuZ3RoID4gMjAwKSB7XG4gICAgICAgICAgdGhpcy5tZW1vcnlTbmFwc2hvdHMgPSB0aGlzLm1lbW9yeVNuYXBzaG90cy5zbGljZSgtMjAwKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sIHRoaXMubWVtb3J5U25hcHNob3RJbnRlcnZhbCk7XG5cbiAgICAvLyBEbyBub3Qga2VlcCB0aGUgTm9kZS5qcyBldmVudCBsb29wIGFsaXZlIHNvbGVseSBmb3IgbW9uaXRvcmluZ1xuICAgIGlmICh0eXBlb2YgdGhpcy5tZW1vcnlNb25pdG9ySW50ZXJ2YWwudW5yZWYgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIHRoaXMubWVtb3J5TW9uaXRvckludGVydmFsLnVucmVmKCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB0YWtlTWVtb3J5U25hcHNob3QoKTogTWVtb3J5VXNhZ2Uge1xuICAgIGNvbnN0IG1lbVVzYWdlID0gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpO1xuICAgIHJldHVybiB7XG4gICAgICBoZWFwVXNlZDogbWVtVXNhZ2UuaGVhcFVzZWQsXG4gICAgICBoZWFwVG90YWw6IG1lbVVzYWdlLmhlYXBUb3RhbCxcbiAgICAgIHJzczogbWVtVXNhZ2UucnNzLFxuICAgICAgZXh0ZXJuYWw6IG1lbVVzYWdlLmV4dGVybmFsLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpXG4gICAgfTtcbiAgfVxuXG4gIHByaXZhdGUgYWdncmVnYXRlQ2FjaGVTdGF0cygpOiBDYWNoZVBlcmZvcm1hbmNlIHtcbiAgICBpZiAodGhpcy5jYWNoZU1ldHJpY3Muc2l6ZSA9PT0gMCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaGl0UmF0ZTogMCxcbiAgICAgICAgYXZnSGl0VGltZTogMCxcbiAgICAgICAgYXZnTWlzc1RpbWU6IDAsXG4gICAgICAgIHRvdGFsSGl0czogMCxcbiAgICAgICAgdG90YWxNaXNzZXM6IDAsXG4gICAgICAgIGV2aWN0aW9uczogMFxuICAgICAgfTtcbiAgICB9XG5cbiAgICBsZXQgdG90YWxIaXRzID0gMDtcbiAgICBsZXQgdG90YWxNaXNzZXMgPSAwO1xuICAgIGxldCB0b3RhbEV2aWN0aW9ucyA9IDA7XG4gICAgbGV0IHdlaWdodGVkSGl0VGltZSA9IDA7XG4gICAgbGV0IHdlaWdodGVkTWlzc1RpbWUgPSAwO1xuXG4gICAgZm9yIChjb25zdCBzdGF0cyBvZiB0aGlzLmNhY2hlTWV0cmljcy52YWx1ZXMoKSkge1xuICAgICAgdG90YWxIaXRzICs9IHN0YXRzLnRvdGFsSGl0cztcbiAgICAgIHRvdGFsTWlzc2VzICs9IHN0YXRzLnRvdGFsTWlzc2VzO1xuICAgICAgdG90YWxFdmljdGlvbnMgKz0gc3RhdHMuZXZpY3Rpb25zO1xuICAgICAgd2VpZ2h0ZWRIaXRUaW1lICs9IHN0YXRzLmF2Z0hpdFRpbWUgKiBzdGF0cy50b3RhbEhpdHM7XG4gICAgICB3ZWlnaHRlZE1pc3NUaW1lICs9IHN0YXRzLmF2Z01pc3NUaW1lICogc3RhdHMudG90YWxNaXNzZXM7XG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGhpdFJhdGU6IHRvdGFsSGl0cyArIHRvdGFsTWlzc2VzID4gMCA/IHRvdGFsSGl0cyAvICh0b3RhbEhpdHMgKyB0b3RhbE1pc3NlcykgOiAwLFxuICAgICAgYXZnSGl0VGltZTogdG90Y