@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.
179 lines • 22.2 kB
JavaScript
/**
* Utility class for efficient file discovery operations
* Provides optimized file search with caching and security
*
* IMPLEMENTATION (PR #503 - PR #496 Recommendation):
* 1. PERFORMANCE: Single readdir operation instead of multiple file checks
* 2. PERFORMANCE: 5-second cache TTL for repeated searches
* 3. SECURITY: Unicode normalization for all search inputs
* 4. MEMORY: Efficient cache management with 100 entry limit
*
* This addresses the PR #496 review recommendation to extract file discovery
* logic to a reusable utility class for better performance and maintainability.
*/
import * as path from 'path';
import { logger } from './logger.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
import { FileOperationsService } from '../services/FileOperationsService.js';
import { FileLockManager } from '../security/fileLockManager.js';
// Singleton file operations service for static methods
let fileOperationsService = null;
function getFileOperationsService() {
if (!fileOperationsService) {
fileOperationsService = new FileOperationsService(new FileLockManager());
}
return fileOperationsService;
}
export class FileDiscoveryUtil {
static CACHE_TTL = 5000; // 5 seconds cache TTL
static cache = new Map();
/**
* Find files in a directory with optimized search
* Uses single readdir operation and filters results
*/
static async findFiles(directory, searchName, options = {}) {
const { extensions = ['.json', '.yaml', '.yml', '.md'], partialMatch = true, maxResults = 10, cacheResults = true } = options;
// Normalize search name for security
const normalizedSearch = UnicodeValidator.normalize(searchName);
if (!normalizedSearch.isValid) {
logger.warn('Invalid Unicode in search name', {
issues: normalizedSearch.detectedIssues
});
return [];
}
const safeName = normalizedSearch.normalizedContent.toLowerCase();
// Check cache if enabled
const cacheKey = `${directory}:${safeName}:${JSON.stringify(options)}`;
if (cacheResults) {
const cached = this.getCached(cacheKey);
if (cached) {
logger.debug('File search cache hit', { directory, searchName });
return cached;
}
}
try {
// Single readdir operation for efficiency
const fileOps = getFileOperationsService();
const files = await fileOps.listDirectory(directory);
// Build search patterns
const searchPatterns = this.buildSearchPatterns(safeName, extensions);
// Filter files efficiently
const matches = [];
for (const file of files) {
const fileLower = file.toLowerCase();
// Check each pattern
for (const pattern of searchPatterns) {
if (this.matchesPattern(fileLower, pattern, partialMatch)) {
const fullPath = path.join(directory, file);
matches.push(fullPath);
if (matches.length >= maxResults) {
break;
}
}
}
if (matches.length >= maxResults) {
break;
}
}
// Cache results if enabled
if (cacheResults && matches.length > 0) {
this.setCached(cacheKey, matches);
}
// Log file discovery for monitoring
if (matches.length > 0) {
logger.debug('Files discovered', {
directory,
searchName,
count: matches.length
});
}
return matches;
}
catch (error) {
logger.error('File discovery failed', {
directory,
searchName,
error: error instanceof Error ? error.message : String(error)
});
return [];
}
}
/**
* Build search patterns for different file extensions
*/
static buildSearchPatterns(baseName, extensions) {
const patterns = [baseName];
// Add extension variations
for (const ext of extensions) {
patterns.push(`${baseName}${ext}`);
}
// Add common variations
const nameWithoutExtension = baseName.replace(/\.[^.]+$/, '');
if (nameWithoutExtension !== baseName) {
patterns.push(nameWithoutExtension);
for (const ext of extensions) {
patterns.push(`${nameWithoutExtension}${ext}`);
}
}
return patterns;
}
/**
* Check if filename matches pattern
*/
static matchesPattern(filename, pattern, partialMatch) {
if (partialMatch) {
return filename.includes(pattern);
}
return filename === pattern;
}
/**
* Get cached results if still valid
*/
static getCached(key) {
const entry = this.cache.get(key);
if (!entry)
return null;
const age = Date.now() - entry.timestamp;
if (age > this.CACHE_TTL) {
this.cache.delete(key);
return null;
}
return entry.files;
}
/**
* Cache results with timestamp
*/
static setCached(key, files) {
// Limit cache size to prevent memory issues
if (this.cache.size > 100) {
// Remove oldest entries
const entries = Array.from(this.cache.entries());
entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
for (let i = 0; i < 50; i++) {
this.cache.delete(entries[i][0]);
}
}
this.cache.set(key, {
files,
timestamp: Date.now()
});
}
/**
* Clear the cache
*/
static clearCache() {
this.cache.clear();
logger.debug('File discovery cache cleared');
}
/**
* Find a single file (convenience method)
*/
static async findFile(directory, searchName, options) {
const files = await this.findFiles(directory, searchName, {
...options,
maxResults: 1
});
return files.length > 0 ? files[0] : null;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmlsZURpc2NvdmVyeVV0aWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvRmlsZURpc2NvdmVyeVV0aWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7OztHQVlHO0FBRUgsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUM5RSxPQUFPLEVBQTBCLHFCQUFxQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDckcsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRWpFLHVEQUF1RDtBQUN2RCxJQUFJLHFCQUFxQixHQUFrQyxJQUFJLENBQUM7QUFFaEUsU0FBUyx3QkFBd0I7SUFDL0IsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDM0IscUJBQXFCLEdBQUcsSUFBSSxxQkFBcUIsQ0FBQyxJQUFJLGVBQWUsRUFBRSxDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUNELE9BQU8scUJBQXFCLENBQUM7QUFDL0IsQ0FBQztBQWNELE1BQU0sT0FBTyxpQkFBaUI7SUFDcEIsTUFBTSxDQUFVLFNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQyxzQkFBc0I7SUFDeEQsTUFBTSxDQUFDLEtBQUssR0FBRyxJQUFJLEdBQUcsRUFBc0IsQ0FBQztJQUVyRDs7O09BR0c7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FDcEIsU0FBaUIsRUFDakIsVUFBa0IsRUFDbEIsVUFBNkIsRUFBRTtRQUUvQixNQUFNLEVBQ0osVUFBVSxHQUFHLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsS0FBSyxDQUFDLEVBQzlDLFlBQVksR0FBRyxJQUFJLEVBQ25CLFVBQVUsR0FBRyxFQUFFLEVBQ2YsWUFBWSxHQUFHLElBQUks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QUVELElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRTtZQUNsQixLQUFLO1lBQ0wsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7U0FDdEIsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFVBQVU7UUFDZixJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ25CLE1BQU0sQ0FBQyxLQUFLLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUMvQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLFFBQVEsQ0FDbkIsU0FBaUIsRUFDakIsVUFBa0IsRUFDbEIsT0FBMkI7UUFFM0IsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsU0FBUyxDQUFDLFNBQVMsRUFBRSxVQUFVLEVBQUU7WUFDeEQsR0FBRyxPQUFPO1lBQ1YsVUFBVSxFQUFFLENBQUM7U0FDZCxDQUFDLENBQUM7UUFDSCxPQUFPLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUM1QyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBVdGlsaXR5IGNsYXNzIGZvciBlZmZpY2llbnQgZmlsZSBkaXNjb3Zlcnkgb3BlcmF0aW9uc1xuICogUHJvdmlkZXMgb3B0aW1pemVkIGZpbGUgc2VhcmNoIHdpdGggY2FjaGluZyBhbmQgc2VjdXJpdHlcbiAqIFxuICogSU1QTEVNRU5UQVRJT04gKFBSICM1MDMgLSBQUiAjNDk2IFJlY29tbWVuZGF0aW9uKTpcbiAqIDEuIFBFUkZPUk1BTkNFOiBTaW5nbGUgcmVhZGRpciBvcGVyYXRpb24gaW5zdGVhZCBvZiBtdWx0aXBsZSBmaWxlIGNoZWNrc1xuICogMi4gUEVSRk9STUFOQ0U6IDUtc2Vjb25kIGNhY2hlIFRUTCBmb3IgcmVwZWF0ZWQgc2VhcmNoZXNcbiAqIDMuIFNFQ1VSSVRZOiBVbmljb2RlIG5vcm1hbGl6YXRpb24gZm9yIGFsbCBzZWFyY2ggaW5wdXRzXG4gKiA0LiBNRU1PUlk6IEVmZmljaWVudCBjYWNoZSBtYW5hZ2VtZW50IHdpdGggMTAwIGVudHJ5IGxpbWl0XG4gKiBcbiAqIFRoaXMgYWRkcmVzc2VzIHRoZSBQUiAjNDk2IHJldmlldyByZWNvbW1lbmRhdGlvbiB0byBleHRyYWN0IGZpbGUgZGlzY292ZXJ5XG4gKiBsb2dpYyB0byBhIHJldXNhYmxlIHV0aWxpdHkgY2xhc3MgZm9yIGJldHRlciBwZXJmb3JtYW5jZSBhbmQgbWFpbnRhaW5hYmlsaXR5LlxuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuL2xvZ2dlci5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IElGaWxlT3BlcmF0aW9uc1NlcnZpY2UsIEZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5pbXBvcnQgeyBGaWxlTG9ja01hbmFnZXIgfSBmcm9tICcuLi9zZWN1cml0eS9maWxlTG9ja01hbmFnZXIuanMnO1xuXG4vLyBTaW5nbGV0b24gZmlsZSBvcGVyYXRpb25zIHNlcnZpY2UgZm9yIHN0YXRpYyBtZXRob2RzXG5sZXQgZmlsZU9wZXJhdGlvbnNTZXJ2aWNlOiBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlIHwgbnVsbCA9IG51bGw7XG5cbmZ1bmN0aW9uIGdldEZpbGVPcGVyYXRpb25zU2VydmljZSgpOiBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlIHtcbiAgaWYgKCFmaWxlT3BlcmF0aW9uc1NlcnZpY2UpIHtcbiAgICBmaWxlT3BlcmF0aW9uc1NlcnZpY2UgPSBuZXcgRmlsZU9wZXJhdGlvbnNTZXJ2aWNlKG5ldyBGaWxlTG9ja01hbmFnZXIoKSk7XG4gIH1cbiAgcmV0dXJuIGZpbGVPcGVyYXRpb25zU2VydmljZTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBGaWxlU2VhcmNoT3B0aW9ucyB7XG4gIGV4dGVuc2lvbnM/OiBzdHJpbmdbXTtcbiAgcGFydGlhbE1hdGNoPzogYm9vbGVhbjtcbiAgbWF4UmVzdWx0cz86IG51bWJlcjtcbiAgY2FjaGVSZXN1bHRzPzogYm9vbGVhbjtcbn1cblxuaW50ZXJmYWNlIENhY2hlRW50cnkge1xuICBmaWxlczogc3RyaW5nW107XG4gIHRpbWVzdGFtcDogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgRmlsZURpc2NvdmVyeVV0aWwge1xuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBDQUNIRV9UVEwgPSA1MDAwOyAvLyA1IHNlY29uZHMgY2FjaGUgVFRMXG4gIHByaXZhdGUgc3RhdGljIGNhY2hlID0gbmV3IE1hcDxzdHJpbmcsIENhY2hlRW50cnk+KCk7XG4gIFxuICAvKipcbiAgICogRmluZCBmaWxlcyBpbiBhIGRpcmVjdG9yeSB3aXRoIG9wdGltaXplZCBzZWFyY2hcbiAgICogVXNlcyBzaW5nbGUgcmVhZGRpciBvcGVyYXRpb24gYW5kIGZpbHRlcnMgcmVzdWx0c1xuICAgKi9cbiAgc3RhdGljIGFzeW5jIGZpbmRGaWxlcyhcbiAgICBkaXJlY3Rvcnk6IHN0cmluZyxcbiAgICBzZWFyY2hOYW1lOiBzdHJpbmcsXG4gICAgb3B0aW9uczogRmlsZVNlYXJjaE9wdGlvbnMgPSB7fVxuICApOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gICAgY29uc3Qge1xuICAgICAgZXh0ZW5zaW9ucyA9IFsnLmpzb24nLCAnLnlhbWwnLCAnLnltbCcsICcubWQnXSxcbiAgICAgIHBhcnRpYWxNYXRjaCA9IHRydWUsXG4gICAgICBtYXhSZXN1bHRzID0gMTAsXG4gICAgICBjYWNoZVJlc3VsdHMgPSB0cnVlXG4gICAgfSA9IG9wdGlvbnM7XG4gICAgXG4gICAgLy8gTm9ybWFsaXplIHNlYXJjaCBuYW1lIGZvciBzZWN1cml0eVxuICAgIGNvbnN0IG5vcm1hbGl6ZWRTZWFyY2ggPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShzZWFyY2hOYW1lKTtcbiAgICBpZiAoIW5vcm1hbGl6ZWRTZWFyY2guaXNWYWxpZCkge1xuICAgICAgbG9nZ2VyLndhcm4oJ0ludmFsaWQgVW5pY29kZSBpbiBzZWFyY2ggbmFtZScsIHtcbiAgICAgICAgaXNzdWVzOiBub3JtYWxpemVkU2VhcmNoLmRldGVjdGVkSXNzdWVzXG4gICAgICB9KTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgY29uc3Qgc2FmZU5hbWUgPSBub3JtYWxpemVkU2VhcmNoLm5vcm1hbGl6ZWRDb250ZW50LnRvTG93ZXJDYXNlKCk7XG4gICAgXG4gICAgLy8gQ2hlY2sgY2FjaGUgaWYgZW5hYmxlZFxuICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7ZGlyZWN0b3J5fToke3NhZmVOYW1lfToke0pTT04uc3RyaW5naWZ5KG9wdGlvbnMpfWA7XG4gICAgaWYgKGNhY2hlUmVzdWx0cykge1xuICAgICAgY29uc3QgY2FjaGVkID0gdGhpcy5nZXRDYWNoZWQoY2FjaGVLZXkpO1xuICAgICAgaWYgKGNhY2hlZCkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZpbGUgc2VhcmNoIGNhY2hlIGhpdCcsIHsgZGlyZWN0b3J5LCBzZWFyY2hOYW1lIH0pO1xuICAgICAgICByZXR1cm4gY2FjaGVkO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICB0cnkge1xuICAgICAgLy8gU2luZ2xlIHJlYWRkaXIgb3BlcmF0aW9uIGZvciBlZmZpY2llbmN5XG4gICAgICBjb25zdCBmaWxlT3BzID0gZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk7XG4gICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGZpbGVPcHMubGlzdERpcmVjdG9yeShkaXJlY3RvcnkpO1xuICAgICAgXG4gICAgICAvLyBCdWlsZCBzZWFyY2ggcGF0dGVybnNcbiAgICAgIGNvbnN0IHNlYXJjaFBhdHRlcm5zID0gdGhpcy5idWlsZFNlYXJjaFBhdHRlcm5zKHNhZmVOYW1lLCBleHRlbnNpb25zKTtcbiAgICAgIFxuICAgICAgLy8gRmlsdGVyIGZpbGVzIGVmZmljaWVudGx5XG4gICAgICBjb25zdCBtYXRjaGVzOiBzdHJpbmdbXSA9IFtdO1xuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICAgIGNvbnN0IGZpbGVMb3dlciA9IGZpbGUudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgXG4gICAgICAgIC8vIENoZWNrIGVhY2ggcGF0dGVyblxuICAgICAgICBmb3IgKGNvbnN0IHBhdHRlcm4gb2Ygc2VhcmNoUGF0dGVybnMpIHtcbiAgICAgICAgICBpZiAodGhpcy5tYXRjaGVzUGF0dGVybihmaWxlTG93ZXIsIHBhdHRlcm4sIHBhcnRpYWxNYXRjaCkpIHtcbiAgICAgICAgICAgIGNvbnN0IGZ1bGxQYXRoID0gcGF0aC5qb2luKGRpcmVjdG9yeSwgZmlsZSk7XG4gICAgICAgICAgICBtYXRjaGVzLnB1c2goZnVsbFBhdGgpO1xuICAgICAgICAgICAgXG4gICAgICAgICAgICBpZiAobWF0Y2hlcy5sZW5ndGggPj0gbWF4UmVzdWx0cykge1xuICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgXG4gICAgICAgIGlmIChtYXRjaGVzLmxlbmd0aCA+PSBtYXhSZXN1bHRzKSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIFxuICAgICAgLy8gQ2FjaGUgcmVzdWx0cyBpZiBlbmFibGVkXG4gICAgICBpZiAoY2FjaGVSZXN1bHRzICYmIG1hdGNoZXMubGVuZ3RoID4gMCkge1xuICAgICAgICB0aGlzLnNldENhY2hlZChjYWNoZUtleSwgbWF0Y2hlcyk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIExvZyBmaWxlIGRpc2NvdmVyeSBmb3IgbW9uaXRvcmluZ1xuICAgICAgaWYgKG1hdGNoZXMubGVuZ3RoID4gMCkge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ0ZpbGVzIGRpc2NvdmVyZWQnLCB7XG4gICAgICAgICAgZGlyZWN0b3J5LFxuICAgICAgICAgIHNlYXJjaE5hbWUsXG4gICAgICAgICAgY291bnQ6IG1hdGNoZXMubGVuZ3RoXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgXG4gICAgICByZXR1cm4gbWF0Y2hlcztcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGaWxlIGRpc2NvdmVyeSBmYWlsZWQnLCB7XG4gICAgICAgIGRpcmVjdG9yeSxcbiAgICAgICAgc2VhcmNoTmFtZSxcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgfSk7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQnVpbGQgc2VhcmNoIHBhdHRlcm5zIGZvciBkaWZmZXJlbnQgZmlsZSBleHRlbnNpb25zXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBidWlsZFNlYXJjaFBhdHRlcm5zKGJhc2VOYW1lOiBzdHJpbmcsIGV4dGVuc2lvbnM6IHN0cmluZ1tdKTogc3RyaW5nW10ge1xuICAgIGNvbnN0IHBhdHRlcm5zOiBzdHJpbmdbXSA9IFtiYXNlTmFtZV07XG4gICAgXG4gICAgLy8gQWRkIGV4dGVuc2lvbiB2YXJpYXRpb25zXG4gICAgZm9yIChjb25zdCBleHQgb2YgZXh0ZW5zaW9ucykge1xuICAgICAgcGF0dGVybnMucHVzaChgJHtiYXNlTmFtZX0ke2V4dH1gKTtcbiAgICB9XG4gICAgXG4gICAgLy8gQWRkIGNvbW1vbiB2YXJpYXRpb25zXG4gICAgY29uc3QgbmFtZVdpdGhvdXRFeHRlbnNpb24gPSBiYXNlTmFtZS5yZXBsYWNlKC9cXC5bXi5dKyQvLCAnJyk7XG4gICAgaWYgKG5hbWVXaXRob3V0RXh0ZW5zaW9uICE9PSBiYXNlTmFtZSkge1xuICAgICAgcGF0dGVybnMucHVzaChuYW1lV2l0aG91dEV4dGVuc2lvbik7XG4gICAgICBmb3IgKGNvbnN0IGV4dCBvZiBleHRlbnNpb25zKSB7XG4gICAgICAgIHBhdHRlcm5zLnB1c2goYCR7bmFtZVdpdGhvdXRFeHRlbnNpb259JHtleHR9YCk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBwYXR0ZXJucztcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIGZpbGVuYW1lIG1hdGNoZXMgcGF0dGVyblxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgbWF0Y2hlc1BhdHRlcm4oZmlsZW5hbWU6IHN0cmluZywgcGF0dGVybjogc3RyaW5nLCBwYXJ0aWFsTWF0Y2g6IGJvb2xlYW4pOiBib29sZWFuIHtcbiAgICBpZiAocGFydGlhbE1hdGNoKSB7XG4gICAgICByZXR1cm4gZmlsZW5hbWUuaW5jbHVkZXMocGF0dGVybik7XG4gICAgfVxuICAgIHJldHVybiBmaWxlbmFtZSA9PT0gcGF0dGVybjtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBjYWNoZWQgcmVzdWx0cyBpZiBzdGlsbCB2YWxpZFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZ2V0Q2FjaGVkKGtleTogc3RyaW5nKTogc3RyaW5nW10gfCBudWxsIHtcbiAgICBjb25zdCBlbnRyeSA9IHRoaXMuY2FjaGUuZ2V0KGtleSk7XG4gICAgaWYgKCFlbnRyeSkgcmV0dXJuIG51bGw7XG4gICAgXG4gICAgY29uc3QgYWdlID0gRGF0ZS5ub3coKSAtIGVudHJ5LnRpbWVzdGFtcDtcbiAgICBpZiAoYWdlID4gdGhpcy5DQUNIRV9UVEwpIHtcbiAgICAgIHRoaXMuY2FjaGUuZGVsZXRlKGtleSk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIGVudHJ5LmZpbGVzO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2FjaGUgcmVzdWx0cyB3aXRoIHRpbWVzdGFtcFxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgc2V0Q2FjaGVkKGtleTogc3RyaW5nLCBmaWxlczogc3RyaW5nW10pOiB2b2lkIHtcbiAgICAvLyBMaW1pdCBjYWNoZSBzaXplIHRvIHByZXZlbnQgbWVtb3J5IGlzc3Vlc1xuICAgIGlmICh0aGlzLmNhY2hlLnNpemUgPiAxMDApIHtcbiAgICAgIC8vIFJlbW92ZSBvbGRlc3QgZW50cmllc1xuICAgICAgY29uc3QgZW50cmllcyA9IEFycmF5LmZyb20odGhpcy5jYWNoZS5lbnRyaWVzKCkpO1xuICAgICAgZW50cmllcy5zb3J0KChhLCBiKSA9PiBhWzFdLnRpbWVzdGFtcCAtIGJbMV0udGltZXN0YW1wKTtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgNTA7IGkrKykge1xuICAgICAgICB0aGlzLmNhY2hlLmRlbGV0ZShlbnRyaWVzW2ldWzBdKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgdGhpcy5jYWNoZS5zZXQoa2V5LCB7XG4gICAgICBmaWxlcyxcbiAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYXIgdGhlIGNhY2hlXG4gICAqL1xuICBzdGF0aWMgY2xlYXJDYWNoZSgpOiB2b2lkIHtcbiAgICB0aGlzLmNhY2hlLmNsZWFyKCk7XG4gICAgbG9nZ2VyLmRlYnVnKCdGaWxlIGRpc2NvdmVyeSBjYWNoZSBjbGVhcmVkJyk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBGaW5kIGEgc2luZ2xlIGZpbGUgKGNvbnZlbmllbmNlIG1ldGhvZClcbiAgICovXG4gIHN0YXRpYyBhc3luYyBmaW5kRmlsZShcbiAgICBkaXJlY3Rvcnk6IHN0cmluZyxcbiAgICBzZWFyY2hOYW1lOiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IEZpbGVTZWFyY2hPcHRpb25zXG4gICk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgdGhpcy5maW5kRmlsZXMoZGlyZWN0b3J5LCBzZWFyY2hOYW1lLCB7XG4gICAgICAuLi5vcHRpb25zLFxuICAgICAgbWF4UmVzdWx0czogMVxuICAgIH0pO1xuICAgIHJldHVybiBmaWxlcy5sZW5ndGggPiAwID8gZmlsZXNbMF0gOiBudWxsO1xuICB9XG59Il19