@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.
364 lines • 46.5 kB
JavaScript
/**
* Smart cache for collection index with conditional fetching and performance optimization
*/
import * as path from 'path';
import { logger } from '../utils/logger.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
import { CacheFactory } from './LRUCache.js';
export class CollectionIndexCache {
cache = null;
TTL = 15 * 60 * 1000; // 15 minutes in milliseconds
INDEX_URL = 'https://dollhousemcp.github.io/collection/collection-index.json';
cacheDir;
cacheFile;
githubClient;
performanceMonitor;
memoryCache;
fetchPromise = null; // Prevent concurrent fetches
// File operations service for secure file I/O
fileOperations;
constructor(githubClient, baseDir, performanceMonitor, fileOperations) {
this.githubClient = githubClient;
this.cacheDir = path.join(baseDir, '.dollhousemcp', 'cache');
this.cacheFile = path.join(this.cacheDir, 'collection-index-cache.json');
this.performanceMonitor = performanceMonitor;
this.fileOperations = fileOperations;
// Initialize memory cache for frequently accessed data
this.memoryCache = CacheFactory.createAPICache({
maxSize: 50,
maxMemoryMB: 10,
ttlMs: 5 * 60 * 1000, // 5 minutes
onEviction: (key, _value) => {
logger.debug('Collection memory cache eviction', { key });
}
});
}
/**
* Get the collection index with performance optimization and lazy loading
*/
async getIndex(lazyLoad = false) {
const startTime = Date.now();
const memoryBefore = process.memoryUsage().heapUsed;
try {
// Check memory cache first for fastest access
const memoryCached = this.memoryCache.get('collection-index');
if (memoryCached && this.isValid()) {
logger.debug('Using memory cached collection index');
this.recordPerformanceMetrics(startTime, memoryBefore, 'memory-hit');
return memoryCached;
}
// Check if we have valid disk cached data
if (this.isValid()) {
logger.debug('Using valid disk cached collection index');
const result = this.cache.data;
this.memoryCache.set('collection-index', result);
this.recordPerformanceMetrics(startTime, memoryBefore, 'disk-hit');
return result;
}
// Prevent concurrent fetches
if (this.fetchPromise) {
logger.debug('Waiting for ongoing collection index fetch');
return await this.fetchPromise;
}
// Lazy loading: Only fetch if not in lazy mode or absolutely necessary
if (lazyLoad && this.cache?.data) {
logger.debug('Using stale cache in lazy load mode');
this.recordPerformanceMetrics(startTime, memoryBefore, 'lazy-stale');
return this.cache.data;
}
// Create fetch promise to prevent concurrent requests
this.fetchPromise = this.fetchFreshWithFallback();
try {
const result = await this.fetchPromise;
this.memoryCache.set('collection-index', result);
this.recordPerformanceMetrics(startTime, memoryBefore, 'fresh-fetch');
return result;
}
finally {
this.fetchPromise = null;
}
}
catch (error) {
logger.error('Failed to get collection index:', error);
// Try loading from persistent cache as last resort
const persistentCache = await this.loadFromDisk();
if (persistentCache) {
logger.debug('Using persistent cache as last resort');
this.cache = persistentCache;
const result = persistentCache.data;
this.memoryCache.set('collection-index', result);
this.recordPerformanceMetrics(startTime, memoryBefore, 'disk-fallback');
return result;
}
throw error;
}
}
/**
* Check if current cache is valid (within TTL)
*/
isValid() {
if (!this.cache) {
return false;
}
const age = Date.now() - this.cache.fetchedAt.getTime();
return age < this.TTL;
}
/**
* Fetch fresh index from GitHub with conditional requests
*/
async fetchFresh() {
try {
// Build headers for conditional request
const headers = {
'Accept': 'application/json',
'User-Agent': 'DollhouseMCP/1.0'
};
// Add conditional headers if we have cache
if (this.cache?.etag) {
headers['If-None-Match'] = this.cache.etag;
}
if (this.cache?.lastModified) {
headers['If-Modified-Since'] = this.cache.lastModified;
}
// Use fetch directly for better control over conditional requests
const response = await fetch(this.INDEX_URL, { headers });
// 304 Not Modified - use cached data
if (response.status === 304) {
if (this.cache) {
// Update timestamp to extend cache validity
this.cache.fetchedAt = new Date();
await this.saveToDisk(this.cache);
logger.debug('Collection index not modified - refreshed cache timestamp');
return this.cache.data;
}
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const indexData = await response.json();
// Validate the index structure
if (!this.validateIndexStructure(indexData)) {
throw new Error('Invalid collection index structure received');
}
// Create new cache entry
const newCache = {
data: indexData,
fetchedAt: new Date(),
etag: response.headers.get('etag') || undefined,
lastModified: response.headers.get('last-modified') || undefined
};
this.cache = newCache;
// Save to persistent cache in background
this.saveToDisk(newCache).catch(error => {
logger.debug('Failed to save index cache to disk:', error);
});
logger.debug(`Fresh collection index fetched with ${indexData.total_elements} elements`);
return indexData;
}
catch (error) {
logger.debug('Failed to fetch fresh collection index:', error);
return null;
}
}
/**
* Validate the structure of a collection index
*/
validateIndexStructure(index) {
return (index &&
typeof index === 'object' &&
typeof index.version === 'string' &&
typeof index.generated === 'string' &&
typeof index.total_elements === 'number' &&
index.index &&
typeof index.index === 'object' &&
index.metadata &&
typeof index.metadata === 'object');
}
/**
* Save cache to persistent storage
*/
async saveToDisk(cache) {
try {
await this.ensureCacheDir();
const cacheData = {
...cache,
fetchedAt: cache.fetchedAt.toISOString() // Serialize date
};
const data = JSON.stringify(cacheData, null, 2);
await this.fileOperations.writeFile(this.cacheFile, data, {
source: 'CollectionIndexCache.saveToDisk'
});
logger.debug('Collection index cache saved to disk');
}
catch (error) {
logger.debug('Failed to save collection index cache:', error);
// Don't throw - cache persistence failures shouldn't break functionality
}
}
/**
* Load cache from persistent storage
*/
async loadFromDisk() {
try {
// Basic security check for path traversal
if (this.cacheFile.includes('..') || this.cacheFile.includes('\0')) {
SecurityMonitor.logSecurityEvent({
type: 'PATH_TRAVERSAL_ATTEMPT',
severity: 'HIGH',
source: 'CollectionIndexCache.loadFromDisk',
details: `Potential path traversal attempt detected: ${this.cacheFile.substring(0, 100)}`
});
return null;
}
const data = await this.fileOperations.readFile(this.cacheFile, {
source: 'CollectionIndexCache.loadFromDisk'
});
const cacheData = JSON.parse(data);
return {
...cacheData,
fetchedAt: new Date(cacheData.fetchedAt) // Deserialize date
};
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug('Failed to load collection index cache from disk:', error);
}
return null;
}
}
/**
* Ensure cache directory exists
*/
async ensureCacheDir() {
try {
await this.fileOperations.createDirectory(this.cacheDir);
}
catch (error) {
logger.error('Failed to create cache directory:', error);
throw error;
}
}
/**
* Clear all cache data with performance monitoring
*/
async clearCache() {
const startTime = Date.now();
this.cache = null;
this.memoryCache.clear();
// Cancel any ongoing fetch
this.fetchPromise = null;
try {
await this.fileOperations.deleteFile(this.cacheFile, undefined, {
source: 'CollectionIndexCache.clearCache'
});
logger.debug('Collection index cache cleared from disk');
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug('Failed to clear collection index cache:', error);
}
}
logger.debug('Collection cache cleared', {
duration: Date.now() - startTime,
memoryFreed: this.memoryCache.getMemoryUsageMB()
});
}
/**
* Get comprehensive cache statistics for debugging and monitoring
*/
getCacheStats() {
if (!this.cache) {
return {
isValid: false,
age: 0,
hasCache: false,
elements: 0,
memoryCache: this.memoryCache.getStats(),
performanceMetrics: null
};
}
const age = Date.now() - this.cache.fetchedAt.getTime();
return {
isValid: this.isValid(),
age,
hasCache: true,
elements: this.cache.data.total_elements,
memoryCache: this.memoryCache.getStats(),
performanceMetrics: {
cacheHitRate: this.calculateCacheHitRate(),
averageAccessTime: this.calculateAverageAccessTime()
}
};
}
/**
* Clear in-memory cache state without touching disk.
*/
clear() {
this.cache = null;
this.memoryCache.clear();
this.fetchPromise = null;
}
// =====================================================
// PRIVATE HELPER METHODS FOR PERFORMANCE
// =====================================================
/**
* Fetch fresh index with comprehensive fallback strategy
*/
async fetchFreshWithFallback() {
try {
// Try to fetch fresh index
const freshIndex = await this.fetchFresh();
if (freshIndex) {
logger.debug('Collection index fetched successfully');
return freshIndex;
}
}
catch (error) {
logger.warn('Fresh fetch failed, trying fallback', {
error: error instanceof Error ? error.message : String(error)
});
}
// Fall back to stale cache if available
if (this.cache?.data) {
logger.debug('Using stale cached collection index as fallback');
return this.cache.data;
}
throw new Error('No collection index available - fresh fetch failed and no cache exists');
}
/**
* Record performance metrics for cache operations
*/
recordPerformanceMetrics(startTime, memoryBefore, operation) {
const duration = Date.now() - startTime;
const memoryAfter = process.memoryUsage().heapUsed;
logger.debug('Collection cache operation completed', {
operation,
duration,
memoryUsageMB: (memoryAfter - memoryBefore) / (1024 * 1024)
});
// Record cache performance metrics
this.performanceMonitor.recordCachePerformance('collectionIndex', {
hitRate: operation.includes('hit') ? 1 : 0,
avgHitTime: operation.includes('hit') ? duration : 0,
avgMissTime: operation.includes('hit') ? 0 : duration,
totalHits: operation.includes('hit') ? 1 : 0,
totalMisses: operation.includes('hit') ? 0 : 1,
evictions: 0
});
}
/**
* Calculate cache hit rate (placeholder for future implementation)
*/
calculateCacheHitRate() {
// This would be implemented with actual metrics tracking
return this.memoryCache.getStats().hitRate;
}
/**
* Calculate average access time (placeholder for future implementation)
*/
calculateAverageAccessTime() {
// This would be implemented with actual timing metrics
return 5; // Placeholder value
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ29sbGVjdGlvbkluZGV4Q2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2FjaGUvQ29sbGVjdGlvbkluZGV4Q2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFNUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLE9BQU8sRUFBWSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFJdkQsTUFBTSxPQUFPLG9CQUFvQjtJQUN2QixLQUFLLEdBQXVCLElBQUksQ0FBQztJQUN4QixHQUFHLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsQ0FBQyw2QkFBNkI7SUFDbkQsU0FBUyxHQUFHLGlFQUFpRSxDQUFDO0lBQ3ZGLFFBQVEsQ0FBUztJQUNqQixTQUFTLENBQVM7SUFDbEIsWUFBWSxDQUFlO0lBQzNCLGtCQUFrQixDQUFxQjtJQUN2QyxXQUFXLENBQWdCO0lBQzNCLFlBQVksR0FBb0MsSUFBSSxDQUFDLENBQUMsNkJBQTZCO0lBRTNGLDhDQUE4QztJQUM3QixjQUFjLENBQXlCO0lBRXhELFlBQ0UsWUFBMEIsRUFDMUIsT0FBZSxFQUNmLGtCQUFzQyxFQUN0QyxjQUFzQztRQUV0QyxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQztRQUNqQyxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSw2QkFBNkIsQ0FBQyxDQUFDO1FBQ3pFLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxrQkFBa0IsQ0FBQztRQUM3QyxJ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QUFFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM1QyxXQUFXLEVBQUUsU0FBUyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzlDLFNBQVMsRUFBRSxDQUFDO1NBQ2IsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0sscUJBQXFCO1FBQzNCLHlEQUF5RDtRQUN6RCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxFQUFFLENBQUMsT0FBTyxDQUFDO0lBQzdDLENBQUM7SUFFRDs7T0FFRztJQUNLLDBCQUEwQjtRQUNoQyx1REFBdUQ7UUFDdkQsT0FBTyxDQUFDLENBQUMsQ0FBQyxvQkFBb0I7SUFDaEMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTbWFydCBjYWNoZSBmb3IgY29sbGVjdGlvbiBpbmRleCB3aXRoIGNvbmRpdGlvbmFsIGZldGNoaW5nIGFuZCBwZXJmb3JtYW5jZSBvcHRpbWl6YXRpb25cbiAqL1xuXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgQ29sbGVjdGlvbkluZGV4LCBDYWNoZWRJbmRleCB9IGZyb20gJy4uL3R5cGVzL2NvbGxlY3Rpb24uanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IEdpdEh1YkNsaWVudCB9IGZyb20gJy4uL2NvbGxlY3Rpb24vR2l0SHViQ2xpZW50LmpzJztcbmltcG9ydCB7IFNlY3VyaXR5TW9uaXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3NlY3VyaXR5TW9uaXRvci5qcyc7XG5pbXBvcnQgeyBMUlVDYWNoZSwgQ2FjaGVGYWN0b3J5IH0gZnJvbSAnLi9MUlVDYWNoZS5qcyc7XG5pbXBvcnQgeyBQZXJmb3JtYW5jZU1vbml0b3IgfSBmcm9tICcuLi91dGlscy9QZXJmb3JtYW5jZU1vbml0b3IuanMnO1xuaW1wb3J0IHsgSUZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5cbmV4cG9ydCBjbGFzcyBDb2xsZWN0aW9uSW5kZXhDYWNoZSB7XG4gIHByaXZhdGUgY2FjaGU6IENhY2hlZEluZGV4IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgVFRMID0gMTUgKiA2MCAqIDEwMDA7IC8vIDE1IG1pbnV0ZXMgaW4gbWlsbGlzZWNvbmRzXG4gIHByaXZhdGUgcmVhZG9ubHkgSU5ERVhfVVJMID0gJ2h0dHBzOi8vZG9sbGhvdXNlbWNwLmdpdGh1Yi5pby9jb2xsZWN0aW9uL2NvbGxlY3Rpb24taW5kZXguanNvbic7XG4gIHByaXZhdGUgY2FjaGVEaXI6IHN0cmluZztcbiAgcHJpdmF0ZSBjYWNoZUZpbGU6IHN0cmluZztcbiAgcHJpdmF0ZSBnaXRodWJDbGllbnQ6IEdpdEh1YkNsaWVudDtcbiAgcHJpdmF0ZSBwZXJmb3JtYW5jZU1vbml0b3I6IFBlcmZvcm1hbmNlTW9uaXRvcjtcbiAgcHJpdmF0ZSBtZW1vcnlDYWNoZTogTFJVQ2FjaGU8YW55PjtcbiAgcHJpdmF0ZSBmZXRjaFByb21pc2U6IFByb21pc2U8Q29sbGVjdGlvbkluZGV4PiB8IG51bGwgPSBudWxsOyAvLyBQcmV2ZW50IGNvbmN1cnJlbnQgZmV0Y2hlc1xuXG4gIC8vIEZpbGUgb3BlcmF0aW9ucyBzZXJ2aWNlIGZvciBzZWN1cmUgZmlsZSBJL09cbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlT3BlcmF0aW9uczogSUZpbGVPcGVyYXRpb25zU2VydmljZTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBnaXRodWJDbGllbnQ6IEdpdEh1YkNsaWVudCxcbiAgICBiYXNlRGlyOiBzdHJpbmcsXG4gICAgcGVyZm9ybWFuY2VNb25pdG9yOiBQZXJmb3JtYW5jZU1vbml0b3IsXG4gICAgZmlsZU9wZXJhdGlvbnM6IElGaWxlT3BlcmF0aW9uc1NlcnZpY2VcbiAgKSB7XG4gICAgdGhpcy5naXRodWJDbGllbnQgPSBnaXRodWJDbGllbnQ7XG4gICAgdGhpcy5jYWNoZURpciA9IHBhdGguam9pbihiYXNlRGlyLCAnLmRvbGxob3VzZW1jcCcsICdjYWNoZScpO1xuICAgIHRoaXMuY2FjaGVGaWxlID0gcGF0aC5qb2luKHRoaXMuY2FjaGVEaXIsICdjb2xsZWN0aW9uLWluZGV4LWNhY2hlLmpzb24nKTtcbiAgICB0aGlzLnBlcmZvcm1hbmNlTW9uaXRvciA9IHBlcmZvcm1hbmNlTW9uaXRvcjtcbiAgICB0aGlzLmZpbGVPcGVyYXRpb25zID0gZmlsZU9wZXJhdGlvbnM7XG5cbiAgICAvLyBJbml0aWFsaXplIG1lbW9yeSBjYWNoZSBmb3IgZnJlcXVlbnRseSBhY2Nlc3NlZCBkYXRhXG4gICAgdGhpcy5tZW1vcnlDYWNoZSA9IENhY2hlRmFjdG9yeS5jcmVhdGVBUElDYWNoZSh7XG4gICAgICBtYXhTaXplOiA1MCxcbiAgICAgIG1heE1lbW9yeU1COiAxMCxcbiAgICAgIHR0bE1zOiA1ICogNjAgKiAxMDAwLCAvLyA1IG1pbnV0ZXNcbiAgICAgIG9uRXZpY3Rpb246IChrZXksIF92YWx1ZSkgPT4ge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gbWVtb3J5IGNhY2hlIGV2aWN0aW9uJywgeyBrZXkgfSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgdGhlIGNvbGxlY3Rpb24gaW5kZXggd2l0aCBwZXJmb3JtYW5jZSBvcHRpbWl6YXRpb24gYW5kIGxhenkgbG9hZGluZ1xuICAgKi9cbiAgYXN5bmMgZ2V0SW5kZXgobGF6eUxvYWQ6IGJvb2xlYW4gPSBmYWxzZSk6IFByb21pc2U8Q29sbGVjdGlvbkluZGV4PiB7XG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBtZW1vcnlCZWZvcmUgPSBwcm9jZXNzLm1lbW9yeVVzYWdlKCkuaGVhcFVzZWQ7XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIENoZWNrIG1lbW9yeSBjYWNoZSBmaXJzdCBmb3IgZmFzdGVzdCBhY2Nlc3NcbiAgICAgIGNvbnN0IG1lbW9yeUNhY2hlZCA9IHRoaXMubWVtb3J5Q2FjaGUuZ2V0KCdjb2xsZWN0aW9uLWluZGV4Jyk7XG4gICAgICBpZiAobWVtb3J5Q2FjaGVkICYmIHRoaXMuaXNWYWxpZCgpKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVXNpbmcgbWVtb3J5IGNhY2hlZCBjb2xsZWN0aW9uIGluZGV4Jyk7XG4gICAgICAgIHRoaXMucmVjb3JkUGVyZm9ybWFuY2VNZXRyaWNzKHN0YXJ0VGltZSwgbWVtb3J5QmVmb3JlLCAnbWVtb3J5LWhpdCcpO1xuICAgICAgICByZXR1cm4gbWVtb3J5Q2FjaGVkO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBDaGVjayBpZiB3ZSBoYXZlIHZhbGlkIGRpc2sgY2FjaGVkIGRhdGFcbiAgICAgIGlmICh0aGlzLmlzVmFsaWQoKSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ1VzaW5nIHZhbGlkIGRpc2sgY2FjaGVkIGNvbGxlY3Rpb24gaW5kZXgnKTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gdGhpcy5jYWNoZSEuZGF0YTtcbiAgICAgICAgdGhpcy5tZW1vcnlDYWNoZS5zZXQoJ2NvbGxlY3Rpb24taW5kZXgnLCByZXN1bHQpO1xuICAgICAgICB0aGlzLnJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWUsIG1lbW9yeUJlZm9yZSwgJ2Rpc2staGl0Jyk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIFByZXZlbnQgY29uY3VycmVudCBmZXRjaGVzXG4gICAgICBpZiAodGhpcy5mZXRjaFByb21pc2UpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdXYWl0aW5nIGZvciBvbmdvaW5nIGNvbGxlY3Rpb24gaW5kZXggZmV0Y2gnKTtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZmV0Y2hQcm9taXNlO1xuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBMYXp5IGxvYWRpbmc6IE9ubHkgZmV0Y2ggaWYgbm90IGluIGxhenkgbW9kZSBvciBhYnNvbHV0ZWx5IG5lY2Vzc2FyeVxuICAgICAgaWYgKGxhenlMb2FkICYmIHRoaXMuY2FjaGU/LmRhdGEpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdVc2luZyBzdGFsZSBjYWNoZSBpbiBsYXp5IGxvYWQgbW9kZScpO1xuICAgICAgICB0aGlzLnJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWUsIG1lbW9yeUJlZm9yZSwgJ2xhenktc3RhbGUnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2FjaGUuZGF0YTtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ3JlYXRlIGZldGNoIHByb21pc2UgdG8gcHJldmVudCBjb25jdXJyZW50IHJlcXVlc3RzXG4gICAgICB0aGlzLmZldGNoUHJvbWlzZSA9IHRoaXMuZmV0Y2hGcmVzaFdpdGhGYWxsYmFjaygpO1xuICAgICAgXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmZldGNoUHJvbWlzZTtcbiAgICAgICAgdGhpcy5tZW1vcnlDYWNoZS5zZXQoJ2NvbGxlY3Rpb24taW5kZXgnLCByZXN1bHQpO1xuICAgICAgICB0aGlzLnJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWUsIG1lbW9yeUJlZm9yZSwgJ2ZyZXNoLWZldGNoJyk7XG4gICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICB0aGlzLmZldGNoUHJvbWlzZSA9IG51bGw7XG4gICAgICB9XG4gICAgICBcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gZ2V0IGNvbGxlY3Rpb24gaW5kZXg6JywgZXJyb3IpO1xuICAgICAgXG4gICAgICAvLyBUcnkgbG9hZGluZyBmcm9tIHBlcnNpc3RlbnQgY2FjaGUgYXMgbGFzdCByZXNvcnRcbiAgICAgIGNvbnN0IHBlcnNpc3RlbnRDYWNoZSA9IGF3YWl0IHRoaXMubG9hZEZyb21EaXNrKCk7XG4gICAgICBpZiAocGVyc2lzdGVudENhY2hlKSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnVXNpbmcgcGVyc2lzdGVudCBjYWNoZSBhcyBsYXN0IHJlc29ydCcpO1xuICAgICAgICB0aGlzLmNhY2hlID0gcGVyc2lzdGVudENhY2hlO1xuICAgICAgICBjb25zdCByZXN1bHQgPSBwZXJzaXN0ZW50Q2FjaGUuZGF0YTtcbiAgICAgICAgdGhpcy5tZW1vcnlDYWNoZS5zZXQoJ2NvbGxlY3Rpb24taW5kZXgnLCByZXN1bHQpO1xuICAgICAgICB0aGlzLnJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWUsIG1lbW9yeUJlZm9yZSwgJ2Rpc2stZmFsbGJhY2snKTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgY3VycmVudCBjYWNoZSBpcyB2YWxpZCAod2l0aGluIFRUTClcbiAgICovXG4gIHByaXZhdGUgaXNWYWxpZCgpOiBib29sZWFuIHtcbiAgICBpZiAoIXRoaXMuY2FjaGUpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIHRoaXMuY2FjaGUuZmV0Y2hlZEF0LmdldFRpbWUoKTtcbiAgICByZXR1cm4gYWdlIDwgdGhpcy5UVEw7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBGZXRjaCBmcmVzaCBpbmRleCBmcm9tIEdpdEh1YiB3aXRoIGNvbmRpdGlvbmFsIHJlcXVlc3RzXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGZldGNoRnJlc2goKTogUHJvbWlzZTxDb2xsZWN0aW9uSW5kZXggfCBudWxsPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEJ1aWxkIGhlYWRlcnMgZm9yIGNvbmRpdGlvbmFsIHJlcXVlc3RcbiAgICAgIGNvbnN0IGhlYWRlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICAgICAgICdBY2NlcHQnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAgICdVc2VyLUFnZW50JzogJ0RvbGxob3VzZU1DUC8xLjAnXG4gICAgICB9O1xuICAgICAgXG4gICAgICAvLyBBZGQgY29uZGl0aW9uYWwgaGVhZGVycyBpZiB3ZSBoYXZlIGNhY2hlXG4gICAgICBpZiAodGhpcy5jYWNoZT8uZXRhZykge1xuICAgICAgICBoZWFkZXJzWydJZi1Ob25lLU1hdGNoJ10gPSB0aGlzLmNhY2hlLmV0YWc7XG4gICAgICB9XG4gICAgICBpZiAodGhpcy5jYWNoZT8ubGFzdE1vZGlmaWVkKSB7XG4gICAgICAgIGhlYWRlcnNbJ0lmLU1vZGlmaWVkLVNpbmNlJ10gPSB0aGlzLmNhY2hlLmxhc3RNb2RpZmllZDtcbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gVXNlIGZldGNoIGRpcmVjdGx5IGZvciBiZXR0ZXIgY29udHJvbCBvdmVyIGNvbmRpdGlvbmFsIHJlcXVlc3RzXG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKHRoaXMuSU5ERVhfVVJMLCB7IGhlYWRlcnMgfSk7XG4gICAgICBcbiAgICAgIC8vIDMwNCBOb3QgTW9kaWZpZWQgLSB1c2UgY2FjaGVkIGRhdGFcbiAgICAgIGlmIChyZXNwb25zZS5zdGF0dXMgPT09IDMwNCkge1xuICAgICAgICBpZiAodGhpcy5jYWNoZSkge1xuICAgICAgICAgIC8vIFVwZGF0ZSB0aW1lc3RhbXAgdG8gZXh0ZW5kIGNhY2hlIHZhbGlkaXR5XG4gICAgICAgICAgdGhpcy5jYWNoZS5mZXRjaGVkQXQgPSBuZXcgRGF0ZSgpO1xuICAgICAgICAgIGF3YWl0IHRoaXMuc2F2ZVRvRGlzayh0aGlzLmNhY2hlKTtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gaW5kZXggbm90IG1vZGlmaWVkIC0gcmVmcmVzaGVkIGNhY2hlIHRpbWVzdGFtcCcpO1xuICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlLmRhdGE7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgaWYgKCFyZXNwb25zZS5vaykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEhUVFAgJHtyZXNwb25zZS5zdGF0dXN9OiAke3Jlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIGNvbnN0IGluZGV4RGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKSBhcyBDb2xsZWN0aW9uSW5kZXg7XG4gICAgICBcbiAgICAgIC8vIFZhbGlkYXRlIHRoZSBpbmRleCBzdHJ1Y3R1cmVcbiAgICAgIGlmICghdGhpcy52YWxpZGF0ZUluZGV4U3RydWN0dXJlKGluZGV4RGF0YSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGNvbGxlY3Rpb24gaW5kZXggc3RydWN0dXJlIHJlY2VpdmVkJyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSBuZXcgY2FjaGUgZW50cnlcbiAgICAgIGNvbnN0IG5ld0NhY2hlOiBDYWNoZWRJbmRleCA9IHtcbiAgICAgICAgZGF0YTogaW5kZXhEYXRhLFxuICAgICAgICBmZXRjaGVkQXQ6IG5ldyBEYXRlKCksXG4gICAgICAgIGV0YWc6IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdldGFnJykgfHwgdW5kZWZpbmVkLFxuICAgICAgICBsYXN0TW9kaWZpZWQ6IHJlc3BvbnNlLmhlYWRlcnMuZ2V0KCdsYXN0LW1vZGlmaWVkJykgfHwgdW5kZWZpbmVkXG4gICAgICB9O1xuICAgICAgXG4gICAgICB0aGlzLmNhY2hlID0gbmV3Q2FjaGU7XG4gICAgICBcbiAgICAgIC8vIFNhdmUgdG8gcGVyc2lzdGVudCBjYWNoZSBpbiBiYWNrZ3JvdW5kXG4gICAgICB0aGlzLnNhdmVUb0Rpc2sobmV3Q2FjaGUpLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdGYWlsZWQgdG8gc2F2ZSBpbmRleCBjYWNoZSB0byBkaXNrOicsIGVycm9yKTtcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICBsb2dnZXIuZGVidWcoYEZyZXNoIGNvbGxlY3Rpb24gaW5kZXggZmV0Y2hlZCB3aXRoICR7aW5kZXhEYXRhLnRvdGFsX2VsZW1lbnRzfSBlbGVtZW50c2ApO1xuICAgICAgcmV0dXJuIGluZGV4RGF0YTtcbiAgICAgIFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBmZXRjaCBmcmVzaCBjb2xsZWN0aW9uIGluZGV4OicsIGVycm9yKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIFZhbGlkYXRlIHRoZSBzdHJ1Y3R1cmUgb2YgYSBjb2xsZWN0aW9uIGluZGV4XG4gICAqL1xuICBwcml2YXRlIHZhbGlkYXRlSW5kZXhTdHJ1Y3R1cmUoaW5kZXg6IGFueSk6IGluZGV4IGlzIENvbGxlY3Rpb25JbmRleCB7XG4gICAgcmV0dXJuIChcbiAgICAgIGluZGV4ICYmXG4gICAgICB0eXBlb2YgaW5kZXggPT09ICdvYmplY3QnICYmXG4gICAgICB0eXBlb2YgaW5kZXgudmVyc2lvbiA9PT0gJ3N0cmluZycgJiZcbiAgICAgIHR5cGVvZiBpbmRleC5nZW5lcmF0ZWQgPT09ICdzdHJpbmcnICYmXG4gICAgICB0eXBlb2YgaW5kZXgudG90YWxfZWxlbWVudHMgPT09ICdudW1iZXInICYmXG4gICAgICBpbmRleC5pbmRleCAmJlxuICAgICAgdHlwZW9mIGluZGV4LmluZGV4ID09PSAnb2JqZWN0JyAmJlxuICAgICAgaW5kZXgubWV0YWRhdGEgJiZcbiAgICAgIHR5cGVvZiBpbmRleC5tZXRhZGF0YSA9PT0gJ29iamVjdCdcbiAgICApO1xuICB9XG4gIFxuICAvKipcbiAgICogU2F2ZSBjYWNoZSB0byBwZXJzaXN0ZW50IHN0b3JhZ2VcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgc2F2ZVRvRGlzayhjYWNoZTogQ2FjaGVkSW5kZXgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5lbnN1cmVDYWNoZURpcigpO1xuXG4gICAgICBjb25zdCBjYWNoZURhdGEgPSB7XG4gICAgICAgIC4uLmNhY2hlLFxuICAgICAgICBmZXRjaGVkQXQ6IGNhY2hlLmZldGNoZWRBdC50b0lTT1N0cmluZygpIC8vIFNlcmlhbGl6ZSBkYXRlXG4gICAgICB9O1xuXG4gICAgICBjb25zdCBkYXRhID0gSlNPTi5zdHJpbmdpZnkoY2FjaGVEYXRhLCBudWxsLCAyKTtcbiAgICAgIGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMud3JpdGVGaWxlKHRoaXMuY2FjaGVGaWxlLCBkYXRhLCB7XG4gICAgICAgIHNvdXJjZTogJ0NvbGxlY3Rpb25JbmRleENhY2hlLnNhdmVUb0Rpc2snXG4gICAgICB9KTtcblxuICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGluZGV4IGNhY2hlIHNhdmVkIHRvIGRpc2snKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdGYWlsZWQgdG8gc2F2ZSBjb2xsZWN0aW9uIGluZGV4IGNhY2hlOicsIGVycm9yKTtcbiAgICAgIC8vIERvbid0IHRocm93IC0gY2FjaGUgcGVyc2lzdGVuY2UgZmFpbHVyZXMgc2hvdWxkbid0IGJyZWFrIGZ1bmN0aW9uYWxpdHlcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2FkIGNhY2hlIGZyb20gcGVyc2lzdGVudCBzdG9yYWdlXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGxvYWRGcm9tRGlzaygpOiBQcm9taXNlPENhY2hlZEluZGV4IHwgbnVsbD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBCYXNpYyBzZWN1cml0eSBjaGVjayBmb3IgcGF0aCB0cmF2ZXJzYWxcbiAgICAgIGlmICh0aGlzLmNhY2hlRmlsZS5pbmNsdWRlcygnLi4nKSB8fCB0aGlzLmNhY2hlRmlsZS5pbmNsdWRlcygnXFwwJykpIHtcbiAgICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICAgIHR5cGU6ICdQQVRIX1RSQVZFUlNBTF9BVFRFTVBUJyxcbiAgICAgICAgICBzZXZlcml0eTogJ0hJR0gnLFxuICAgICAgICAgIHNvdXJjZTogJ0NvbGxlY3Rpb25JbmRleENhY2hlLmxvYWRGcm9tRGlzaycsXG4gICAgICAgICAgZGV0YWlsczogYFBvdGVudGlhbCBwYXRoIHRyYXZlcnNhbCBhdHRlbXB0IGRldGVjdGVkOiAke3RoaXMuY2FjaGVGaWxlLnN1YnN0cmluZygwLCAxMDApfWBcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBkYXRhID0gYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5yZWFkRmlsZSh0aGlzLmNhY2hlRmlsZSwge1xuICAgICAgICBzb3VyY2U6ICdDb2xsZWN0aW9uSW5kZXhDYWNoZS5sb2FkRnJvbURpc2snXG4gICAgICB9KTtcbiAgICAgIGNvbnN0IGNhY2hlRGF0YSA9IEpTT04ucGFyc2UoZGF0YSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmNhY2hlRGF0YSxcbiAgICAgICAgZmV0Y2hlZEF0OiBuZXcgRGF0ZShjYWNoZURhdGEuZmV0Y2hlZEF0KSAvLyBEZXNlcmlhbGl6ZSBkYXRlXG4gICAgICB9O1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdGYWlsZWQgdG8gbG9hZCBjb2xsZWN0aW9uIGluZGV4IGNhY2hlIGZyb20gZGlzazonLCBlcnJvcik7XG4gICAgICB9XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBFbnN1cmUgY2FjaGUgZGlyZWN0b3J5IGV4aXN0c1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVDYWNoZURpcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5jcmVhdGVEaXJlY3RvcnkodGhpcy5jYWNoZURpcik7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGNyZWF0ZSBjYWNoZSBkaXJlY3Rvcnk6JywgZXJyb3IpO1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYXIgYWxsIGNhY2hlIGRhdGEgd2l0aCBwZXJmb3JtYW5jZSBtb25pdG9yaW5nXG4gICAqL1xuICBhc3luYyBjbGVhckNhY2hlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG5cbiAgICB0aGlzLmNhY2hlID0gbnVsbDtcbiAgICB0aGlzLm1lbW9yeUNhY2hlLmNsZWFyKCk7XG5cbiAgICAvLyBDYW5jZWwgYW55IG9uZ29pbmcgZmV0Y2hcbiAgICB0aGlzLmZldGNoUHJvbWlzZSA9IG51bGw7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5kZWxldGVGaWxlKHRoaXMuY2FjaGVGaWxlLCB1bmRlZmluZWQsIHtcbiAgICAgICAgc291cmNlOiAnQ29sbGVjdGlvbkluZGV4Q2FjaGUuY2xlYXJDYWNoZSdcbiAgICAgIH0pO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdDb2xsZWN0aW9uIGluZGV4IGNhY2hlIGNsZWFyZWQgZnJvbSBkaXNrJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBjbGVhciBjb2xsZWN0aW9uIGluZGV4IGNhY2hlOicsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoJ0NvbGxlY3Rpb24gY2FjaGUgY2xlYXJlZCcsIHtcbiAgICAgIGR1cmF0aW9uOiBEYXRlLm5vdygpIC0gc3RhcnRUaW1lLFxuICAgICAgbWVtb3J5RnJlZWQ6IHRoaXMubWVtb3J5Q2FjaGUuZ2V0TWVtb3J5VXNhZ2VNQigpXG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgY29tcHJlaGVuc2l2ZSBjYWNoZSBzdGF0aXN0aWNzIGZvciBkZWJ1Z2dpbmcgYW5kIG1vbml0b3JpbmdcbiAgICovXG4gIGdldENhY2hlU3RhdHMoKTogeyBcbiAgICBpc1ZhbGlkOiBib29sZWFuOyBcbiAgICBhZ2U6IG51bWJlcjsgXG4gICAgaGFzQ2FjaGU6IGJvb2xlYW47IFxuICAgIGVsZW1lbnRzOiBudW1iZXI7XG4gICAgbWVtb3J5Q2FjaGU6IGFueTtcbiAgICBwZXJmb3JtYW5jZU1ldHJpY3M6IGFueTtcbiAgfSB7XG4gICAgaWYgKCF0aGlzLmNhY2hlKSB7XG4gICAgICByZXR1cm4geyBcbiAgICAgICAgaXNWYWxpZDogZmFsc2UsIFxuICAgICAgICBhZ2U6IDAsIFxuICAgICAgICBoYXNDYWNoZTogZmFsc2UsIFxuICAgICAgICBlbGVtZW50czogMCxcbiAgICAgICAgbWVtb3J5Q2FjaGU6IHRoaXMubWVtb3J5Q2FjaGUuZ2V0U3RhdHMoKSxcbiAgICAgICAgcGVyZm9ybWFuY2VNZXRyaWNzOiBudWxsXG4gICAgICB9O1xuICAgIH1cbiAgICBcbiAgICBjb25zdCBhZ2UgPSBEYXRlLm5vdygpIC0gdGhpcy5jYWNoZS5mZXRjaGVkQXQuZ2V0VGltZSgpO1xuICAgIHJldHVybiB7XG4gICAgICBpc1ZhbGlkOiB0aGlzLmlzVmFsaWQoKSxcbiAgICAgIGFnZSxcbiAgICAgIGhhc0NhY2hlOiB0cnVlLFxuICAgICAgZWxlbWVudHM6IHRoaXMuY2FjaGUuZGF0YS50b3RhbF9lbGVtZW50cyxcbiAgICAgIG1lbW9yeUNhY2hlOiB0aGlzLm1lbW9yeUNhY2hlLmdldFN0YXRzKCksXG4gICAgICBwZXJmb3JtYW5jZU1ldHJpY3M6IHtcbiAgICAgICAgY2FjaGVIaXRSYXRlOiB0aGlzLmNhbGN1bGF0ZUNhY2hlSGl0UmF0ZSgpLFxuICAgICAgICBhdmVyYWdlQWNjZXNzVGltZTogdGhpcy5jYWxjdWxhdGVBdmVyYWdlQWNjZXNzVGltZSgpXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciBpbi1tZW1vcnkgY2FjaGUgc3RhdGUgd2l0aG91dCB0b3VjaGluZyBkaXNrLlxuICAgKi9cbiAgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5jYWNoZSA9IG51bGw7XG4gICAgdGhpcy5tZW1vcnlDYWNoZS5jbGVhcigpO1xuICAgIHRoaXMuZmV0Y2hQcm9taXNlID0gbnVsbDtcbiAgfVxuICBcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gUFJJVkFURSBIRUxQRVIgTUVUSE9EUyBGT1IgUEVSRk9STUFOQ0VcbiAgLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiAgXG4gIC8qKlxuICAgKiBGZXRjaCBmcmVzaCBpbmRleCB3aXRoIGNvbXByZWhlbnNpdmUgZmFsbGJhY2sgc3RyYXRlZ3lcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmV0Y2hGcmVzaFdpdGhGYWxsYmFjaygpOiBQcm9taXNlPENvbGxlY3Rpb25JbmRleD4ge1xuICAgIHRyeSB7XG4gICAgICAvLyBUcnkgdG8gZmV0Y2ggZnJlc2ggaW5kZXhcbiAgICAgIGNvbnN0IGZyZXNoSW5kZXggPSBhd2FpdCB0aGlzLmZldGNoRnJlc2goKTtcbiAgICAgIGlmIChmcmVzaEluZGV4KSB7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnQ29sbGVjdGlvbiBpbmRleCBmZXRjaGVkIHN1Y2Nlc3NmdWxseScpO1xuICAgICAgICByZXR1cm4gZnJlc2hJbmRleDtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLndhcm4oJ0ZyZXNoIGZldGNoIGZhaWxlZCwgdHJ5aW5nIGZhbGxiYWNrJywge1xuICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpXG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgLy8gRmFsbCBiYWNrIHRvIHN0YWxlIGNhY2hlIGlmIGF2YWlsYWJsZVxuICAgIGlmICh0aGlzLmNhY2hlPy5kYXRhKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ1VzaW5nIHN0YWxlIGNhY2hlZCBjb2xsZWN0aW9uIGluZGV4IGFzIGZhbGxiYWNrJyk7XG4gICAgICByZXR1cm4gdGhpcy5jYWNoZS5kYXRhO1xuICAgIH1cbiAgICBcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vIGNvbGxlY3Rpb24gaW5kZXggYXZhaWxhYmxlIC0gZnJlc2ggZmV0Y2ggZmFpbGVkIGFuZCBubyBjYWNoZSBleGlzdHMnKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJlY29yZCBwZXJmb3JtYW5jZSBtZXRyaWNzIGZvciBjYWNoZSBvcGVyYXRpb25zXG4gICAqL1xuICBwcml2YXRlIHJlY29yZFBlcmZvcm1hbmNlTWV0cmljcyhzdGFydFRpbWU6IG51bWJlciwgbWVtb3J5QmVmb3JlOiBudW1iZXIsIG9wZXJhdGlvbjogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3QgZHVyYXRpb24gPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lO1xuICAgIGNvbnN0IG1lbW9yeUFmdGVyID0gcHJvY2Vzcy5tZW1vcnlVc2FnZSgpLmhlYXBVc2VkO1xuICAgIFxuICAgIGxvZ2dlci5kZWJ1ZygnQ29sbGVjdGlvbiBjYWNoZSBvcGVyYXRpb24gY29tcGxldGVkJywge1xuICAgICAgb3BlcmF0aW9uLFxuICAgICAgZHVyYXRpb24sXG4gICAgICBtZW1vcnlVc2FnZU1COiAobWVtb3J5QWZ0ZXIgLSBtZW1vcnlCZWZvcmUpIC8gKDEwMjQgKiAxMDI0KVxuICAgIH0pO1xuICAgIFxuICAgIC8vIFJlY29yZCBjYWNoZSBwZXJmb3JtYW5jZSBtZXRyaWNzXG4gICAgdGhpcy5wZXJmb3JtYW5jZU1vbml0b3IucmVjb3JkQ2FjaGVQZXJmb3JtYW5jZSgnY29sbGVjdGlvbkluZGV4Jywge1xuICAgICAgaGl0UmF0ZTogb3BlcmF0aW9uLmluY2x1ZGVzKCdoaXQnKSA/IDEgOiAwLFxuICAgICAgYXZnSGl0VGltZTogb3BlcmF0aW9uLmluY2x1ZGVzKCdoaXQnKSA/IGR1cmF0aW9uIDogMCxcbiAgICAgIGF2Z01pc3NUaW1lOiBvcGVyYXRpb24uaW5jbHVkZXMoJ2hpdCcpID8gMCA6IGR1cmF0aW9uLFxuICAgICAgdG90YWxIaXRzOiBvcGVyYXRpb24uaW5jbHVkZXMoJ2hpdCcpID8gMSA6IDAsXG4gICAgICB0b3RhbE1pc3Nlczogb3BlcmF0aW9uLmluY2x1ZGVzKCdoaXQnKSA/IDAgOiAxLFxuICAgICAgZXZpY3Rpb25zOiAwXG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgY2FjaGUgaGl0IHJhdGUgKHBsYWNlaG9sZGVyIGZvciBmdXR1cmUgaW1wbGVtZW50YXRpb24pXG4gICAqL1xuICBwcml2YXRlIGNhbGN1bGF0ZUNhY2hlSGl0UmF0ZSgpOiBudW1iZXIge1xuICAgIC8vIFRoaXMgd291bGQgYmUgaW1wbGVtZW50ZWQgd2l0aCBhY3R1YWwgbWV0cmljcyB0cmFja2luZ1xuICAgIHJldHVybiB0aGlzLm1lbW9yeUNhY2hlLmdldFN0YXRzKCkuaGl0UmF0ZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBhdmVyYWdlIGFjY2VzcyB0aW1lIChwbGFjZWhvbGRlciBmb3IgZnV0dXJlIGltcGxlbWVudGF0aW9uKVxuICAgKi9cbiAgcHJpdmF0ZSBjYWxjdWxhdGVBdmVyYWdlQWNjZXNzVGltZSgpOiBudW1iZXIge1xuICAgIC8vIFRoaXMgd291bGQgYmUgaW1wbGVtZW50ZWQgd2l0aCBhY3R1YWwgdGltaW5nIG1ldHJpY3NcbiAgICByZXR1cm4gNTsgLy8gUGxhY2Vob2xkZXIgdmFsdWVcbiAgfVxufVxuIl19