@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
JavaScript
/**
* 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