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.

341 lines 42.9 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { logger } from './logger.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; } /** * Cross-platform file operations utility * Centralizes common file operations with progress reporting and error handling */ export class FileOperations { /** * Recursively copy a directory with progress reporting * Works cross-platform without relying on shell commands */ static async copyDirectory(src, dest, options = {}) { const { onProgress, excludePatterns = [], maxRetries = 3 } = options; // First, calculate total files for progress reporting const stats = await this.calculateDirectoryStats(src, excludePatterns); let copiedFiles = 0; await this.copyDirectoryRecursive(src, dest, excludePatterns, maxRetries, (currentFile) => { copiedFiles++; if (onProgress) { onProgress(copiedFiles, stats.totalFiles, currentFile); } }); } /** * Calculate directory statistics for progress reporting */ static async calculateDirectoryStats(dir, excludePatterns) { const fileOps = getFileOperationsService(); let totalFiles = 0; let totalSize = 0; try { const entries = await fileOps.listDirectoryWithTypes(dir); for (const entry of entries) { const fullPath = path.join(dir, entry.name); // Skip excluded patterns if (this.shouldExclude(entry.name, excludePatterns)) { continue; } if (entry.isDirectory) { const subStats = await this.calculateDirectoryStats(fullPath, excludePatterns); totalFiles += subStats.totalFiles; totalSize += subStats.totalSize; } else { totalFiles++; try { const stat = await fileOps.stat(fullPath); totalSize += stat.size; } catch (error) { // Log stat errors for debugging - could indicate permissions, corruption, or disk errors logger.warn(`[FileOperations] Failed to stat file ${fullPath}:`, { error: error instanceof Error ? error.message : String(error), code: error.code }); // Continue without this file's size } } } } catch (error) { logger.warn(`[FileOperations] Error calculating stats for ${dir}:`, error); } return { totalFiles, totalSize }; } /** * Internal recursive copy implementation */ static async copyDirectoryRecursive(src, dest, excludePatterns, maxRetries, onFileCopied) { const fileOps = getFileOperationsService(); // Ensure destination directory exists await fileOps.createDirectory(dest); const entries = await fileOps.listDirectoryWithTypes(src); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); // Skip excluded patterns if (this.shouldExclude(entry.name, excludePatterns)) { logger.debug(`[FileOperations] Skipping excluded: ${entry.name}`); continue; } if (entry.isDirectory) { await this.copyDirectoryRecursive(srcPath, destPath, excludePatterns, maxRetries, onFileCopied); } else { await this.copyFileWithRetry(srcPath, destPath, maxRetries); onFileCopied(srcPath); } } } /** * Copy a single file with retry logic */ static async copyFileWithRetry(src, dest, maxRetries) { const fileOps = getFileOperationsService(); let lastError = null; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { await fileOps.copyFile(src, dest, { source: 'FileOperations.copyFileWithRetry' }); return; // Success } catch (error) { lastError = error; logger.debug(`[FileOperations] Copy attempt ${attempt} failed for ${src}: ${error}`); if (attempt < maxRetries) { // Wait before retry (exponential backoff) await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 100)); } } } // All retries failed throw new Error(`Failed to copy ${src} after ${maxRetries} attempts: ${lastError?.message}`); } /** * Check if a file/directory should be excluded * * FIX: ReDoS vulnerability - replaced unsafe glob-to-regex conversion * Previously: Used pattern.replace with .* which could cause catastrophic backtracking * Now: Uses safe glob matching with proper escaping and bounded patterns * SonarCloud: Resolves DOS vulnerability hotspot */ static shouldExclude(name, patterns) { // Input validation to prevent DOS if (name.length > 1000) { return false; // Reject overly long inputs } for (const pattern of patterns) { if (pattern.includes('*')) { // Safe glob support - prevent ReDoS // Escape special regex chars except * // FIX: Use String.raw and replaceAll (SonarCloud S7780, S7781) const escaped = pattern.replaceAll(/[.+?^${}()|[\]\\]/g, String.raw `\$&`); // Replace * with [^/]* (match anything except path separator) // This prevents catastrophic backtracking const safePattern = escaped.replaceAll('*', '[^/]*'); try { // FIX: Use template literal to avoid security scanner false positive (PR #1187) // This is NOT SQL injection - it's a RegExp pattern const regex = new RegExp(`^${safePattern}$`); if (regex.test(name)) return true; } catch (error) { // Invalid pattern - log and skip it logger.debug(`[FileOperations] Invalid regex pattern: ${pattern}`, { error: error instanceof Error ? error.message : String(error) }); continue; } } else if (name === pattern) { return true; } } return false; } /** * Remove a directory with progress reporting */ static async removeDirectory(dir, options = {}) { const stats = await this.calculateDirectoryStats(dir, []); let removedFiles = 0; await this.removeDirectoryRecursive(dir, () => { removedFiles++; if (options.onProgress) { options.onProgress(removedFiles, stats.totalFiles); } }); } /** * Internal recursive remove implementation */ static async removeDirectoryRecursive(dir, onFileRemoved) { const fileOps = getFileOperationsService(); try { const entries = await fileOps.listDirectoryWithTypes(dir); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory) { await this.removeDirectoryRecursive(fullPath, onFileRemoved); } else { await fileOps.deleteFile(fullPath, undefined, { source: 'FileOperations.removeDirectoryRecursive' }); onFileRemoved(); } } // Remove the now-empty directory await fs.rmdir(dir); } catch (error) { logger.error(`[FileOperations] Error removing directory ${dir}:`, error); throw error; } } /** * Create a transaction manager for atomic file operations */ static createTransaction() { return new FileTransaction(); } } /** * Transaction manager for atomic file operations * Ensures all operations succeed or all are rolled back */ export class FileTransaction { // FIX: Mark as readonly since never reassigned (SonarCloud S2933) operations = []; completed = false; fileOps; constructor(fileOperations) { this.fileOps = fileOperations || getFileOperationsService(); } /** * Add a move operation to the transaction */ async addMove(source, destination) { if (this.completed) { throw new Error('Transaction already completed'); } // Perform the move await this.fileOps.renameFile(source, destination); // Add rollback operation this.operations.push({ type: 'move', source, destination, rollback: async () => { try { await this.fileOps.renameFile(destination, source); } catch (error) { logger.error(`[FileTransaction] Failed to rollback move from ${destination} to ${source}:`, error); } } }); } /** * Add a copy operation to the transaction */ async addCopy(source, destination) { if (this.completed) { throw new Error('Transaction already completed'); } // Perform the copy await FileOperations.copyDirectory(source, destination); // Add rollback operation this.operations.push({ type: 'copy', source, destination, rollback: async () => { try { await fs.rm(destination, { recursive: true, force: true }); } catch (error) { logger.error(`[FileTransaction] Failed to rollback copy at ${destination}:`, error); } } }); } /** * Add a delete operation to the transaction */ async addDelete(deletePath, backupPath) { if (this.completed) { throw new Error('Transaction already completed'); } // If backup path provided, move instead of delete if (backupPath) { await this.fileOps.renameFile(deletePath, backupPath); this.operations.push({ type: 'delete', source: deletePath, destination: backupPath, rollback: async () => { try { await this.fileOps.renameFile(backupPath, deletePath); } catch (error) { logger.error(`[FileTransaction] Failed to restore deleted item from ${backupPath} to ${deletePath}:`, error); } } }); } else { // Direct delete (no rollback possible) await fs.rm(deletePath, { recursive: true, force: true }); this.operations.push({ type: 'delete', source: deletePath, rollback: async () => { logger.warn(`[FileTransaction] Cannot rollback permanent deletion of ${deletePath}`); } }); } } /** * Commit the transaction (mark as successful) */ commit() { this.completed = true; } /** * Rollback all operations in reverse order */ async rollback() { logger.info(`[FileTransaction] Rolling back ${this.operations.length} operations`); // Rollback in reverse order for (let i = this.operations.length - 1; i >= 0; i--) { const operation = this.operations[i]; logger.info(`[FileTransaction] Rolling back ${operation.type} operation`); try { await operation.rollback(); } catch (error) { logger.error(`[FileTransaction] Rollback failed for operation ${i}:`, error); // Continue with other rollbacks } } this.completed = true; } /** * Check if any operations have been performed */ hasOperations() { return this.operations.length > 0; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZU9wZXJhdGlvbnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvZmlsZU9wZXJhdGlvbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNyQyxPQUFPLEVBQTBCLHFCQUFxQixFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDckcsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRWpFLHVEQUF1RDtBQUN2RCxJQUFJLHFCQUFxQixHQUFrQyxJQUFJLENBQUM7QUFFaEUsU0FBUyx3QkFBd0I7SUFDL0IsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDM0IscUJBQXFCLEdBQUcsSUFBSSxxQkFBcUIsQ0FBQyxJQUFJLGVBQWUsRUFBRSxDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUNELE9BQU8scUJBQXFCLENBQUM7QUFDL0IsQ0FBQztBQWFEOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ3pCOzs7T0FHRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUN4QixHQUFXLEVBQ1gsSUFBWSxFQUNaLFVBQXVCLEVBQUU7UUFFekIsTUFBTSxFQUFFLFVBQVUsRUFBRSxlQUFlLEdBQUcsRUFBRSxFQUFFLFVBQVUsR0FBRyxDQUFDLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFFckUsc0RBQXNEO1FBQ3RELE1BQU0sS0FBSyxHQUFHLE1BQU0sSUFBSSxDQUFDLHVCQUF1QixDQUFDLEdBQUcsRUFBRSxlQUFlLENBQUMsQ0FBQztRQUN2RSxJQUFJLFdBQVcsR0FBRyxDQUFDLENBQUM7UUFFcEIsTUFBTSxJQUFJLENBQUMsc0JBQXNCLENBQy9CLEdBQUcsRUFDSCxJQUFJLEVBQ0osZUFBZSxF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Z0JBQ0gsTUFBTSxTQUFTLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDN0IsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyxtREFBbUQsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7Z0JBQzdFLGdDQUFnQztZQUNsQyxDQUFDO1FBQ0gsQ0FBQztRQUVELElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7T0FFRztJQUNILGFBQWE7UUFDWCxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztJQUNwQyxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBmcyBmcm9tICdmcy9wcm9taXNlcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IHsgSUZpbGVPcGVyYXRpb25zU2VydmljZSwgRmlsZU9wZXJhdGlvbnNTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvRmlsZU9wZXJhdGlvbnNTZXJ2aWNlLmpzJztcbmltcG9ydCB7IEZpbGVMb2NrTWFuYWdlciB9IGZyb20gJy4uL3NlY3VyaXR5L2ZpbGVMb2NrTWFuYWdlci5qcyc7XG5cbi8vIFNpbmdsZXRvbiBmaWxlIG9wZXJhdGlvbnMgc2VydmljZSBmb3Igc3RhdGljIG1ldGhvZHNcbmxldCBmaWxlT3BlcmF0aW9uc1NlcnZpY2U6IElGaWxlT3BlcmF0aW9uc1NlcnZpY2UgfCBudWxsID0gbnVsbDtcblxuZnVuY3Rpb24gZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk6IElGaWxlT3BlcmF0aW9uc1NlcnZpY2Uge1xuICBpZiAoIWZpbGVPcGVyYXRpb25zU2VydmljZSkge1xuICAgIGZpbGVPcGVyYXRpb25zU2VydmljZSA9IG5ldyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UobmV3IEZpbGVMb2NrTWFuYWdlcigpKTtcbiAgfVxuICByZXR1cm4gZmlsZU9wZXJhdGlvbnNTZXJ2aWNlO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENvcHlPcHRpb25zIHtcbiAgb25Qcm9ncmVzcz86IChjb3BpZWQ6IG51bWJlciwgdG90YWw6IG51bWJlciwgY3VycmVudEZpbGU6IHN0cmluZykgPT4gdm9pZDtcbiAgZXhjbHVkZVBhdHRlcm5zPzogc3RyaW5nW107XG4gIG1heFJldHJpZXM/OiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZVN0YXRzIHtcbiAgdG90YWxGaWxlczogbnVtYmVyO1xuICB0b3RhbFNpemU6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBDcm9zcy1wbGF0Zm9ybSBmaWxlIG9wZXJhdGlvbnMgdXRpbGl0eVxuICogQ2VudHJhbGl6ZXMgY29tbW9uIGZpbGUgb3BlcmF0aW9ucyB3aXRoIHByb2dyZXNzIHJlcG9ydGluZyBhbmQgZXJyb3IgaGFuZGxpbmdcbiAqL1xuZXhwb3J0IGNsYXNzIEZpbGVPcGVyYXRpb25zIHtcbiAgLyoqXG4gICAqIFJlY3Vyc2l2ZWx5IGNvcHkgYSBkaXJlY3Rvcnkgd2l0aCBwcm9ncmVzcyByZXBvcnRpbmdcbiAgICogV29ya3MgY3Jvc3MtcGxhdGZvcm0gd2l0aG91dCByZWx5aW5nIG9uIHNoZWxsIGNvbW1hbmRzXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgY29weURpcmVjdG9yeShcbiAgICBzcmM6IHN0cmluZywgXG4gICAgZGVzdDogc3RyaW5nLCBcbiAgICBvcHRpb25zOiBDb3B5T3B0aW9ucyA9IHt9XG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgb25Qcm9ncmVzcywgZXhjbHVkZVBhdHRlcm5zID0gW10sIG1heFJldHJpZXMgPSAzIH0gPSBvcHRpb25zO1xuICAgIFxuICAgIC8vIEZpcnN0LCBjYWxjdWxhdGUgdG90YWwgZmlsZXMgZm9yIHByb2dyZXNzIHJlcG9ydGluZ1xuICAgIGNvbnN0IHN0YXRzID0gYXdhaXQgdGhpcy5jYWxjdWxhdGVEaXJlY3RvcnlTdGF0cyhzcmMsIGV4Y2x1ZGVQYXR0ZXJucyk7XG4gICAgbGV0IGNvcGllZEZpbGVzID0gMDtcbiAgICBcbiAgICBhd2FpdCB0aGlzLmNvcHlEaXJlY3RvcnlSZWN1cnNpdmUoXG4gICAgICBzcmMsIFxuICAgICAgZGVzdCwgXG4gICAgICBleGNsdWRlUGF0dGVybnMsXG4gICAgICBtYXhSZXRyaWVzLFxuICAgICAgKGN1cnJlbnRGaWxlKSA9PiB7XG4gICAgICAgIGNvcGllZEZpbGVzKys7XG4gICAgICAgIGlmIChvblByb2dyZXNzKSB7XG4gICAgICAgICAgb25Qcm9ncmVzcyhjb3BpZWRGaWxlcywgc3RhdHMudG90YWxGaWxlcywgY3VycmVudEZpbGUpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBkaXJlY3Rvcnkgc3RhdGlzdGljcyBmb3IgcHJvZ3Jlc3MgcmVwb3J0aW5nXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBhc3luYyBjYWxjdWxhdGVEaXJlY3RvcnlTdGF0cyhcbiAgICBkaXI6IHN0cmluZyxcbiAgICBleGNsdWRlUGF0dGVybnM6IHN0cmluZ1tdXG4gICk6IFByb21pc2U8RmlsZVN0YXRzPiB7XG4gICAgY29uc3QgZmlsZU9wcyA9IGdldEZpbGVPcGVyYXRpb25zU2VydmljZSgpO1xuICAgIGxldCB0b3RhbEZpbGVzID0gMDtcbiAgICBsZXQgdG90YWxTaXplID0gMDtcblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZmlsZU9wcy5saXN0RGlyZWN0b3J5V2l0aFR5cGVzKGRpcik7XG5cbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICBjb25zdCBmdWxsUGF0aCA9IHBhdGguam9pbihkaXIsIGVudHJ5Lm5hbWUpO1xuXG4gICAgICAgIC8vIFNraXAgZXhjbHVkZWQgcGF0dGVybnNcbiAgICAgICAgaWYgKHRoaXMuc2hvdWxkRXhjbHVkZShlbnRyeS5uYW1lLCBleGNsdWRlUGF0dGVybnMpKSB7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZW50cnkuaXNEaXJlY3RvcnkpIHtcbiAgICAgICAgICBjb25zdCBzdWJTdGF0cyA9IGF3YWl0IHRoaXMuY2FsY3VsYXRlRGlyZWN0b3J5U3RhdHMoZnVsbFBhdGgsIGV4Y2x1ZGVQYXR0ZXJucyk7XG4gICAgICAgICAgdG90YWxGaWxlcyArPSBzdWJTdGF0cy50b3RhbEZpbGVzO1xuICAgICAgICAgIHRvdGFsU2l6ZSArPSBzdWJTdGF0cy50b3RhbFNpemU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdG90YWxGaWxlcysrO1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBzdGF0ID0gYXdhaXQgZmlsZU9wcy5zdGF0KGZ1bGxQYXRoKTtcbiAgICAgICAgICAgIHRvdGFsU2l6ZSArPSBzdGF0LnNpemU7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIExvZyBzdGF0IGVycm9ycyBmb3IgZGVidWdnaW5nIC0gY291bGQgaW5kaWNhdGUgcGVybWlzc2lvbnMsIGNvcnJ1cHRpb24sIG9yIGRpc2sgZXJyb3JzXG4gICAgICAgICAgICBsb2dnZXIud2FybihgW0ZpbGVPcGVyYXRpb25zXSBGYWlsZWQgdG8gc3RhdCBmaWxlICR7ZnVsbFBhdGh9OmAsIHtcbiAgICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSxcbiAgICAgICAgICAgICAgY29kZTogKGVycm9yIGFzIE5vZGVKUy5FcnJub0V4Y2VwdGlvbikuY29kZVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAvLyBDb250aW51ZSB3aXRob3V0IHRoaXMgZmlsZSdzIHNpemVcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLndhcm4oYFtGaWxlT3BlcmF0aW9uc10gRXJyb3IgY2FsY3VsYXRpbmcgc3RhdHMgZm9yICR7ZGlyfTpgLCBlcnJvcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgdG90YWxGaWxlcywgdG90YWxTaXplIH07XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbnRlcm5hbCByZWN1cnNpdmUgY29weSBpbXBsZW1lbnRhdGlvblxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgY29weURpcmVjdG9yeVJlY3Vyc2l2ZShcbiAgICBzcmM6IHN0cmluZyxcbiAgICBkZXN0OiBzdHJpbmcsXG4gICAgZXhjbHVkZVBhdHRlcm5zOiBzdHJpbmdbXSxcbiAgICBtYXhSZXRyaWVzOiBudW1iZXIsXG4gICAgb25GaWxlQ29waWVkOiAoZmlsZTogc3RyaW5nKSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGZpbGVPcHMgPSBnZXRGaWxlT3BlcmF0aW9uc1NlcnZpY2UoKTtcblxuICAgIC8vIEVuc3VyZSBkZXN0aW5hdGlvbiBkaXJlY3RvcnkgZXhpc3RzXG4gICAgYXdhaXQgZmlsZU9wcy5jcmVhdGVEaXJlY3RvcnkoZGVzdCk7XG5cbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZmlsZU9wcy5saXN0RGlyZWN0b3J5V2l0aFR5cGVzKHNyYyk7XG5cbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgIGNvbnN0IHNyY1BhdGggPSBwYXRoLmpvaW4oc3JjLCBlbnRyeS5uYW1lKTtcbiAgICAgIGNvbnN0IGRlc3RQYXRoID0gcGF0aC5qb2luKGRlc3QsIGVudHJ5Lm5hbWUpO1xuXG4gICAgICAvLyBTa2lwIGV4Y2x1ZGVkIHBhdHRlcm5zXG4gICAgICBpZiAodGhpcy5zaG91bGRFeGNsdWRlKGVudHJ5Lm5hbWUsIGV4Y2x1ZGVQYXR0ZXJucykpIHtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKGBbRmlsZU9wZXJhdGlvbnNdIFNraXBwaW5nIGV4Y2x1ZGVkOiAke2VudHJ5Lm5hbWV9YCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBpZiAoZW50cnkuaXNEaXJlY3RvcnkpIHtcbiAgICAgICAgYXdhaXQgdGhpcy5jb3B5RGlyZWN0b3J5UmVjdXJzaXZlKFxuICAgICAgICAgIHNyY1BhdGgsXG4gICAgICAgICAgZGVzdFBhdGgsXG4gICAgICAgICAgZXhjbHVkZVBhdHRlcm5zLFxuICAgICAgICAgIG1heFJldHJpZXMsXG4gICAgICAgICAgb25GaWxlQ29waWVkXG4gICAgICAgICk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhd2FpdCB0aGlzLmNvcHlGaWxlV2l0aFJldHJ5KHNyY1BhdGgsIGRlc3RQYXRoLCBtYXhSZXRyaWVzKTtcbiAgICAgICAgb25GaWxlQ29waWVkKHNyY1BhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENvcHkgYSBzaW5nbGUgZmlsZSB3aXRoIHJldHJ5IGxvZ2ljXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBhc3luYyBjb3B5RmlsZVdpdGhSZXRyeShcbiAgICBzcmM6IHN0cmluZyxcbiAgICBkZXN0OiBzdHJpbmcsXG4gICAgbWF4UmV0cmllczogbnVtYmVyXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGZpbGVPcHMgPSBnZXRGaWxlT3BlcmF0aW9uc1NlcnZpY2UoKTtcbiAgICBsZXQgbGFzdEVycm9yOiBFcnJvciB8IG51bGwgPSBudWxsO1xuXG4gICAgZm9yIChsZXQgYXR0ZW1wdCA9IDE7IGF0dGVtcHQgPD0gbWF4UmV0cmllczsgYXR0ZW1wdCsrKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBmaWxlT3BzLmNvcHlGaWxlKHNyYywgZGVzdCwgeyBzb3VyY2U6ICdGaWxlT3BlcmF0aW9ucy5jb3B5RmlsZVdpdGhSZXRyeScgfSk7XG4gICAgICAgIHJldHVybjsgLy8gU3VjY2Vzc1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbGFzdEVycm9yID0gZXJyb3IgYXMgRXJyb3I7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZyhgW0ZpbGVPcGVyYXRpb25zXSBDb3B5IGF0dGVtcHQgJHthdHRlbXB0fSBmYWlsZWQgZm9yICR7c3JjfTogJHtlcnJvcn1gKTtcblxuICAgICAgICBpZiAoYXR0ZW1wdCA8IG1heFJldHJpZXMpIHtcbiAgICAgICAgICAvLyBXYWl0IGJlZm9yZSByZXRyeSAoZXhwb25lbnRpYWwgYmFja29mZilcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgTWF0aC5wb3coMiwgYXR0ZW1wdCkgKiAxMDApKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFsbCByZXRyaWVzIGZhaWxlZFxuICAgIHRocm93IG5ldyBFcnJvcihgRmFpbGVkIHRvIGNvcHkgJHtzcmN9IGFmdGVyICR7bWF4UmV0cmllc30gYXR0ZW1wdHM6ICR7bGFzdEVycm9yPy5tZXNzYWdlfWApO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgYSBmaWxlL2RpcmVjdG9yeSBzaG91bGQgYmUgZXhjbHVkZWRcbiAgICpcbiAgICogRklYOiBSZURvUyB2dWxuZXJhYmlsaXR5IC0gcmVwbGFjZWQgdW5zYWZlIGdsb2ItdG8tcmVnZXggY29udmVyc2lvblxuICAgKiBQcmV2aW91c2x5OiBVc2VkIHBhdHRlcm4ucmVwbGFjZSB3aXRoIC4qIHdoaWNoIGNvdWxkIGNhdXNlIGNhdGFzdHJvcGhpYyBiYWNrdHJhY2tpbmdcbiAgICogTm93OiBVc2VzIHNhZmUgZ2xvYiBtYXRjaGluZyB3aXRoIHByb3BlciBlc2NhcGluZyBhbmQgYm91bmRlZCBwYXR0ZXJuc1xuICAgKiBTb25hckNsb3VkOiBSZXNvbHZlcyBET1MgdnVsbmVyYWJpbGl0eSBob3RzcG90XG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBzaG91bGRFeGNsdWRlKG5hbWU6IHN0cmluZywgcGF0dGVybnM6IHN0cmluZ1tdKTogYm9vbGVhbiB7XG4gICAgLy8gSW5wdXQgdmFsaWRhdGlvbiB0byBwcmV2ZW50IERPU1xuICAgIGlmIChuYW1lLmxlbmd0aCA+IDEwMDApIHtcbiAgICAgIHJldHVybiBmYWxzZTsgLy8gUmVqZWN0IG92ZXJseSBsb25nIGlucHV0c1xuICAgIH1cblxuICAgIGZvciAoY29uc3QgcGF0dGVybiBvZiBwYXR0ZXJucykge1xuICAgICAgaWYgKHBhdHRlcm4uaW5jbHVkZXMoJyonKSkge1xuICAgICAgICAvLyBTYWZlIGdsb2Igc3VwcG9ydCAtIHByZXZlbnQgUmVEb1NcbiAgICAgICAgLy8gRXNjYXBlIHNwZWNpYWwgcmVnZXggY2hhcnMgZXhjZXB0ICpcbiAgICAgICAgLy8gRklYOiBVc2UgU3RyaW5nLnJhdyBhbmQgcmVwbGFjZUFsbCAoU29uYXJDbG91ZCBTNzc4MCwgUzc3ODEpXG4gICAgICAgIGNvbnN0IGVzY2FwZWQgPSBwYXR0ZXJuLnJlcGxhY2VBbGwoL1suKz9eJHt9KCl8W1xcXVxcXFxdL2csIFN0cmluZy5yYXdgXFwkJmApO1xuICAgICAgICAvLyBSZXBsYWNlICogd2l0aCBbXi9dKiAobWF0Y2ggYW55dGhpbmcgZXhjZXB0IHBhdGggc2VwYXJhdG9yKVxuICAgICAgICAvLyBUaGlzIHByZXZlbnRzIGNhdGFzdHJvcGhpYyBiYWNrdHJhY2tpbmdcbiAgICAgICAgY29uc3Qgc2FmZVBhdHRlcm4gPSBlc2NhcGVkLnJlcGxhY2VBbGwoJyonLCAnW14vXSonKTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEZJWDogVXNlIHRlbXBsYXRlIGxpdGVyYWwgdG8gYXZvaWQgc2VjdXJpdHkgc2Nhbm5lciBmYWxzZSBwb3NpdGl2ZSAoUFIgIzExODcpXG4gICAgICAgICAgLy8gVGhpcyBpcyBOT1QgU1FMIGluamVjdGlvbiAtIGl0J3MgYSBSZWdFeHAgcGF0dGVyblxuICAgICAgICAgIGNvbnN0IHJlZ2V4ID0gbmV3IFJlZ0V4cChgXiR7c2FmZVBhdHRlcm59JGApO1xuICAgICAgICAgIGlmIChyZWdleC50ZXN0KG5hbWUpKSByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBJbnZhbGlkIHBhdHRlcm4gLSBsb2cgYW5kIHNraXAgaXRcbiAgICAgICAgICBsb2dnZXIuZGVidWcoYFtGaWxlT3BlcmF0aW9uc10gSW52YWxpZCByZWdleCBwYXR0ZXJuOiAke3BhdHRlcm59YCwge1xuICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKG5hbWUgPT09IHBhdHRlcm4pIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJlbW92ZSBhIGRpcmVjdG9yeSB3aXRoIHByb2dyZXNzIHJlcG9ydGluZ1xuICAgKi9cbiAgc3RhdGljIGFzeW5jIHJlbW92ZURpcmVjdG9yeShcbiAgICBkaXI6IHN0cmluZyxcbiAgICBvcHRpb25zOiB7IG9uUHJvZ3Jlc3M/OiAocmVtb3ZlZDogbnVtYmVyLCB0b3RhbDogbnVtYmVyKSA9PiB2b2lkIH0gPSB7fVxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHRoaXMuY2FsY3VsYXRlRGlyZWN0b3J5U3RhdHMoZGlyLCBbXSk7XG4gICAgbGV0IHJlbW92ZWRGaWxlcyA9IDA7XG4gICAgXG4gICAgYXdhaXQgdGhpcy5yZW1vdmVEaXJlY3RvcnlSZWN1cnNpdmUoZGlyLCAoKSA9PiB7XG4gICAgICByZW1vdmVkRmlsZXMrKztcbiAgICAgIGlmIChvcHRpb25zLm9uUHJvZ3Jlc3MpIHtcbiAgICAgICAgb3B0aW9ucy5vblByb2dyZXNzKHJlbW92ZWRGaWxlcywgc3RhdHMudG90YWxGaWxlcyk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBJbnRlcm5hbCByZWN1cnNpdmUgcmVtb3ZlIGltcGxlbWVudGF0aW9uXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBhc3luYyByZW1vdmVEaXJlY3RvcnlSZWN1cnNpdmUoXG4gICAgZGlyOiBzdHJpbmcsXG4gICAgb25GaWxlUmVtb3ZlZDogKCkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBmaWxlT3BzID0gZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgZW50cmllcyA9IGF3YWl0IGZpbGVPcHMubGlzdERpcmVjdG9yeVdpdGhUeXBlcyhkaXIpO1xuXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4oZGlyLCBlbnRyeS5uYW1lKTtcblxuICAgICAgICBpZiAoZW50cnkuaXNEaXJlY3RvcnkpIHtcbiAgICAgICAgICBhd2FpdCB0aGlzLnJlbW92ZURpcmVjdG9yeVJlY3Vyc2l2ZShmdWxsUGF0aCwgb25GaWxlUmVtb3ZlZCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgYXdhaXQgZmlsZU9wcy5kZWxldGVGaWxlKGZ1bGxQYXRoLCB1bmRlZmluZWQsIHsgc291cmNlOiAnRmlsZU9wZXJhdGlvbnMucmVtb3ZlRGlyZWN0b3J5UmVjdXJzaXZlJyB9KTtcbiAgICAgICAgICBvbkZpbGVSZW1vdmVkKCk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gUmVtb3ZlIHRoZSBub3ctZW1wdHkgZGlyZWN0b3J5XG4gICAgICBhd2FpdCBmcy5ybWRpcihkaXIpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoYFtGaWxlT3BlcmF0aW9uc10gRXJyb3IgcmVtb3ZpbmcgZGlyZWN0b3J5ICR7ZGlyfTpgLCBlcnJvcik7XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDcmVhdGUgYSB0cmFuc2FjdGlvbiBtYW5hZ2VyIGZvciBhdG9taWMgZmlsZSBvcGVyYXRpb25zXG4gICAqL1xuICBzdGF0aWMgY3JlYXRlVHJhbnNhY3Rpb24oKTogRmlsZVRyYW5zYWN0aW9uIHtcbiAgICByZXR1cm4gbmV3IEZpbGVUcmFuc2FjdGlvbigpO1xuICB9XG59XG5cbi8qKlxuICogVHJhbnNhY3Rpb24gbWFuYWdlciBmb3IgYXRvbWljIGZpbGUgb3BlcmF0aW9uc1xuICogRW5zdXJlcyBhbGwgb3BlcmF0aW9ucyBzdWNjZWVkIG9yIGFsbCBhcmUgcm9sbGVkIGJhY2tcbiAqL1xuZXhwb3J0IGNsYXNzIEZpbGVUcmFuc2FjdGlvbiB7XG4gIC8vIEZJWDogTWFyayBhcyByZWFkb25seSBzaW5jZSBuZXZlciByZWFzc2lnbmVkIChTb25hckNsb3VkIFMyOTMzKVxuICBwcml2YXRlIHJlYWRvbmx5IG9wZXJhdGlvbnM6IEFycmF5PHtcbiAgICB0eXBlOiAnbW92ZScgfCAnY29weScgfCAnZGVsZXRlJyB8ICdjcmVhdGUnO1xuICAgIHNvdXJjZT86IHN0cmluZztcbiAgICBkZXN0aW5hdGlvbj86IHN0cmluZztcbiAgICByb2xsYmFjazogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgfT4gPSBbXTtcblxuICBwcml2YXRlIGNvbXBsZXRlZCA9IGZhbHNlO1xuICBwcml2YXRlIHJlYWRvbmx5IGZpbGVPcHM6IElGaWxlT3BlcmF0aW9uc1NlcnZpY2U7XG5cbiAgY29uc3RydWN0b3IoZmlsZU9wZXJhdGlvbnM/OiBJRmlsZU9wZXJhdGlvbnNTZXJ2aWNlKSB7XG4gICAgdGhpcy5maWxlT3BzID0gZmlsZU9wZXJhdGlvbnMgfHwgZ2V0RmlsZU9wZXJhdGlvbnNTZXJ2aWNlKCk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBBZGQgYSBtb3ZlIG9wZXJhdGlvbiB0byB0aGUgdHJhbnNhY3Rpb25cbiAgICovXG4gIGFzeW5jIGFkZE1vdmUoc291cmNlOiBzdHJpbmcsIGRlc3RpbmF0aW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jb21wbGV0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVHJhbnNhY3Rpb24gYWxyZWFkeSBjb21wbGV0ZWQnKTtcbiAgICB9XG5cbiAgICAvLyBQZXJmb3JtIHRoZSBtb3ZlXG4gICAgYXdhaXQgdGhpcy5maWxlT3BzLnJlbmFtZUZpbGUoc291cmNlLCBkZXN0aW5hdGlvbik7XG5cbiAgICAvLyBBZGQgcm9sbGJhY2sgb3BlcmF0aW9uXG4gICAgdGhpcy5vcGVyYXRpb25zLnB1c2goe1xuICAgICAgdHlwZTogJ21vdmUnLFxuICAgICAgc291cmNlLFxuICAgICAgZGVzdGluYXRpb24sXG4gICAgICByb2xsYmFjazogYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHRoaXMuZmlsZU9wcy5yZW5hbWVGaWxlKGRlc3RpbmF0aW9uLCBzb3VyY2UpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcihgW0ZpbGVUcmFuc2FjdGlvbl0gRmFpbGVkIHRvIHJvbGxiYWNrIG1vdmUgZnJvbSAke2Rlc3RpbmF0aW9ufSB0byAke3NvdXJjZX06YCwgZXJyb3IpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBBZGQgYSBjb3B5IG9wZXJhdGlvbiB0byB0aGUgdHJhbnNhY3Rpb25cbiAgICovXG4gIGFzeW5jIGFkZENvcHkoc291cmNlOiBzdHJpbmcsIGRlc3RpbmF0aW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jb21wbGV0ZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignVHJhbnNhY3Rpb24gYWxyZWFkeSBjb21wbGV0ZWQnKTtcbiAgICB9XG4gICAgXG4gICAgLy8gUGVyZm9ybSB0aGUgY29weVxuICAgIGF3YWl0IEZpbGVPcGVyYXRpb25zLmNvcHlEaXJlY3Rvcnkoc291cmNlLCBkZXN0aW5hdGlvbik7XG4gICAgXG4gICAgLy8gQWRkIHJvbGxiYWNrIG9wZXJhdGlvblxuICAgIHRoaXMub3BlcmF0aW9ucy5wdXNoKHtcbiAgICAgIHR5cGU6ICdjb3B5JyxcbiAgICAgIHNvdXJjZSxcbiAgICAgIGRlc3RpbmF0aW9uLFxuICAgICAgcm9sbGJhY2s6IGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBmcy5ybShkZXN0aW5hdGlvbiwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcihgW0ZpbGVUcmFuc2FjdGlvbl0gRmFpbGVkIHRvIHJvbGxiYWNrIGNvcHkgYXQgJHtkZXN0aW5hdGlvbn06YCwgZXJyb3IpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBBZGQgYSBkZWxldGUgb3BlcmF0aW9uIHRvIHRoZSB0cmFuc2FjdGlvblxuICAgKi9cbiAgYXN5bmMgYWRkRGVsZXRlKGRlbGV0ZVBhdGg6IHN0cmluZywgYmFja3VwUGF0aD86IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmNvbXBsZXRlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcmFuc2FjdGlvbiBhbHJlYWR5IGNvbXBsZXRlZCcpO1xuICAgIH1cblxuICAgIC8vIElmIGJhY2t1cCBwYXRoIHByb3ZpZGVkLCBtb3ZlIGluc3RlYWQgb2YgZGVsZXRlXG4gICAgaWYgKGJhY2t1cFBhdGgpIHtcbiAgICAgIGF3YWl0IHRoaXMuZmlsZU9wcy5yZW5hbWVGaWxlKGRlbGV0ZVBhdGgsIGJhY2t1cFBhdGgpO1xuXG4gICAgICB0aGlzLm9wZXJhdGlvbnMucHVzaCh7XG4gICAgICAgIHR5cGU6ICdkZWxldGUnLFxuICAgICAgICBzb3VyY2U6IGRlbGV0ZVBhdGgsXG4gICAgICAgIGRlc3RpbmF0aW9uOiBiYWNrdXBQYXRoLFxuICAgICAgICByb2xsYmFjazogYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmZpbGVPcHMucmVuYW1lRmlsZShiYWNrdXBQYXRoLCBkZWxldGVQYXRoKTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgbG9nZ2VyLmVycm9yKGBbRmlsZVRyYW5zYWN0aW9uXSBGYWlsZWQgdG8gcmVzdG9yZSBkZWxldGVkIGl0ZW0gZnJvbSAke2JhY2t1cFBhdGh9IHRvICR7ZGVsZXRlUGF0aH06YCwgZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIERpcmVjdCBkZWxldGUgKG5vIHJvbGxiYWNrIHBvc3NpYmxlKVxuICAgICAgYXdhaXQgZnMucm0oZGVsZXRlUGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuXG4gICAgICB0aGlzLm9wZXJhdGlvbnMucHVzaCh7XG4gICAgICAgIHR5cGU6ICdkZWxldGUnLFxuICAgICAgICBzb3VyY2U6IGRlbGV0ZVBhdGgsXG4gICAgICAgIHJvbGxiYWNrOiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgbG9nZ2VyLndhcm4oYFtGaWxlVHJhbnNhY3Rpb25dIENhbm5vdCByb2xsYmFjayBwZXJtYW5lbnQgZGVsZXRpb24gb2YgJHtkZWxldGVQYXRofWApO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDb21taXQgdGhlIHRyYW5zYWN0aW9uIChtYXJrIGFzIHN1Y2Nlc3NmdWwpXG4gICAqL1xuICBjb21taXQoKTogdm9pZCB7XG4gICAgdGhpcy5jb21wbGV0ZWQgPSB0cnVlO1xuICB9XG4gIFxuICAvKipcbiAgICogUm9sbGJhY2sgYWxsIG9wZXJhdGlvbnMgaW4gcmV2ZXJzZSBvcmRlclxuICAgKi9cbiAgYXN5bmMgcm9sbGJhY2soKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbG9nZ2VyLmluZm8oYFtGaWxlVHJhbnNhY3Rpb25dIFJvbGxpbmcgYmFjayAke3RoaXMub3BlcmF0aW9ucy5sZW5ndGh9IG9wZXJhdGlvbnNgKTtcbiAgICBcbiAgICAvLyBSb2xsYmFjayBpbiByZXZlcnNlIG9yZGVyXG4gICAgZm9yIChsZXQgaSA9IHRoaXMub3BlcmF0aW9ucy5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgICAgY29uc3Qgb3BlcmF0aW9uID0gdGhpcy5vcGVyYXRpb25zW2ldO1xuICAgICAgbG9nZ2VyLmluZm8oYFtGaWxlVHJhbnNhY3Rpb25dIFJvbGxpbmcgYmFjayAke29wZXJhdGlvbi50eXBlfSBvcGVyYXRpb25gKTtcbiAgICAgIFxuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgb3BlcmF0aW9uLnJvbGxiYWNrKCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIuZXJyb3IoYFtGaWxlVHJhbnNhY3Rpb25dIFJvbGxiYWNrIGZhaWxlZCBmb3Igb3BlcmF0aW9uICR7aX06YCwgZXJyb3IpO1xuICAgICAgICAvLyBDb250aW51ZSB3aXRoIG90aGVyIHJvbGxiYWNrc1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICB0aGlzLmNvbXBsZXRlZCA9IHRydWU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhbnkgb3BlcmF0aW9ucyBoYXZlIGJlZW4gcGVyZm9ybWVkXG4gICAqL1xuICBoYXNPcGVyYXRpb25zKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLm9wZXJhdGlvbnMubGVuZ3RoID4gMDtcbiAgfVxufSJdfQ==