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.

226 lines 27.5 kB
/** * ElementFileOperations - Common file operations for element managers * * Provides shared file handling operations for all element types: * - Reading files with frontmatter parsing * - Writing files with atomic operations * - Directory scanning and filtering * * SECURITY: All operations use FileLockManager for atomic reads/writes * PATH VALIDATION: All paths are validated before file operations * LOGGING: Operations are logged for debugging and audit trail */ import { sanitizeInput, validatePath } from '../../security/InputValidator.js'; import { SecurityMonitor } from '../../security/securityMonitor.js'; import { logger } from '../../utils/logger.js'; import { promises as fs } from 'fs'; import * as path from 'path'; import matter from 'gray-matter'; /** * Utility class for common file operations * Used by BaseElementManager and can be used by other managers * * DEPENDENCY INJECTION: Requires FileLockManager instance for atomic operations */ export class ElementFileOperations { fileLockManager; /** * Constructor - accepts FileLockManager for atomic operations * @param fileLockManager - FileLockManager instance for atomic file operations */ constructor(fileLockManager) { this.fileLockManager = fileLockManager; } /** * Atomically read a file with frontmatter * SECURITY: Uses FileLockManager to prevent race conditions * * @param filePath - Absolute or relative path to file * @param baseDir - Base directory for validation * @param options - Operation options * @returns Parsed file with metadata and content */ async readFileWithFrontmatter(filePath, baseDir, options = {}) { const { maxSize = 1024 * 1024, // 1MB default validatePath: shouldValidatePath = true } = options; // SECURITY: Sanitize path const sanitizedPath = sanitizeInput(filePath, 255); // Validate path if requested if (shouldValidatePath) { try { validatePath(sanitizedPath, baseDir); } catch (error) { throw new Error(`Invalid file path: ${error instanceof Error ? error.message : 'Invalid path'}`); } } // Resolve full path const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(baseDir, sanitizedPath); // Check file size before reading const stats = await fs.stat(fullPath); if (stats.size > maxSize) { throw new Error(`File too large: ${stats.size} bytes (max: ${maxSize})`); } // CRITICAL FIX: Use atomic file read via injected instance const raw = await this.fileLockManager.atomicReadFile(fullPath, { encoding: 'utf-8' }); // Parse frontmatter const parsed = matter(raw); return { metadata: parsed.data, content: parsed.content, raw }; } /** * Atomically write a file with frontmatter * SECURITY: Uses FileLockManager to prevent corruption * * @param filePath - Absolute or relative path to file * @param metadata - YAML frontmatter metadata * @param content - File content * @param baseDir - Base directory for validation * @param options - Operation options */ async writeFileWithFrontmatter(filePath, metadata, content, baseDir, options = {}) { const { validatePath: shouldValidatePath = true, ensureDir = true } = options; // SECURITY: Sanitize path const sanitizedPath = sanitizeInput(filePath, 255); // Validate path if requested if (shouldValidatePath) { try { validatePath(sanitizedPath, baseDir); } catch (error) { throw new Error(`Invalid file path: ${error instanceof Error ? error.message : 'Invalid path'}`); } } // Resolve full path const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(baseDir, sanitizedPath); // Ensure directory exists if (ensureDir) { await fs.mkdir(path.dirname(fullPath), { recursive: true }); } // Clean metadata to remove undefined values const cleanMetadata = Object.entries(metadata).reduce((acc, [key, value]) => { if (key !== 'gatekeeperDiagnostics' && value !== undefined) { acc[key] = value; } return acc; }, {}); // Create frontmatter content const fileContent = matter.stringify(content, cleanMetadata); // CRITICAL FIX: Use atomic file write via injected instance await this.fileLockManager.atomicWriteFile(fullPath, fileContent, { encoding: 'utf-8' }); } /** * Validate path is within base directory and resolve to absolute path * SECURITY: Prevents path traversal attacks * * @param filePath - Path to validate * @param baseDir - Base directory * @returns Normalized absolute path * @throws Error if path is invalid or outside base directory */ validateAndResolvePath(filePath, baseDir) { const sanitizedPath = sanitizeInput(filePath, 255); // Resolve to absolute path const fullPath = path.isAbsolute(sanitizedPath) ? sanitizedPath : path.join(baseDir, sanitizedPath); // Normalize and check path traversal const normalizedPath = path.normalize(fullPath); if (!normalizedPath.startsWith(baseDir)) { SecurityMonitor.logSecurityEvent({ type: 'PATH_TRAVERSAL_ATTEMPT', severity: 'CRITICAL', source: 'ElementFileOperations.validateAndResolvePath', details: `Attempted to access file outside directory: ${sanitizedPath}` }); throw new Error('Path traversal attempt detected'); } return normalizedPath; } /** * Generate filename from element name * Converts name to lowercase and replaces invalid characters with hyphens * * @param name - Element name * @param extension - File extension (default: '.md') * @returns Sanitized filename */ generateFilename(name, extension = '.md') { const sanitized = sanitizeInput(name, 100); const filename = sanitized .toLowerCase() .replaceAll(/[^a-z0-9-]/g, '-') .replaceAll(/-+/g, '-') .replaceAll(/^-|-$/g, ''); return `${filename}${extension}`; } /** * Check if a file exists * * @param filePath - Path to check * @param baseDir - Base directory (optional) * @returns True if file exists */ async fileExists(filePath, baseDir) { try { const sanitizedPath = sanitizeInput(filePath, 255); const fullPath = baseDir ? path.join(baseDir, sanitizedPath) : sanitizedPath; await fs.access(fullPath); return true; } catch { return false; } } /** * Delete a file with validation * SECURITY: Validates path before deletion * * @param filePath - Path to delete * @param baseDir - Base directory for validation * @throws Error if path is invalid */ async deleteFile(filePath, baseDir) { const normalizedPath = this.validateAndResolvePath(filePath, baseDir); // Log security event SecurityMonitor.logSecurityEvent({ type: 'ELEMENT_DELETED', severity: 'MEDIUM', source: 'ElementFileOperations.deleteFile', details: `File deleted: ${path.basename(normalizedPath)}` }); await fs.unlink(normalizedPath); } /** * List files in a directory * * @param dir - Directory to list * @param extension - Filter by extension (optional) * @returns Array of file paths */ async listFiles(dir, extension) { try { // Ensure directory exists await fs.mkdir(dir, { recursive: true }); const files = await fs.readdir(dir); if (extension) { return files.filter(f => f.endsWith(extension)); } return files; } catch (error) { logger.error(`Failed to list files in ${dir}:`, error); return []; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlbWVudEZpbGVPcGVyYXRpb25zLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2VsZW1lbnRzL2Jhc2UvRWxlbWVudEZpbGVPcGVyYXRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7OztHQVdHO0FBR0gsT0FBTyxFQUFFLGFBQWEsRUFBRSxZQUFZLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUMvRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sbUNBQW1DLENBQUM7QUFDcEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9DLE9BQU8sRUFBRSxRQUFRLElBQUksRUFBRSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BDLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sTUFBTSxNQUFNLGFBQWEsQ0FBQztBQXVCakM7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8scUJBQXFCO0lBQ3hCLGVBQWUsQ0FBa0I7SUFFekM7OztPQUdHO0lBQ0gsWUFBWSxlQUFnQztRQUMxQyxJQUFJLENBQUMsZUFBZSxHQUFHLGVBQWUsQ0FBQztJQUN6QyxDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxLQUFLLENBQUMsdUJBQXVCLENBQzNCLFFBQWdCLEVBQ2hCLE9BQWUsRUFDZixVQUFnQyxFQUFFO1FBRWxDLE1BQU0sRUFDSixPQUFPLEdBQUcsSUFBSSxHQUFHLElBQUksRUFBRSxjQUFjO1FBQ3JDLFlBQVksRUFBRSxrQkFBa0IsR0FBRyxJQUFJLEVBQ3hDLEdBQUcsT0FBTyxDQUFDO1FBRVosMEJBQTBCO1FBQzFCLE1BQU0sYUFBYSxHQUFHLGFBQWEsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFFbkQsNkJBQTZCO1FBQzdCLElBQUksa0JBQWtCLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUM7Z0JBQ0gsWUFBWSxDQUFDLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUN2QyxDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDO1lBQ25HLENBQUM7UUFDSCxDQUFDO1FBRUQsb0JBQW9CO1FBQ3BCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDO1lBQzdDLENBQUMsQ0FBQyxhQUFhO1lBQ2YsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRXRDLGlDQUFpQztRQUNqQyxNQUFNLEtBQUssR0FBRyxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdEMsSUFBSSxLQUFLLENBQUMsSUFBSSxHQUFHLE9BQU8sRUFBRSxDQUFDO1lBQ3pCLE1BQU0sSUFBSSxLQUFLLENBQUMsbUJBQW1CLEtBQUssQ0FBQyxJQUFJLGdCQUFnQixPQUFPLEdBQUcsQ0FBQyxDQUFDO1FBQzNFLENBQUM7UUFFRCwyREFBMkQ7UUFDM0QsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsZUFBZSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUV2RixvQkFBb0I7UUFDcEIsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTNCLE9BQU87WUFDTCxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUk7WUFDckIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO1lBQ3ZCLEdBQUc7U0FDSixDQUFDO0lBQ0osQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNILEtBQUssQ0FBQyx3QkFBd0IsQ0FDNUIsUUFBZ0IsRUFDaEIsUUFBYSxFQUNiLE9BQWUsRUFDZixPQUFlLEVBQ2YsVUFBZ0MsRUFBRTtRQUVsQyxNQUFNLEVBQ0osWUFBWSxFQUFFLGtCQUFrQixHQUFHLElBQUksRUFDdkMsU0FBUyxHQUFHLElBQUksRUFDakIsR0FBRyxPQUFPLENBQUM7UUFFWiwwQkFBMEI7UUFDMUIsTUFBTSxhQUFhLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUVuRCw2QkFBNkI7UUFDN0IsSUFBSSxrQkFBa0IsRUFBRSxDQUFDO1lBQ3ZCLElBQUksQ0FBQztnQkFDSCxZQUFZLENBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ3ZDLENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLGNBQWMsRUFBRSxDQUFDLENBQUM7WUFDbkcsQ0FBQztRQUNILENBQUM7UUFFRCxvQkFBb0I7UUFDcEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUM7WUFDN0MsQ0FBQyxDQUFDLGFBQWE7WUFDZixDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFFdEMsMEJBQTBCO1FBQzFCLElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQzlELENBQUM7UUFFRCw0Q0FBNEM7UUFDNUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEVBQUUsRUFBRTtZQUMxRSxJQUFJLEdBQUcsS0FBSyx1QkFBdUIsSUFBSSxLQUFLLEtBQUssU0FBUyxFQUFFLENBQUM7Z0JBQzNELEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDbkIsQ0FBQztZQUNELE9BQU8sR0FBRyxDQUFDO1FBQ2IsQ0FBQyxFQUFFLEVBQVMsQ0FBQyxDQUFDO1FBRWQsNkJBQTZCO1FBQzdCLE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRTdELDREQUE0RDtRQUM1RCxNQUFNLElBQUksQ0FBQyxlQUFlLENBQUMsZUFBZSxDQUFDLFFBQVEsRUFBRSxXQUFXLEVBQUUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBRUQ7Ozs7Ozs7O09BUUc7SUFDSCxzQkFBc0IsQ0FBQyxRQUFnQixFQUFFLE9BQWU7UUFDdEQsTUFBTSxhQUFhLEdBQUcsYUFBYSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsQ0FBQztRQUVuRCwyQkFBMkI7UUFDM0IsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUM7WUFDN0MsQ0FBQyxDQUFDLGFBQWE7WUFDZixDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsYUFBYSxDQUFDLENBQUM7UUFFdEMscUNBQXFDO1FBQ3JDLE1BQU0sY0FBYyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFaEQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUN4QyxlQUFlLENBQUMsZ0JBQWdCLENBQUM7Z0JBQy9CLElBQUksRUFBRSx3QkFBd0I7Z0JBQzlCLFFBQVEsRUFBRSxVQUFVO2dCQUNwQixNQUFNLEVBQUUsOENBQThDO2dCQUN0RCxPQUFPLEVBQUUsK0NBQStDLGFBQWEsRUFBRTthQUN4RSxDQUFDLENBQUM7WUFDSCxNQUFNLElBQUksS0FBSyxDQUFDLGlDQUFpQyxDQUFDLENBQUM7UUFDckQsQ0FBQztRQUVELE9BQU8sY0FBYyxDQUFDO0lBQ3hCLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsZ0JBQWdCLENBQUMsSUFBWSxFQUFFLFlBQW9CLEtBQUs7UUFDdEQsTUFBTSxTQUFTLEdBQUcsYUFBYSxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsQ0FBQztRQUMzQyxNQUFNLFFBQVEsR0FBRyxTQUFTO2FBQ3ZCLFdBQVcsRUFBRTthQUNiLFVBQVUsQ0FBQyxhQUFhLEVBQUUsR0FBRyxDQUFDO2FBQzlCLFVBQVUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDO2FBQ3RCLFVBQVUsQ0FBQyxRQUFRLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFNUIsT0FBTyxHQUFHLFFBQVEsR0FBRyxTQUFTLEVBQUUsQ0FBQztJQUNuQyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsS0FBSyxDQUFDLFVBQVUsQ0FBQyxRQUFnQixFQUFFLE9BQWdCO1FBQ2pELElBQUksQ0FBQztZQUNILE1BQU0sYUFBYSxHQUFHLGFBQWEsQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLENBQUM7WUFDbkQsTUFBTSxRQUFRLEdBQUcsT0FBTztnQkFDdEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLGFBQWEsQ0FBQztnQkFDbkMsQ0FBQyxDQUFDLGFBQWEsQ0FBQztZQUNsQixNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDMUIsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsT0FBTyxLQUFLLENBQUM7UUFDZixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSCxLQUFLLENBQUMsVUFBVSxDQUFDLFFBQWdCLEVBQUUsT0FBZTtRQUNoRCxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsc0JBQXNCLENBQUMsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBRXRFLHFCQUFxQjtRQUNyQixlQUFlLENBQUMsZ0JBQWdCLENBQUM7WUFDL0IsSUFBSSxFQUFFLGlCQUFpQjtZQUN2QixRQUFRLEVBQUUsUUFBUTtZQUNsQixNQUFNLEVBQUUsa0NBQWtDO1lBQzFDLE9BQU8sRUFBRSxpQkFBaUIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsRUFBRTtTQUMxRCxDQUFDLENBQUM7UUFFSCxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILEtBQUssQ0FBQyxTQUFTLENBQUMsR0FBVyxFQUFFLFNBQWtCO1FBQzdDLElBQUksQ0FBQztZQUNILDBCQUEwQjtZQUMxQixNQUFNLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFFekMsTUFBTSxLQUFLLEdBQUcsTUFBTSxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBRXBDLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ2QsT0FBTyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1lBQ2xELENBQUM7WUFFRCxPQUFPLEtBQUssQ0FBQztRQUNmLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsR0FBRyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDdkQsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFbGVtZW50RmlsZU9wZXJhdGlvbnMgLSBDb21tb24gZmlsZSBvcGVyYXRpb25zIGZvciBlbGVtZW50IG1hbmFnZXJzXG4gKlxuICogUHJvdmlkZXMgc2hhcmVkIGZpbGUgaGFuZGxpbmcgb3BlcmF0aW9ucyBmb3IgYWxsIGVsZW1lbnQgdHlwZXM6XG4gKiAtIFJlYWRpbmcgZmlsZXMgd2l0aCBmcm9udG1hdHRlciBwYXJzaW5nXG4gKiAtIFdyaXRpbmcgZmlsZXMgd2l0aCBhdG9taWMgb3BlcmF0aW9uc1xuICogLSBEaXJlY3Rvcnkgc2Nhbm5pbmcgYW5kIGZpbHRlcmluZ1xuICpcbiAqIFNFQ1VSSVRZOiBBbGwgb3BlcmF0aW9ucyB1c2UgRmlsZUxvY2tNYW5hZ2VyIGZvciBhdG9taWMgcmVhZHMvd3JpdGVzXG4gKiBQQVRIIFZBTElEQVRJT046IEFsbCBwYXRocyBhcmUgdmFsaWRhdGVkIGJlZm9yZSBmaWxlIG9wZXJhdGlvbnNcbiAqIExPR0dJTkc6IE9wZXJhdGlvbnMgYXJlIGxvZ2dlZCBmb3IgZGVidWdnaW5nIGFuZCBhdWRpdCB0cmFpbFxuICovXG5cbmltcG9ydCB7IEZpbGVMb2NrTWFuYWdlciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L2ZpbGVMb2NrTWFuYWdlci5qcyc7XG5pbXBvcnQgeyBzYW5pdGl6ZUlucHV0LCB2YWxpZGF0ZVBhdGggfSBmcm9tICcuLi8uLi9zZWN1cml0eS9JbnB1dFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBTZWN1cml0eU1vbml0b3IgfSBmcm9tICcuLi8uLi9zZWN1cml0eS9zZWN1cml0eU1vbml0b3IuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IHByb21pc2VzIGFzIGZzIH0gZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCBtYXR0ZXIgZnJvbSAnZ3JheS1tYXR0ZXInO1xuXG4vKipcbiAqIFJlc3VsdCBvZiBwYXJzaW5nIGEgZmlsZSB3aXRoIGZyb250bWF0dGVyXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFyc2VkRmlsZSB7XG4gIG1ldGFkYXRhOiBhbnk7XG4gIGNvbnRlbnQ6IHN0cmluZztcbiAgcmF3OiBzdHJpbmc7XG59XG5cbi8qKlxuICogT3B0aW9ucyBmb3IgZmlsZSBvcGVyYXRpb25zXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZU9wZXJhdGlvbk9wdGlvbnMge1xuICAvKiogTWF4aW11bSBmaWxlIHNpemUgaW4gYnl0ZXMgKGRlZmF1bHQ6IDFNQikgKi9cbiAgbWF4U2l6ZT86IG51bWJlcjtcbiAgLyoqIFdoZXRoZXIgdG8gdmFsaWRhdGUgcGF0aCBpcyB3aXRoaW4gYmFzZSBkaXJlY3RvcnkgKGRlZmF1bHQ6IHRydWUpICovXG4gIHZhbGlkYXRlUGF0aD86IGJvb2xlYW47XG4gIC8qKiBXaGV0aGVyIHRvIGNyZWF0ZSBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBleGlzdCAoZGVmYXVsdDogdHJ1ZSkgKi9cbiAgZW5zdXJlRGlyPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBVdGlsaXR5IGNsYXNzIGZvciBjb21tb24gZmlsZSBvcGVyYXRpb25zXG4gKiBVc2VkIGJ5IEJhc2VFbGVtZW50TWFuYWdlciBhbmQgY2FuIGJlIHVzZWQgYnkgb3RoZXIgbWFuYWdlcnNcbiAqXG4gKiBERVBFTkRFTkNZIElOSkVDVElPTjogUmVxdWlyZXMgRmlsZUxvY2tNYW5hZ2VyIGluc3RhbmNlIGZvciBhdG9taWMgb3BlcmF0aW9uc1xuICovXG5leHBvcnQgY2xhc3MgRWxlbWVudEZpbGVPcGVyYXRpb25zIHtcbiAgcHJpdmF0ZSBmaWxlTG9ja01hbmFnZXI6IEZpbGVMb2NrTWFuYWdlcjtcblxuICAvKipcbiAgICogQ29uc3RydWN0b3IgLSBhY2NlcHRzIEZpbGVMb2NrTWFuYWdlciBmb3IgYXRvbWljIG9wZXJhdGlvbnNcbiAgICogQHBhcmFtIGZpbGVMb2NrTWFuYWdlciAtIEZpbGVMb2NrTWFuYWdlciBpbnN0YW5jZSBmb3IgYXRvbWljIGZpbGUgb3BlcmF0aW9uc1xuICAgKi9cbiAgY29uc3RydWN0b3IoZmlsZUxvY2tNYW5hZ2VyOiBGaWxlTG9ja01hbmFnZXIpIHtcbiAgICB0aGlzLmZpbGVMb2NrTWFuYWdlciA9IGZpbGVMb2NrTWFuYWdlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBBdG9taWNhbGx5IHJlYWQgYSBmaWxlIHdpdGggZnJvbnRtYXR0ZXJcbiAgICogU0VDVVJJVFk6IFVzZXMgRmlsZUxvY2tNYW5hZ2VyIHRvIHByZXZlbnQgcmFjZSBjb25kaXRpb25zXG4gICAqXG4gICAqIEBwYXJhbSBmaWxlUGF0aCAtIEFic29sdXRlIG9yIHJlbGF0aXZlIHBhdGggdG8gZmlsZVxuICAgKiBAcGFyYW0gYmFzZURpciAtIEJhc2UgZGlyZWN0b3J5IGZvciB2YWxpZGF0aW9uXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gT3BlcmF0aW9uIG9wdGlvbnNcbiAgICogQHJldHVybnMgUGFyc2VkIGZpbGUgd2l0aCBtZXRhZGF0YSBhbmQgY29udGVudFxuICAgKi9cbiAgYXN5bmMgcmVhZEZpbGVXaXRoRnJvbnRtYXR0ZXIoXG4gICAgZmlsZVBhdGg6IHN0cmluZyxcbiAgICBiYXNlRGlyOiBzdHJpbmcsXG4gICAgb3B0aW9uczogRmlsZU9wZXJhdGlvbk9wdGlvbnMgPSB7fVxuICApOiBQcm9taXNlPFBhcnNlZEZpbGU+IHtcbiAgICBjb25zdCB7XG4gICAgICBtYXhTaXplID0gMTAyNCAqIDEwMjQsIC8vIDFNQiBkZWZhdWx0XG4gICAgICB2YWxpZGF0ZVBhdGg6IHNob3VsZFZhbGlkYXRlUGF0aCA9IHRydWVcbiAgICB9ID0gb3B0aW9ucztcblxuICAgIC8vIFNFQ1VSSVRZOiBTYW5pdGl6ZSBwYXRoXG4gICAgY29uc3Qgc2FuaXRpemVkUGF0aCA9IHNhbml0aXplSW5wdXQoZmlsZVBhdGgsIDI1NSk7XG5cbiAgICAvLyBWYWxpZGF0ZSBwYXRoIGlmIHJlcXVlc3RlZFxuICAgIGlmIChzaG91bGRWYWxpZGF0ZVBhdGgpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHZhbGlkYXRlUGF0aChzYW5pdGl6ZWRQYXRoLCBiYXNlRGlyKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBmaWxlIHBhdGg6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAnSW52YWxpZCBwYXRoJ31gKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZXNvbHZlIGZ1bGwgcGF0aFxuICAgIGNvbnN0IGZ1bGxQYXRoID0gcGF0aC5pc0Fic29sdXRlKHNhbml0aXplZFBhdGgpXG4gICAgICA/IHNhbml0aXplZFBhdGhcbiAgICAgIDogcGF0aC5qb2luKGJhc2VEaXIsIHNhbml0aXplZFBhdGgpO1xuXG4gICAgLy8gQ2hlY2sgZmlsZSBzaXplIGJlZm9yZSByZWFkaW5nXG4gICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBmcy5zdGF0KGZ1bGxQYXRoKTtcbiAgICBpZiAoc3RhdHMuc2l6ZSA+IG1heFNpemUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRmlsZSB0b28gbGFyZ2U6ICR7c3RhdHMuc2l6ZX0gYnl0ZXMgKG1heDogJHttYXhTaXplfSlgKTtcbiAgICB9XG5cbiAgICAvLyBDUklUSUNBTCBGSVg6IFVzZSBhdG9taWMgZmlsZSByZWFkIHZpYSBpbmplY3RlZCBpbnN0YW5jZVxuICAgIGNvbnN0IHJhdyA9IGF3YWl0IHRoaXMuZmlsZUxvY2tNYW5hZ2VyLmF0b21pY1JlYWRGaWxlKGZ1bGxQYXRoLCB7IGVuY29kaW5nOiAndXRmLTgnIH0pO1xuXG4gICAgLy8gUGFyc2UgZnJvbnRtYXR0ZXJcbiAgICBjb25zdCBwYXJzZWQgPSBtYXR0ZXIocmF3KTtcblxuICAgIHJldHVybiB7XG4gICAgICBtZXRhZGF0YTogcGFyc2VkLmRhdGEsXG4gICAgICBjb250ZW50OiBwYXJzZWQuY29udGVudCxcbiAgICAgIHJhd1xuICAgIH07XG4gIH1cblxuICAvKipcbiAgICogQXRvbWljYWxseSB3cml0ZSBhIGZpbGUgd2l0aCBmcm9udG1hdHRlclxuICAgKiBTRUNVUklUWTogVXNlcyBGaWxlTG9ja01hbmFnZXIgdG8gcHJldmVudCBjb3JydXB0aW9uXG4gICAqXG4gICAqIEBwYXJhbSBmaWxlUGF0aCAtIEFic29sdXRlIG9yIHJlbGF0aXZlIHBhdGggdG8gZmlsZVxuICAgKiBAcGFyYW0gbWV0YWRhdGEgLSBZQU1MIGZyb250bWF0dGVyIG1ldGFkYXRhXG4gICAqIEBwYXJhbSBjb250ZW50IC0gRmlsZSBjb250ZW50XG4gICAqIEBwYXJhbSBiYXNlRGlyIC0gQmFzZSBkaXJlY3RvcnkgZm9yIHZhbGlkYXRpb25cbiAgICogQHBhcmFtIG9wdGlvbnMgLSBPcGVyYXRpb24gb3B0aW9uc1xuICAgKi9cbiAgYXN5bmMgd3JpdGVGaWxlV2l0aEZyb250bWF0dGVyKFxuICAgIGZpbGVQYXRoOiBzdHJpbmcsXG4gICAgbWV0YWRhdGE6IGFueSxcbiAgICBjb250ZW50OiBzdHJpbmcsXG4gICAgYmFzZURpcjogc3RyaW5nLFxuICAgIG9wdGlvbnM6IEZpbGVPcGVyYXRpb25PcHRpb25zID0ge31cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qge1xuICAgICAgdmFsaWRhdGVQYXRoOiBzaG91bGRWYWxpZGF0ZVBhdGggPSB0cnVlLFxuICAgICAgZW5zdXJlRGlyID0gdHJ1ZVxuICAgIH0gPSBvcHRpb25zO1xuXG4gICAgLy8gU0VDVVJJVFk6IFNhbml0aXplIHBhdGhcbiAgICBjb25zdCBzYW5pdGl6ZWRQYXRoID0gc2FuaXRpemVJbnB1dChmaWxlUGF0aCwgMjU1KTtcblxuICAgIC8vIFZhbGlkYXRlIHBhdGggaWYgcmVxdWVzdGVkXG4gICAgaWYgKHNob3VsZFZhbGlkYXRlUGF0aCkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgdmFsaWRhdGVQYXRoKHNhbml0aXplZFBhdGgsIGJhc2VEaXIpO1xuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGZpbGUgcGF0aDogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdJbnZhbGlkIHBhdGgnfWApO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJlc29sdmUgZnVsbCBwYXRoXG4gICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmlzQWJzb2x1dGUoc2FuaXRpemVkUGF0aClcbiAgICAgID8gc2FuaXRpemVkUGF0aFxuICAgICAgOiBwYXRoLmpvaW4oYmFzZURpciwgc2FuaXRpemVkUGF0aCk7XG5cbiAgICAvLyBFbnN1cmUgZGlyZWN0b3J5IGV4aXN0c1xuICAgIGlmIChlbnN1cmVEaXIpIHtcbiAgICAgIGF3YWl0IGZzLm1rZGlyKHBhdGguZGlybmFtZShmdWxsUGF0aCksIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgIH1cblxuICAgIC8vIENsZWFuIG1ldGFkYXRhIHRvIHJlbW92ZSB1bmRlZmluZWQgdmFsdWVzXG4gICAgY29uc3QgY2xlYW5NZXRhZGF0YSA9IE9iamVjdC5lbnRyaWVzKG1ldGFkYXRhKS5yZWR1Y2UoKGFjYywgW2tleSwgdmFsdWVdKSA9PiB7XG4gICAgICBpZiAoa2V5ICE9PSAnZ2F0ZWtlZXBlckRpYWdub3N0aWNzJyAmJiB2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGFjY1trZXldID0gdmFsdWU7XG4gICAgICB9XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIHt9IGFzIGFueSk7XG5cbiAgICAvLyBDcmVhdGUgZnJvbnRtYXR0ZXIgY29udGVudFxuICAgIGNvbnN0IGZpbGVDb250ZW50ID0gbWF0dGVyLnN0cmluZ2lmeShjb250ZW50LCBjbGVhbk1ldGFkYXRhKTtcblxuICAgIC8vIENSSVRJQ0FMIEZJWDogVXNlIGF0b21pYyBmaWxlIHdyaXRlIHZpYSBpbmplY3RlZCBpbnN0YW5jZVxuICAgIGF3YWl0IHRoaXMuZmlsZUxvY2tNYW5hZ2VyLmF0b21pY1dyaXRlRmlsZShmdWxsUGF0aCwgZmlsZUNvbnRlbnQsIHsgZW5jb2Rpbmc6ICd1dGYtOCcgfSk7XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGUgcGF0aCBpcyB3aXRoaW4gYmFzZSBkaXJlY3RvcnkgYW5kIHJlc29sdmUgdG8gYWJzb2x1dGUgcGF0aFxuICAgKiBTRUNVUklUWTogUHJldmVudHMgcGF0aCB0cmF2ZXJzYWwgYXR0YWNrc1xuICAgKlxuICAgKiBAcGFyYW0gZmlsZVBhdGggLSBQYXRoIHRvIHZhbGlkYXRlXG4gICAqIEBwYXJhbSBiYXNlRGlyIC0gQmFzZSBkaXJlY3RvcnlcbiAgICogQHJldHVybnMgTm9ybWFsaXplZCBhYnNvbHV0ZSBwYXRoXG4gICAqIEB0aHJvd3MgRXJyb3IgaWYgcGF0aCBpcyBpbnZhbGlkIG9yIG91dHNpZGUgYmFzZSBkaXJlY3RvcnlcbiAgICovXG4gIHZhbGlkYXRlQW5kUmVzb2x2ZVBhdGgoZmlsZVBhdGg6IHN0cmluZywgYmFzZURpcjogc3RyaW5nKTogc3RyaW5nIHtcbiAgICBjb25zdCBzYW5pdGl6ZWRQYXRoID0gc2FuaXRpemVJbnB1dChmaWxlUGF0aCwgMjU1KTtcblxuICAgIC8vIFJlc29sdmUgdG8gYWJzb2x1dGUgcGF0aFxuICAgIGNvbnN0IGZ1bGxQYXRoID0gcGF0aC5pc0Fic29sdXRlKHNhbml0aXplZFBhdGgpXG4gICAgICA/IHNhbml0aXplZFBhdGhcbiAgICAgIDogcGF0aC5qb2luKGJhc2VEaXIsIHNhbml0aXplZFBhdGgpO1xuXG4gICAgLy8gTm9ybWFsaXplIGFuZCBjaGVjayBwYXRoIHRyYXZlcnNhbFxuICAgIGNvbnN0IG5vcm1hbGl6ZWRQYXRoID0gcGF0aC5ub3JtYWxpemUoZnVsbFBhdGgpO1xuXG4gICAgaWYgKCFub3JtYWxpemVkUGF0aC5zdGFydHNXaXRoKGJhc2VEaXIpKSB7XG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdQQVRIX1RSQVZFUlNBTF9BVFRFTVBUJyxcbiAgICAgICAgc2V2ZXJpdHk6ICdDUklUSUNBTCcsXG4gICAgICAgIHNvdXJjZTogJ0VsZW1lbnRGaWxlT3BlcmF0aW9ucy52YWxpZGF0ZUFuZFJlc29sdmVQYXRoJyxcbiAgICAgICAgZGV0YWlsczogYEF0dGVtcHRlZCB0byBhY2Nlc3MgZmlsZSBvdXRzaWRlIGRpcmVjdG9yeTogJHtzYW5pdGl6ZWRQYXRofWBcbiAgICAgIH0pO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdQYXRoIHRyYXZlcnNhbCBhdHRlbXB0IGRldGVjdGVkJyk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5vcm1hbGl6ZWRQYXRoO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIGZpbGVuYW1lIGZyb20gZWxlbWVudCBuYW1lXG4gICAqIENvbnZlcnRzIG5hbWUgdG8gbG93ZXJjYXNlIGFuZCByZXBsYWNlcyBpbnZhbGlkIGNoYXJhY3RlcnMgd2l0aCBoeXBoZW5zXG4gICAqXG4gICAqIEBwYXJhbSBuYW1lIC0gRWxlbWVudCBuYW1lXG4gICAqIEBwYXJhbSBleHRlbnNpb24gLSBGaWxlIGV4dGVuc2lvbiAoZGVmYXVsdDogJy5tZCcpXG4gICAqIEByZXR1cm5zIFNhbml0aXplZCBmaWxlbmFtZVxuICAgKi9cbiAgZ2VuZXJhdGVGaWxlbmFtZShuYW1lOiBzdHJpbmcsIGV4dGVuc2lvbjogc3RyaW5nID0gJy5tZCcpOiBzdHJpbmcge1xuICAgIGNvbnN0IHNhbml0aXplZCA9IHNhbml0aXplSW5wdXQobmFtZSwgMTAwKTtcbiAgICBjb25zdCBmaWxlbmFtZSA9IHNhbml0aXplZFxuICAgICAgLnRvTG93ZXJDYXNlKClcbiAgICAgIC5yZXBsYWNlQWxsKC9bXmEtejAtOS1dL2csICctJylcbiAgICAgIC5yZXBsYWNlQWxsKC8tKy9nLCAnLScpXG4gICAgICAucmVwbGFjZUFsbCgvXi18LSQvZywgJycpO1xuXG4gICAgcmV0dXJuIGAke2ZpbGVuYW1lfSR7ZXh0ZW5zaW9ufWA7XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgYSBmaWxlIGV4aXN0c1xuICAgKlxuICAgKiBAcGFyYW0gZmlsZVBhdGggLSBQYXRoIHRvIGNoZWNrXG4gICAqIEBwYXJhbSBiYXNlRGlyIC0gQmFzZSBkaXJlY3RvcnkgKG9wdGlvbmFsKVxuICAgKiBAcmV0dXJucyBUcnVlIGlmIGZpbGUgZXhpc3RzXG4gICAqL1xuICBhc3luYyBmaWxlRXhpc3RzKGZpbGVQYXRoOiBzdHJpbmcsIGJhc2VEaXI/OiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc2FuaXRpemVkUGF0aCA9IHNhbml0aXplSW5wdXQoZmlsZVBhdGgsIDI1NSk7XG4gICAgICBjb25zdCBmdWxsUGF0aCA9IGJhc2VEaXJcbiAgICAgICAgPyBwYXRoLmpvaW4oYmFzZURpciwgc2FuaXRpemVkUGF0aClcbiAgICAgICAgOiBzYW5pdGl6ZWRQYXRoO1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGZ1bGxQYXRoKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2gge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWxldGUgYSBmaWxlIHdpdGggdmFsaWRhdGlvblxuICAgKiBTRUNVUklUWTogVmFsaWRhdGVzIHBhdGggYmVmb3JlIGRlbGV0aW9uXG4gICAqXG4gICAqIEBwYXJhbSBmaWxlUGF0aCAtIFBhdGggdG8gZGVsZXRlXG4gICAqIEBwYXJhbSBiYXNlRGlyIC0gQmFzZSBkaXJlY3RvcnkgZm9yIHZhbGlkYXRpb25cbiAgICogQHRocm93cyBFcnJvciBpZiBwYXRoIGlzIGludmFsaWRcbiAgICovXG4gIGFzeW5jIGRlbGV0ZUZpbGUoZmlsZVBhdGg6IHN0cmluZywgYmFzZURpcjogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3Qgbm9ybWFsaXplZFBhdGggPSB0aGlzLnZhbGlkYXRlQW5kUmVzb2x2ZVBhdGgoZmlsZVBhdGgsIGJhc2VEaXIpO1xuXG4gICAgLy8gTG9nIHNlY3VyaXR5IGV2ZW50XG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0VMRU1FTlRfREVMRVRFRCcsXG4gICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICBzb3VyY2U6ICdFbGVtZW50RmlsZU9wZXJhdGlvbnMuZGVsZXRlRmlsZScsXG4gICAgICBkZXRhaWxzOiBgRmlsZSBkZWxldGVkOiAke3BhdGguYmFzZW5hbWUobm9ybWFsaXplZFBhdGgpfWBcbiAgICB9KTtcblxuICAgIGF3YWl0IGZzLnVubGluayhub3JtYWxpemVkUGF0aCk7XG4gIH1cblxuICAvKipcbiAgICogTGlzdCBmaWxlcyBpbiBhIGRpcmVjdG9yeVxuICAgKlxuICAgKiBAcGFyYW0gZGlyIC0gRGlyZWN0b3J5IHRvIGxpc3RcbiAgICogQHBhcmFtIGV4dGVuc2lvbiAtIEZpbHRlciBieSBleHRlbnNpb24gKG9wdGlvbmFsKVxuICAgKiBAcmV0dXJucyBBcnJheSBvZiBmaWxlIHBhdGhzXG4gICAqL1xuICBhc3luYyBsaXN0RmlsZXMoZGlyOiBzdHJpbmcsIGV4dGVuc2lvbj86IHN0cmluZyk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICB0cnkge1xuICAgICAgLy8gRW5zdXJlIGRpcmVjdG9yeSBleGlzdHNcbiAgICAgIGF3YWl0IGZzLm1rZGlyKGRpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG5cbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihkaXIpO1xuXG4gICAgICBpZiAoZXh0ZW5zaW9uKSB7XG4gICAgICAgIHJldHVybiBmaWxlcy5maWx0ZXIoZiA9PiBmLmVuZHNXaXRoKGV4dGVuc2lvbikpO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gZmlsZXM7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcihgRmFpbGVkIHRvIGxpc3QgZmlsZXMgaW4gJHtkaXJ9OmAsIGVycm9yKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==