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.

614 lines 70.6 kB
/** * High-performance LRU Cache with memory monitoring and automatic cleanup * Optimized for large-scale indexing operations with configurable memory limits */ import { SecurityMonitor } from '../security/securityMonitor.js'; import { CACHE_SIZE_ESTIMATION_CONFIG, getValidatedSampleSize } from '../config/performance-constants.js'; export class LRUCache { static hasLoggedInit = false; static logListener; static addLogListener(fn) { LRUCache.logListener = fn; return () => { LRUCache.logListener = undefined; }; } /** Reset static flags for test isolation. */ static resetStaticState() { LRUCache.hasLoggedInit = false; } name; maxSize; maxMemoryBytes; ttlMs; onEviction; onSet; sizeEstimationMode; cache = new Map(); head = null; tail = null; currentMemoryBytes = 0; nextExpiryTimestamp = Infinity; lastActivityTime = 0; // Performance counters hitCount = 0; missCount = 0; evictionCount = 0; // Constants for fast estimation - using centralized configuration static PRIMITIVE_SIZE = CACHE_SIZE_ESTIMATION_CONFIG.PRIMITIVE_SIZE; static OBJECT_BASE_OVERHEAD = CACHE_SIZE_ESTIMATION_CONFIG.OBJECT_BASE_OVERHEAD; static ARRAY_BASE_OVERHEAD = CACHE_SIZE_ESTIMATION_CONFIG.ARRAY_BASE_OVERHEAD; static FIELD_OVERHEAD = CACHE_SIZE_ESTIMATION_CONFIG.FIELD_OVERHEAD; static ELEMENT_ESTIMATE = CACHE_SIZE_ESTIMATION_CONFIG.ELEMENT_ESTIMATE; static BALANCED_SAMPLE_SIZE = getValidatedSampleSize(); constructor(options) { this.name = options.name ?? 'unnamed'; this.maxSize = options.maxSize; this.maxMemoryBytes = (options.maxMemoryMB || 50) * 1024 * 1024; // Convert MB to bytes this.ttlMs = options.ttlMs || 0; // 0 means no TTL this.onEviction = options.onEviction; this.onSet = options.onSet; this.sizeEstimationMode = options.sizeEstimationMode || 'fast'; // FIX: DMCP-SEC-006 - Audit cache initialization (logs only for the first cache constructed) if (!LRUCache.hasLoggedInit) { SecurityMonitor.logSecurityEvent({ type: 'PORTFOLIO_INITIALIZATION', severity: 'LOW', source: 'LRUCache.constructor', details: `Cache initialized with maxSize=${this.maxSize}, maxMemoryMB=${options.maxMemoryMB || 50}, ttl=${this.ttlMs}ms`, additionalData: { maxSize: this.maxSize, maxMemoryMB: options.maxMemoryMB || 50, ttlMs: this.ttlMs, sizeEstimationMode: this.sizeEstimationMode } }); LRUCache.hasLoggedInit = true; } } /** * Get value from cache with automatic cleanup */ get(key) { const node = this.cache.get(key); if (!node) { this.missCount++; return undefined; } // Check TTL if enabled if (this.ttlMs > 0 && Date.now() - node.timestamp > this.ttlMs) { this.delete(key); this.missCount++; return undefined; } // Move to front (most recently used) this.moveToFront(node); this.hitCount++; this.lastActivityTime = Date.now(); return node.value; } /** * Set value in cache with automatic eviction */ set(key, value, sizeHint) { const existingNode = this.cache.get(key); if (existingNode) { // Update existing node const oldSize = existingNode.size; existingNode.value = value; existingNode.timestamp = Date.now(); existingNode.size = sizeHint ?? this.estimateSize(value); this.currentMemoryBytes += existingNode.size - oldSize; this.moveToFront(existingNode); } else { // Create new node const newNode = { key, value, prev: null, next: null, timestamp: Date.now(), size: sizeHint ?? this.estimateSize(value) }; this.cache.set(key, newNode); this.currentMemoryBytes += newNode.size; this.addToFront(newNode); } // Track earliest expiry for deterministic TTL cleanup if (this.ttlMs > 0) { const expiry = Date.now() + this.ttlMs; if (expiry < this.nextExpiryTimestamp) { this.nextExpiryTimestamp = expiry; } } // Evict if necessary this.evictIfNecessary(); this.lastActivityTime = Date.now(); // Notify budget coordinator (if registered) if (this.onSet) { this.onSet(); } return this; } /** * Delete specific key from cache */ delete(key) { const node = this.cache.get(key); if (!node) { return false; } this.removeNode(node); this.cache.delete(key); this.currentMemoryBytes -= node.size; if (this.onEviction) { this.onEviction(key, node.value); } return true; } /** * Check if key exists in cache */ has(key) { const node = this.cache.get(key); if (!node) { return false; } // Check TTL if (this.ttlMs > 0 && Date.now() - node.timestamp > this.ttlMs) { this.delete(key); return false; } return true; } /** * Clear all entries from cache */ clear() { const entriesCleared = this.cache.size; if (this.onEviction) { for (const [key, node] of this.cache) { this.onEviction(key, node.value); } } this.cache.clear(); this.head = null; this.tail = null; this.currentMemoryBytes = 0; this.nextExpiryTimestamp = Infinity; this.evictionCount += this.cache.size; if (entriesCleared > 0) { LRUCache.logListener?.('debug', 'Clear cache', { cacheName: this.name, entriesCleared, }); // FIX: DMCP-SEC-006 - Audit cache clearing SecurityMonitor.logSecurityEvent({ type: 'MEMORY_CLEARED', severity: 'LOW', source: 'LRUCache.clear', details: `Cache cleared: ${entriesCleared} entries removed`, additionalData: { entriesCleared } }); } } /** * Get current cache size (number of entries) */ get size() { return this.cache.size; } /** * Get cache statistics */ getStats() { return { size: this.cache.size, maxSize: this.maxSize, hitCount: this.hitCount, missCount: this.missCount, evictionCount: this.evictionCount, memoryUsageMB: this.currentMemoryBytes / (1024 * 1024), hitRate: this.hitCount + this.missCount > 0 ? this.hitCount / (this.hitCount + this.missCount) : 0 }; } /** * Get all keys in access order (most recent first) * Returns an array for compatibility with Map behavior and array indexing */ keys() { const result = []; let current = this.head; while (current) { result.push(current.key); current = current.next; } // Add iterator interface to support both array and iterator usage let index = 0; const iterator = { [Symbol.iterator]: () => iterator, next: () => { if (index < result.length) { return { value: result[index++], done: false }; } return { done: true }; } }; // Merge array methods with iterator return Object.assign(result, iterator); } /** * Get all values in access order (most recent first) * Returns an array for compatibility with Map behavior and array indexing */ values() { const result = []; let current = this.head; while (current) { result.push(current.value); current = current.next; } // Add iterator interface to support both array and iterator usage let index = 0; const iterator = { [Symbol.iterator]: () => iterator, next: () => { if (index < result.length) { return { value: result[index++], done: false }; } return { done: true }; } }; // Merge array methods with iterator return Object.assign(result, iterator); } /** * Get all entries in access order (most recent first) * Returns an array for compatibility with Map behavior and array indexing */ entries() { const result = []; let current = this.head; while (current) { result.push([current.key, current.value]); current = current.next; } // Add iterator interface to support both array and iterator usage let index = 0; const iterator = { [Symbol.iterator]: () => iterator, next: () => { if (index < result.length) { return { value: result[index++], done: false }; } return { done: true }; } }; // Merge array methods with iterator return Object.assign(result, iterator); } /** * Execute a callback for each entry in the cache */ forEach(callback, thisArg) { let current = this.head; while (current) { callback.call(thisArg, current.value, current.key, this); current = current.next; } } /** * Iterator for entries (for Map compatibility) */ *[Symbol.iterator]() { let current = this.head; while (current) { yield [current.key, current.value]; current = current.next; } } /** * String tag for Map compatibility */ get [Symbol.toStringTag]() { return 'LRUCache'; } /** * Get current memory usage in MB */ getMemoryUsageMB() { return this.currentMemoryBytes / (1024 * 1024); } /** * Get current memory usage in bytes */ getMemoryUsageBytes() { return this.currentMemoryBytes; } /** * Get the cache name (set via constructor options) */ getName() { return this.name; } /** * Get the timestamp of the most recent get() hit or set() call. * Used by CacheMemoryBudget to identify the coldest (least-active) cache. * Returns 0 if the cache has never been accessed. */ getLastActivityTimestamp() { return this.lastActivityTime; } /** * Evict the least recently used entry from the cache. * Used by CacheMemoryBudget to enforce global memory limits. * @returns true if an entry was evicted, false if the cache was empty */ evictOne() { if (!this.tail) { return false; } this.evictLeastRecentlyUsed(); return true; } /** * Manually trigger cleanup of expired entries. * Recomputes nextExpiryTimestamp from remaining entries. */ cleanup() { if (this.ttlMs <= 0) { return 0; } const now = Date.now(); const keysToDelete = []; let earliestRemaining = Infinity; for (const [key, node] of this.cache) { const expiry = node.timestamp + this.ttlMs; if (now >= expiry) { keysToDelete.push(key); } else if (expiry < earliestRemaining) { earliestRemaining = expiry; } } for (const key of keysToDelete) { this.delete(key); } this.nextExpiryTimestamp = earliestRemaining; return keysToDelete.length; } // Private methods moveToFront(node) { if (node === this.head) { return; // Already at front } // Remove from current position this.removeNode(node); // Add to front this.addToFront(node); } addToFront(node) { node.prev = null; node.next = this.head; if (this.head) { this.head.prev = node; } this.head = node; if (!this.tail) { this.tail = node; } } removeNode(node) { if (node.prev) { node.prev.next = node.next; } else { this.head = node.next; } if (node.next) { node.next.prev = node.prev; } else { this.tail = node.prev; } } evictIfNecessary() { // Evict by size while (this.cache.size > this.maxSize) { this.evictLeastRecentlyUsed(); } // Evict by memory if (this.currentMemoryBytes > this.maxMemoryBytes && this.tail) { LRUCache.logListener?.('warn', 'Memory limit exceeded', { cacheName: this.name, memoryUsageMB: this.currentMemoryBytes / (1024 * 1024), maxMemoryMB: this.maxMemoryBytes / (1024 * 1024), }); while (this.currentMemoryBytes > this.maxMemoryBytes && this.tail) { this.evictLeastRecentlyUsed(); } } // Deterministic TTL cleanup: fires exactly when an entry has expired if (this.ttlMs > 0 && Date.now() >= this.nextExpiryTimestamp) { this.cleanup(); } } evictLeastRecentlyUsed() { if (!this.tail) { return; } const evicted = this.tail; this.removeNode(evicted); this.cache.delete(evicted.key); this.currentMemoryBytes -= evicted.size; this.evictionCount++; LRUCache.logListener?.('debug', 'Evict entry', { cacheName: this.name, key: evicted.key, size: evicted.size, cacheSize: this.cache.size, }); if (this.onEviction) { this.onEviction(evicted.key, evicted.value); } } /** * Estimates the memory size of a value using configurable accuracy modes: * - fast: O(1) heuristics based on type and property count (2-5x faster than JSON.stringify) * - balanced: Samples first N properties for better accuracy (moderate speed) * - accurate: Uses JSON.stringify for precise size (slowest, original behavior) */ estimateSize(value) { try { switch (this.sizeEstimationMode) { case 'fast': return this.estimateSizeFast(value); case 'balanced': return this.estimateSizeBalanced(value); case 'accurate': return this.estimateSizeAccurate(value); default: return this.estimateSizeFast(value); } } catch { return LRUCache.OBJECT_BASE_OVERHEAD; // Fallback } } /** * Fast O(1) size estimation using heuristics * Achieves 2-5x speedup over JSON.stringify with reasonable accuracy (50-200%) */ estimateSizeFast(value) { if (value === null || value === undefined) { return LRUCache.PRIMITIVE_SIZE; } const type = typeof value; if (type === 'string') { return value.length * 2; // UTF-16 characters } if (type === 'number' || type === 'boolean') { return LRUCache.PRIMITIVE_SIZE; } if (Array.isArray(value)) { // Fast estimate: base overhead + element count * average element size // Assumes average element is ~64 bytes (works well for mixed content) return LRUCache.ARRAY_BASE_OVERHEAD + value.length * LRUCache.ELEMENT_ESTIMATE; } if (type === 'object') { // Fast estimate: base overhead + key count * average field overhead // Each field assumed to take ~48 bytes (key string + value + pointer) const keyCount = Object.keys(value).length; return LRUCache.OBJECT_BASE_OVERHEAD + keyCount * LRUCache.FIELD_OVERHEAD; } return LRUCache.OBJECT_BASE_OVERHEAD; } /** * Balanced estimation: Samples first N properties for improved accuracy * Provides better accuracy than fast mode with moderate performance cost */ estimateSizeBalanced(value) { if (value === null || value === undefined) { return LRUCache.PRIMITIVE_SIZE; } const type = typeof value; if (type === 'string') { return value.length * 2; } if (type === 'number' || type === 'boolean') { return LRUCache.PRIMITIVE_SIZE; } if (Array.isArray(value)) { const sampleSize = Math.min(value.length, LRUCache.BALANCED_SAMPLE_SIZE); if (sampleSize === 0) { return LRUCache.ARRAY_BASE_OVERHEAD; } // Sample first N elements and extrapolate let sampleTotal = 0; for (let i = 0; i < sampleSize; i++) { sampleTotal += this.estimateSizeFast(value[i]); } const avgSize = sampleTotal / sampleSize; return LRUCache.ARRAY_BASE_OVERHEAD + value.length * avgSize; } if (type === 'object') { const keys = Object.keys(value); const sampleSize = Math.min(keys.length, LRUCache.BALANCED_SAMPLE_SIZE); if (sampleSize === 0) { return LRUCache.OBJECT_BASE_OVERHEAD; } // Sample first N properties and extrapolate let sampleTotal = 0; for (let i = 0; i < sampleSize; i++) { const key = keys[i]; const propValue = value[key]; // Key size + value size + pointer overhead sampleTotal += key.length * 2 + this.estimateSizeFast(propValue) + 16; } const avgFieldSize = sampleTotal / sampleSize; return LRUCache.OBJECT_BASE_OVERHEAD + keys.length * avgFieldSize; } return LRUCache.OBJECT_BASE_OVERHEAD; } /** * Accurate estimation using JSON.stringify (original behavior) * Slowest but most accurate, O(n) where n is the size of the object */ estimateSizeAccurate(value) { if (value === null || value === undefined) { return LRUCache.PRIMITIVE_SIZE; } if (typeof value === 'string') { return value.length * 2; } if (typeof value === 'number' || typeof value === 'boolean') { return LRUCache.PRIMITIVE_SIZE; } if (Array.isArray(value)) { return value.reduce((acc, item) => acc + this.estimateSizeAccurate(item), LRUCache.ARRAY_BASE_OVERHEAD); } if (typeof value === 'object') { const jsonStr = JSON.stringify(value); return jsonStr.length * 2 + LRUCache.OBJECT_BASE_OVERHEAD; } return LRUCache.OBJECT_BASE_OVERHEAD; } } /** * Factory for creating optimized LRU caches for different use cases */ export class CacheFactory { /** * Create cache optimized for search results */ static createSearchResultCache(options) { return new LRUCache({ maxSize: 100, maxMemoryMB: 10, ttlMs: 5 * 60 * 1000, // 5 minutes ...options }); } /** * Create cache optimized for index data */ static createIndexCache(options) { return new LRUCache({ maxSize: 50, maxMemoryMB: 25, ttlMs: 15 * 60 * 1000, // 15 minutes ...options }); } /** * Create cache optimized for API responses */ static createAPICache(options) { return new LRUCache({ maxSize: 200, maxMemoryMB: 5, ttlMs: 10 * 60 * 1000, // 10 minutes ...options }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTFJVQ2FjaGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2FjaGUvTFJVQ2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLE9BQU8sRUFDTCw0QkFBNEIsRUFDNUIsc0JBQXNCLEVBQ3ZCLE1BQU0sb0NBQW9DLENBQUM7QUFpQzVDLE1BQU0sT0FBTyxRQUFRO0lBQ1gsTUFBTSxDQUFDLGFBQWEsR0FBRyxLQUFLLENBQUM7SUFDN0IsTUFBTSxDQUFDLFdBQVcsQ0FBeUc7SUFFbkksTUFBTSxDQUFDLGNBQWMsQ0FBQyxFQUF5RztRQUM3SCxRQUFRLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztRQUMxQixPQUFPLEdBQUcsRUFBRSxHQUFHLFFBQVEsQ0FBQyxXQUFXLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3JELENBQUM7SUFFRCw2Q0FBNkM7SUFDN0MsTUFBTSxDQUFDLGdCQUFnQjtRQUNyQixRQUFRLENBQUMsYUFBYSxHQUFHLEtBQUssQ0FBQztJQUNqQyxDQUFDO0lBRWdCLElBQUksQ0FBUztJQUNiLE9BQU8sQ0FBUztJQUNoQixjQUFjLENBQVM7SUFDdkIsS0FBSyxDQUFTO0lBQ2QsVUFBVSxDQUFtQztJQUM3QyxLQUFLLENBQWM7SUFDbkIsa0JBQWtCLENBQXFCO0lBRWhELEtBQUssR0FBRyxJQUFJLEdBQUcsRUFBd0IsQ0FBQztJQUN4QyxJQUFJLEdBQXdCLElBQUksQ0FBQztJQUNqQyxJQUFJLEdBQXdCLElBQUksQ0FBQztJQUNqQyxrQkFBa0IsR0FBRyxDQUFDLENBQUM7SUFDdkIsbUJBQW1CLEdBQUcsUUFBUSxDQUFDO0lBQy9CLGdCQUFnQixHQUFHLENBQUMsQ0FBQztJQUU3Qix1QkFBdUI7SUFDZixRQUFRLEdBQUcsQ0FBQyxDQUFDO0lBQ2IsU0FBUyxHQUFHLENBQUMsQ0FBQztJQUNk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SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxjQUFjLENBQUksT0FBa0M7UUFDekQsT0FBTyxJQUFJLFFBQVEsQ0FBSTtZQUNyQixPQUFPLEVBQUUsR0FBRztZQUNaLFdBQVcsRUFBRSxDQUFDO1lBQ2QsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLGFBQWE7WUFDcEMsR0FBRyxPQUFPO1NBQ1gsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBIaWdoLXBlcmZvcm1hbmNlIExSVSBDYWNoZSB3aXRoIG1lbW9yeSBtb25pdG9yaW5nIGFuZCBhdXRvbWF0aWMgY2xlYW51cFxuICogT3B0aW1pemVkIGZvciBsYXJnZS1zY2FsZSBpbmRleGluZyBvcGVyYXRpb25zIHdpdGggY29uZmlndXJhYmxlIG1lbW9yeSBsaW1pdHNcbiAqL1xuXG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHtcbiAgQ0FDSEVfU0laRV9FU1RJTUFUSU9OX0NPTkZJRyxcbiAgZ2V0VmFsaWRhdGVkU2FtcGxlU2l6ZVxufSBmcm9tICcuLi9jb25maWcvcGVyZm9ybWFuY2UtY29uc3RhbnRzLmpzJztcblxuZXhwb3J0IHR5cGUgU2l6ZUVzdGltYXRpb25Nb2RlID0gJ2Zhc3QnIHwgJ2JhbGFuY2VkJyB8ICdhY2N1cmF0ZSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgTFJVQ2FjaGVPcHRpb25zIHtcbiAgbmFtZT86IHN0cmluZztcbiAgbWF4U2l6ZTogbnVtYmVyO1xuICBtYXhNZW1vcnlNQj86IG51bWJlcjtcbiAgdHRsTXM/OiBudW1iZXI7XG4gIG9uRXZpY3Rpb24/OiAoa2V5OiBzdHJpbmcsIHZhbHVlOiBhbnkpID0+IHZvaWQ7XG4gIG9uU2V0PzogKCkgPT4gdm9pZDtcbiAgc2l6ZUVzdGltYXRpb25Nb2RlPzogU2l6ZUVzdGltYXRpb25Nb2RlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENhY2hlU3RhdHMge1xuICBzaXplOiBudW1iZXI7XG4gIG1heFNpemU6IG51bWJlcjtcbiAgaGl0Q291bnQ6IG51bWJlcjtcbiAgbWlzc0NvdW50OiBudW1iZXI7XG4gIGV2aWN0aW9uQ291bnQ6IG51bWJlcjtcbiAgbWVtb3J5VXNhZ2VNQjogbnVtYmVyO1xuICBoaXRSYXRlOiBudW1iZXI7XG59XG5cbmludGVyZmFjZSBDYWNoZU5vZGU8VD4ge1xuICBrZXk6IHN0cmluZztcbiAgdmFsdWU6IFQ7XG4gIHByZXY6IENhY2hlTm9kZTxUPiB8IG51bGw7XG4gIG5leHQ6IENhY2hlTm9kZTxUPiB8IG51bGw7XG4gIHRpbWVzdGFtcDogbnVtYmVyO1xuICBzaXplOiBudW1iZXI7IC8vIEVzdGltYXRlZCBzaXplIGluIGJ5dGVzXG59XG5cbmV4cG9ydCBjbGFzcyBMUlVDYWNoZTxUPiB7XG4gIHByaXZhdGUgc3RhdGljIGhhc0xvZ2dlZEluaXQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBzdGF0aWMgbG9nTGlzdGVuZXI/OiAobGV2ZWw6ICdkZWJ1ZycgfCAnaW5mbycgfCAnd2FybicgfCAnZXJyb3InLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gdm9pZDtcblxuICBzdGF0aWMgYWRkTG9nTGlzdGVuZXIoZm46IChsZXZlbDogJ2RlYnVnJyB8ICdpbmZvJyB8ICd3YXJuJyB8ICdlcnJvcicsIG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+KSA9PiB2b2lkKTogKCkgPT4gdm9pZCB7XG4gICAgTFJVQ2FjaGUubG9nTGlzdGVuZXIgPSBmbjtcbiAgICByZXR1cm4gKCkgPT4geyBMUlVDYWNoZS5sb2dMaXN0ZW5lciA9IHVuZGVmaW5lZDsgfTtcbiAgfVxuXG4gIC8qKiBSZXNldCBzdGF0aWMgZmxhZ3MgZm9yIHRlc3QgaXNvbGF0aW9uLiAqL1xuICBzdGF0aWMgcmVzZXRTdGF0aWNTdGF0ZSgpOiB2b2lkIHtcbiAgICBMUlVDYWNoZS5oYXNMb2dnZWRJbml0ID0gZmFsc2U7XG4gIH1cblxuICBwcml2YXRlIHJlYWRvbmx5IG5hbWU6IHN0cmluZztcbiAgcHJpdmF0ZSByZWFkb25seSBtYXhTaXplOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWF4TWVtb3J5Qnl0ZXM6IG51bWJlcjtcbiAgcHJpdmF0ZSByZWFkb25seSB0dGxNczogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IG9uRXZpY3Rpb24/OiAoa2V5OiBzdHJpbmcsIHZhbHVlOiBUKSA9PiB2b2lkO1xuICBwcml2YXRlIHJlYWRvbmx5IG9uU2V0PzogKCkgPT4gdm9pZDtcbiAgcHJpdmF0ZSByZWFkb25seSBzaXplRXN0aW1hdGlvbk1vZGU6IFNpemVFc3RpbWF0aW9uTW9kZTtcblxuICBwcml2YXRlIGNhY2hlID0gbmV3IE1hcDxzdHJpbmcsIENhY2hlTm9kZTxUPj4oKTtcbiAgcHJpdmF0ZSBoZWFkOiBDYWNoZU5vZGU8VD4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSB0YWlsOiBDYWNoZU5vZGU8VD4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjdXJyZW50TWVtb3J5Qnl0ZXMgPSAwO1xuICBwcml2YXRlIG5leHRFeHBpcnlUaW1lc3RhbXAgPSBJbmZpbml0eTtcbiAgcHJpdmF0ZSBsYXN0QWN0aXZpdHlUaW1lID0gMDtcblxuICAvLyBQZXJmb3JtYW5jZSBjb3VudGVyc1xuICBwcml2YXRlIGhpdENvdW50ID0gMDtcbiAgcHJpdmF0ZSBtaXNzQ291bnQgPSAwO1xuICBwcml2YXRlIGV2aWN0aW9uQ291bnQgPSAwO1xuXG4gIC8vIENvbnN0YW50cyBmb3IgZmFzdCBlc3RpbWF0aW9uIC0gdXNpbmcgY2VudHJhbGl6ZWQgY29uZmlndXJhdGlvblxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBQUklNSVRJVkVfU0laRSA9IENBQ0hFX1NJWkVfRVNUSU1BVElPTl9DT05GSUcuUFJJTUlUSVZFX1NJWkU7XG4gIHByaXZhdGUgc3RhdGljIHJlYWRvbmx5IE9CSkVDVF9CQVNFX09WRVJIRUFEID0gQ0FDSEVfU0laRV9FU1RJTUFUSU9OX0NPTkZJRy5PQkpFQ1RfQkFTRV9PVkVSSEVBRDtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgQVJSQVlfQkFTRV9PVkVSSEVBRCA9IENBQ0hFX1NJWkVfRVNUSU1BVElPTl9DT05GSUcuQVJSQVlfQkFTRV9PVkVSSEVBRDtcbiAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgRklFTERfT1ZFUkhFQUQgPSBDQUNIRV9TSVpFX0VTVElNQVRJT05fQ09ORklHLkZJRUxEX09WRVJIRUFEO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBFTEVNRU5UX0VTVElNQVRFID0gQ0FDSEVfU0laRV9FU1RJTUFUSU9OX0NPTkZJRy5FTEVNRU5UX0VTVElNQVRFO1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBCQUxBTkNFRF9TQU1QTEVfU0laRSA9IGdldFZhbGlkYXRlZFNhbXBsZVNpemUoKTtcblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBMUlVDYWNoZU9wdGlvbnMpIHtcbiAgICB0aGlzLm5hbWUgPSBvcHRpb25zLm5hbWUgPz8gJ3VubmFtZWQnO1xuICAgIHRoaXMubWF4U2l6ZSA9IG9wdGlvbnMubWF4U2l6ZTtcbiAgICB0aGlzLm1heE1lbW9yeUJ5dGVzID0gKG9wdGlvbnMubWF4TWVtb3J5TUIgfHwgNTApICogMTAyNCAqIDEwMjQ7IC8vIENvbnZlcnQgTUIgdG8gYnl0ZXNcbiAgICB0aGlzLnR0bE1zID0gb3B0aW9ucy50dGxNcyB8fCAwOyAvLyAwIG1lYW5zIG5vIFRUTFxuICAgIHRoaXMub25FdmljdGlvbiA9IG9wdGlvbnMub25FdmljdGlvbjtcbiAgICB0aGlzLm9uU2V0ID0gb3B0aW9ucy5vblNldDtcbiAgICB0aGlzLnNpemVFc3RpbWF0aW9uTW9kZSA9IG9wdGlvbnMuc2l6ZUVzdGltYXRpb25Nb2RlIHx8ICdmYXN0JztcblxuICAgIC8vIEZJWDogRE1DUC1TRUMtMDA2IC0gQXVkaXQgY2FjaGUgaW5pdGlhbGl6YXRpb24gKGxvZ3Mgb25seSBmb3IgdGhlIGZpcnN0IGNhY2hlIGNvbnN0cnVjdGVkKVxuICAgIGlmICghTFJVQ2FjaGUuaGFzTG9nZ2VkSW5pdCkge1xuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnUE9SVEZPTElPX0lOSVRJQUxJWkFUSU9OJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICBzb3VyY2U6ICdMUlVDYWNoZS5jb25zdHJ1Y3RvcicsXG4gICAgICAgIGRldGFpbHM6IGBDYWNoZSBpbml0aWFsaXplZCB3aXRoIG1heFNpemU9JHt0aGlzLm1heFNpemV9LCBtYXhNZW1vcnlNQj0ke29wdGlvbnMubWF4TWVtb3J5TUIgfHwgNTB9LCB0dGw9JHt0aGlzLnR0bE1zfW1zYCxcbiAgICAgICAgYWRkaXRpb25hbERhdGE6IHtcbiAgICAgICAgICBtYXhTaXplOiB0aGlzLm1heFNpemUsXG4gICAgICAgICAgbWF4TWVtb3J5TUI6IG9wdGlvbnMubWF4TWVtb3J5TUIgfHwgNTAsXG4gICAgICAgICAgdHRsTXM6IHRoaXMudHRsTXMsXG4gICAgICAgICAgc2l6ZUVzdGltYXRpb25Nb2RlOiB0aGlzLnNpemVFc3RpbWF0aW9uTW9kZVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIExSVUNhY2hlLmhhc0xvZ2dlZEluaXQgPSB0cnVlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdmFsdWUgZnJvbSBjYWNoZSB3aXRoIGF1dG9tYXRpYyBjbGVhbnVwXG4gICAqL1xuICBnZXQoa2V5OiBzdHJpbmcpOiBUIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBub2RlID0gdGhpcy5jYWNoZS5nZXQoa2V5KTtcblxuICAgIGlmICghbm9kZSkge1xuICAgICAgdGhpcy5taXNzQ291bnQrKztcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgVFRMIGlmIGVuYWJsZWRcbiAgICBpZiAodGhpcy50dGxNcyA+IDAgJiYgRGF0ZS5ub3coKSAtIG5vZGUudGltZXN0YW1wID4gdGhpcy50dGxNcykge1xuICAgICAgdGhpcy5kZWxldGUoa2V5KTtcbiAgICAgIHRoaXMubWlzc0NvdW50Kys7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIC8vIE1vdmUgdG8gZnJvbnQgKG1vc3QgcmVjZW50bHkgdXNlZClcbiAgICB0aGlzLm1vdmVUb0Zyb250KG5vZGUpO1xuICAgIHRoaXMuaGl0Q291bnQrKztcbiAgICB0aGlzLmxhc3RBY3Rpdml0eVRpbWUgPSBEYXRlLm5vdygpO1xuICAgIHJldHVybiBub2RlLnZhbHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCB2YWx1ZSBpbiBjYWNoZSB3aXRoIGF1dG9tYXRpYyBldmljdGlvblxuICAgKi9cbiAgc2V0KGtleTogc3RyaW5nLCB2YWx1ZTogVCwgc2l6ZUhpbnQ/OiBudW1iZXIpOiB0aGlzIHtcbiAgICBjb25zdCBleGlzdGluZ05vZGUgPSB0aGlzLmNhY2hlLmdldChrZXkpO1xuXG4gICAgaWYgKGV4aXN0aW5nTm9kZSkge1xuICAgICAgLy8gVXBkYXRlIGV4aXN0aW5nIG5vZGVcbiAgICAgIGNvbnN0IG9sZFNpemUgPSBleGlzdGluZ05vZGUuc2l6ZTtcbiAgICAgIGV4aXN0aW5nTm9kZS52YWx1ZSA9IHZhbHVlO1xuICAgICAgZXhpc3RpbmdOb2RlLnRpbWVzdGFtcCA9IERhdGUubm93KCk7XG4gICAgICBleGlzdGluZ05vZGUuc2l6ZSA9IHNpemVIaW50ID8/IHRoaXMuZXN0aW1hdGVTaXplKHZhbHVlKTtcblxuICAgICAgdGhpcy5jdXJyZW50TWVtb3J5Qnl0ZXMgKz0gZXhpc3RpbmdOb2RlLnNpemUgLSBvbGRTaXplO1xuICAgICAgdGhpcy5tb3ZlVG9Gcm9udChleGlzdGluZ05vZGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBDcmVhdGUgbmV3IG5vZGVcbiAgICAgIGNvbnN0IG5ld05vZGU6IENhY2hlTm9kZTxUPiA9IHtcbiAgICAgICAga2V5LFxuICAgICAgICB2YWx1ZSxcbiAgICAgICAgcHJldjogbnVsbCxcbiAgICAgICAgbmV4dDogbnVsbCxcbiAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLFxuICAgICAgICBzaXplOiBzaXplSGludCA/PyB0aGlzLmVzdGltYXRlU2l6ZSh2YWx1ZSlcbiAgICAgIH07XG5cbiAgICAgIHRoaXMuY2FjaGUuc2V0KGtleSwgbmV3Tm9kZSk7XG4gICAgICB0aGlzLmN1cnJlbnRNZW1vcnlCeXRlcyArPSBuZXdOb2RlLnNpemU7XG4gICAgICB0aGlzLmFkZFRvRnJvbnQobmV3Tm9kZSk7XG4gICAgfVxuXG4gICAgLy8gVHJhY2sgZWFybGllc3QgZXhwaXJ5IGZvciBkZXRlcm1pbmlzdGljIFRUTCBjbGVhbnVwXG4gICAgaWYgKHRoaXMudHRsTXMgPiAwKSB7XG4gICAgICBjb25zdCBleHBpcnkgPSBEYXRlLm5vdygpICsgdGhpcy50dGxNcztcbiAgICAgIGlmIChleHBpcnkgPCB0aGlzLm5leHRFeHBpcnlUaW1lc3RhbXApIHtcbiAgICAgICAgdGhpcy5uZXh0R