@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
JavaScript
/**
* 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==