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.

179 lines 22.2 kB
/** * 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