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.

370 lines 49.6 kB
/** * Manage backups during updates */ import * as fs from 'fs/promises'; import * as fsSync from 'fs'; import * as path from 'path'; import { safeExec } from '../utils/git.js'; import { logger } from '../utils/logger.js'; import { FileOperations } from '../utils/fileOperations.js'; export class BackupManager { rootDir; backupsDir; constructor(rootDir) { // Validate rootDir parameter if provided if (rootDir) { // Prevent path traversal attacks first if (rootDir.includes('../') || rootDir.includes('..\\')) { throw new Error('rootDir cannot contain path traversal sequences'); } // Then check if it's absolute if (!path.isAbsolute(rootDir)) { throw new Error('rootDir must be an absolute path'); } } // Allow override for testing, default to process.cwd() this.rootDir = rootDir || process.cwd(); // Safety check: Don't allow operations on directories containing critical files // This prevents accidental deletion of the actual project directory if (this.hasProductionFiles() && !this.isSafeTestDirectory()) { throw new Error('BackupManager cannot operate on production directory. Pass a safe test directory to the constructor.'); } this.backupsDir = path.join(this.rootDir, "..", "dollhousemcp-backups"); } /** * Check if the directory contains production files */ hasProductionFiles() { try { const productionIndicators = [ 'package.json', 'tsconfig.json', '.git', 'src', 'LICENSE' ]; const files = fsSync.readdirSync(this.rootDir); const hasProductionFile = productionIndicators.some(indicator => files.includes(indicator)); // Additional check: if package.json exists, check if it's a real project if (hasProductionFile && files.includes('package.json')) { const packageJsonPath = path.join(this.rootDir, 'package.json'); const packageJson = JSON.parse(fsSync.readFileSync(packageJsonPath, 'utf-8')); // If it has a name and dependencies, it's likely a real project return !!(packageJson.name && packageJson.dependencies); } return hasProductionFile; } catch { // If we can't read the directory, assume it's safe return false; } } /** * Check if this appears to be a safe test directory */ isSafeTestDirectory() { const safePaths = ['test', 'tmp', 'temp', '.test', '__test__']; const dirPath = this.rootDir.toLowerCase(); // Check if running in Docker container if (this.isDockerEnvironment()) { return true; // Docker containers are immutable, updates don't apply } return safePaths.some(safe => dirPath.includes(safe)); } /** * Check if running in a Docker container */ isDockerEnvironment() { // Check common Docker indicators if (process.env.DOLLHOUSE_DISABLE_UPDATES === 'true') { return true; } // Check if running from /app directory (common Docker practice) if (this.rootDir === '/app') { return true; } // Check for Docker-specific files try { const hasDockerEnv = fsSync.existsSync('/.dockerenv'); if (hasDockerEnv) { return true; } } catch { // Ignore errors } return false; } /** * Create a backup of the current installation */ async createBackup(version) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupName = `backup-${timestamp}`; const backupPath = path.join(this.backupsDir, backupName); // Ensure backups directory exists await fs.mkdir(this.backupsDir, { recursive: true }); // Use git to create a clean copy (respecting .gitignore) await safeExec('git', [ 'archive', '--format=tar', 'HEAD' ], { cwd: this.rootDir }).then(async ({ stdout }) => { // Create backup directory await fs.mkdir(backupPath, { recursive: true }); // Extract tar to backup directory const tarPath = path.join(backupPath, 'archive.tar'); await fs.writeFile(tarPath, stdout); // Extract using tar command await safeExec('tar', ['-xf', 'archive.tar'], { cwd: backupPath }); await fs.unlink(tarPath); }); // Also backup node_modules if it exists const nodeModulesPath = path.join(this.rootDir, 'node_modules'); try { await fs.access(nodeModulesPath); const destNodeModules = path.join(backupPath, 'node_modules'); // Use cross-platform file copy instead of Unix-specific cp -r await FileOperations.copyDirectory(nodeModulesPath, destNodeModules, { excludePatterns: ['.bin', '.cache'], onProgress: (copied, total) => { if (copied % 100 === 0) { logger.debug(`[BackupManager] Backing up node_modules: ${copied}/${total} files`); } } }); } catch (error) { // node_modules doesn't exist or copy failed, that's okay logger.debug('[BackupManager] Could not backup node_modules:', error); } // Backup all persona files (including user-created ones not in git) const personasDir = path.join(this.rootDir, 'personas'); const backupPersonasDir = path.join(backupPath, 'personas'); try { await fs.access(personasDir); await fs.mkdir(backupPersonasDir, { recursive: true }); const personaFiles = await fs.readdir(personasDir); for (const file of personaFiles) { if (file.endsWith('.md')) { const sourcePath = path.join(personasDir, file); const destPath = path.join(backupPersonasDir, file); // Copy the file, overwriting if it already exists from git archive await fs.copyFile(sourcePath, destPath); } } } catch (error) { // Log warning but don't fail the backup logger.warn('Could not backup all personas:', error); } // Save backup metadata const metadata = { timestamp, version, createdAt: new Date().toISOString() }; await fs.writeFile(path.join(backupPath, 'backup-metadata.json'), JSON.stringify(metadata, null, 2)); return { path: backupPath, timestamp, version }; } /** * Create a backup specifically for npm installations */ async createNpmBackup(npmGlobalPath, version) { try { // Create npm-specific backup directory const npmBackupsDir = path.join(path.dirname(this.backupsDir), '.dollhouse', 'backups', 'npm'); await fs.mkdir(npmBackupsDir, { recursive: true }); // Create timestamp-based backup directory const timestamp = new Date().toISOString().replace(/:/g, '-').replace(/\./g, '-'); const backupName = `npm-backup-${timestamp}`; const backupPath = path.join(npmBackupsDir, backupName); logger.info(`[BackupManager] Creating npm backup at: ${backupPath}`); // Create backup directory await fs.mkdir(backupPath, { recursive: true }); // Copy the npm package directory await this.copyDirectory(npmGlobalPath, path.join(backupPath, 'package')); // Save backup metadata const metadata = { timestamp, version, npmGlobalPath, installationType: 'npm', createdAt: new Date().toISOString() }; await fs.writeFile(path.join(backupPath, 'backup-metadata.json'), JSON.stringify(metadata, null, 2)); // Update manifest await this.updateNpmBackupManifest(npmBackupsDir, { backupName, timestamp, version, path: backupPath }); // Cleanup old npm backups (keep last 3) await this.cleanupOldNpmBackups(npmBackupsDir, 3); return backupPath; } catch (error) { logger.error('[BackupManager] Failed to create npm backup:', error); throw error; } } /** * Copy directory recursively with progress reporting * @deprecated Use FileOperations.copyDirectory instead */ async copyDirectory(src, dest) { await FileOperations.copyDirectory(src, dest, { excludePatterns: ['.git'], onProgress: (copied, total, file) => { if (copied % 50 === 0) { logger.debug(`[BackupManager] Copying: ${copied}/${total} files`); } } }); } /** * Update npm backup manifest */ async updateNpmBackupManifest(npmBackupsDir, backupInfo) { const manifestPath = path.join(npmBackupsDir, 'manifest.json'); let manifest = { backups: [] }; try { const content = await fs.readFile(manifestPath, 'utf-8'); manifest = JSON.parse(content); } catch { // File doesn't exist or is invalid, use default } // Add new backup to beginning of list manifest.backups.unshift(backupInfo); // Keep only last 10 entries in manifest manifest.backups = manifest.backups.slice(0, 10); await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2)); } /** * Clean up old npm backups */ async cleanupOldNpmBackups(npmBackupsDir, keepCount) { try { const entries = await fs.readdir(npmBackupsDir, { withFileTypes: true }); const backupDirs = entries .filter(e => e.isDirectory() && e.name.startsWith('npm-backup-')) .map(e => e.name) .sort() .reverse(); // Remove old backups const toRemove = backupDirs.slice(keepCount); for (const dir of toRemove) { const dirPath = path.join(npmBackupsDir, dir); logger.info(`[BackupManager] Removing old npm backup: ${dir}`); await fs.rm(dirPath, { recursive: true, force: true }); } } catch (error) { logger.warn('[BackupManager] Failed to cleanup old npm backups:', error); } } /** * List available backups */ async listBackups() { try { const entries = await fs.readdir(this.backupsDir, { withFileTypes: true }); const backups = []; for (const entry of entries) { if (entry.isDirectory() && entry.name.startsWith('backup-')) { const backupPath = path.join(this.backupsDir, entry.name); const timestamp = entry.name.replace('backup-', ''); // Try to read metadata let version; try { const metadataPath = path.join(backupPath, 'backup-metadata.json'); const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf-8')); version = metadata.version; } catch { // No metadata file, that's okay } backups.push({ path: backupPath, timestamp, version }); } } // Sort by timestamp descending (newest first) return backups.sort((a, b) => b.timestamp.localeCompare(a.timestamp)); } catch { return []; } } /** * Get the most recent backup */ async getLatestBackup() { const backups = await this.listBackups(); return backups.length > 0 ? backups[0] : null; } /** * Restore from a backup */ async restoreBackup(backupPath) { // Verify backup exists try { await fs.access(backupPath); } catch { throw new Error(`Backup not found: ${backupPath}`); } // Create a temporary directory for the current state const tempDir = path.join(this.backupsDir, 'temp-current'); await fs.mkdir(tempDir, { recursive: true }); // Move current files to temp (except .git and node_modules) const entries = await fs.readdir(this.rootDir); for (const entry of entries) { if (entry !== '.git' && entry !== 'node_modules' && entry !== 'dist') { const sourcePath = path.join(this.rootDir, entry); const destPath = path.join(tempDir, entry); await fs.rename(sourcePath, destPath); } } // Copy backup files to root const backupEntries = await fs.readdir(backupPath); for (const entry of backupEntries) { if (entry !== 'backup-metadata.json' && entry !== '.git') { const sourcePath = path.join(backupPath, entry); const destPath = path.join(this.rootDir, entry); await safeExec('cp', ['-r', sourcePath, destPath]); } } // Clean up temp directory await fs.rm(tempDir, { recursive: true, force: true }); } /** * Clean up old backups (keep the 5 most recent) */ async cleanupOldBackups(keepCount = 5) { const backups = await this.listBackups(); let deletedCount = 0; if (backups.length > keepCount) { const backupsToDelete = backups.slice(keepCount); for (const backup of backupsToDelete) { try { await fs.rm(backup.path, { recursive: true, force: true }); deletedCount++; } catch (error) { logger.error(`Failed to delete backup ${backup.path}:`, error); } } } return deletedCount; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFja3VwTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91cGRhdGUvQmFja3VwTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxNQUFNLE1BQU0sSUFBSSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMzQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDNUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBUTVELE1BQU0sT0FBTyxhQUFhO0lBQ2hCLE9BQU8sQ0FBUztJQUNoQixVQUFVLENBQVM7SUFFM0IsWUFBWSxPQUFnQjtRQUMxQix5Q0FBeUM7UUFDekMsSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUNaLHVDQUF1QztZQUN2QyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dCQUN4RCxNQUFNLElBQUksS0FBSyxDQUFDLGlEQUFpRCxDQUFDLENBQUM7WUFDckUsQ0FBQztZQUNELDhCQUE4QjtZQUM5QixJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO2dCQUM5QixNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7WUFDdEQsQ0FBQztRQUNILENBQUM7UUFFRCx1REFBdUQ7UUFDdkQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBRXhDLGdGQUFnRjtRQUNoRixvRUFBb0U7UUFDcEUsSUFBSSxJQUFJLENBQUMsa0JBQWtCLEVBQUUs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Q0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNYW5hZ2UgYmFja3VwcyBkdXJpbmcgdXBkYXRlc1xuICovXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzL3Byb21pc2VzJztcbmltcG9ydCAqIGFzIGZzU3luYyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgc2FmZUV4ZWMgfSBmcm9tICcuLi91dGlscy9naXQuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IEZpbGVPcGVyYXRpb25zIH0gZnJvbSAnLi4vdXRpbHMvZmlsZU9wZXJhdGlvbnMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEJhY2t1cEluZm8ge1xuICBwYXRoOiBzdHJpbmc7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICB2ZXJzaW9uPzogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgQmFja3VwTWFuYWdlciB7XG4gIHByaXZhdGUgcm9vdERpcjogc3RyaW5nO1xuICBwcml2YXRlIGJhY2t1cHNEaXI6IHN0cmluZztcbiAgXG4gIGNvbnN0cnVjdG9yKHJvb3REaXI/OiBzdHJpbmcpIHtcbiAgICAvLyBWYWxpZGF0ZSByb290RGlyIHBhcmFtZXRlciBpZiBwcm92aWRlZFxuICAgIGlmIChyb290RGlyKSB7XG4gICAgICAvLyBQcmV2ZW50IHBhdGggdHJhdmVyc2FsIGF0dGFja3MgZmlyc3RcbiAgICAgIGlmIChyb290RGlyLmluY2x1ZGVzKCcuLi8nKSB8fCByb290RGlyLmluY2x1ZGVzKCcuLlxcXFwnKSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3Jvb3REaXIgY2Fubm90IGNvbnRhaW4gcGF0aCB0cmF2ZXJzYWwgc2VxdWVuY2VzJyk7XG4gICAgICB9XG4gICAgICAvLyBUaGVuIGNoZWNrIGlmIGl0J3MgYWJzb2x1dGVcbiAgICAgIGlmICghcGF0aC5pc0Fic29sdXRlKHJvb3REaXIpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcigncm9vdERpciBtdXN0IGJlIGFuIGFic29sdXRlIHBhdGgnKTtcbiAgICAgIH1cbiAgICB9XG4gICAgXG4gICAgLy8gQWxsb3cgb3ZlcnJpZGUgZm9yIHRlc3RpbmcsIGRlZmF1bHQgdG8gcHJvY2Vzcy5jd2QoKVxuICAgIHRoaXMucm9vdERpciA9IHJvb3REaXIgfHwgcHJvY2Vzcy5jd2QoKTtcbiAgICBcbiAgICAvLyBTYWZldHkgY2hlY2s6IERvbid0IGFsbG93IG9wZXJhdGlvbnMgb24gZGlyZWN0b3JpZXMgY29udGFpbmluZyBjcml0aWNhbCBmaWxlc1xuICAgIC8vIFRoaXMgcHJldmVudHMgYWNjaWRlbnRhbCBkZWxldGlvbiBvZiB0aGUgYWN0dWFsIHByb2plY3QgZGlyZWN0b3J5XG4gICAgaWYgKHRoaXMuaGFzUHJvZHVjdGlvbkZpbGVzKCkgJiYgIXRoaXMuaXNTYWZlVGVzdERpcmVjdG9yeSgpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0JhY2t1cE1hbmFnZXIgY2Fubm90IG9wZXJhdGUgb24gcHJvZHVjdGlvbiBkaXJlY3RvcnkuIFBhc3MgYSBzYWZlIHRlc3QgZGlyZWN0b3J5IHRvIHRoZSBjb25zdHJ1Y3Rvci4nKTtcbiAgICB9XG4gICAgXG4gICAgdGhpcy5iYWNrdXBzRGlyID0gcGF0aC5qb2luKHRoaXMucm9vdERpciwgXCIuLlwiLCBcImRvbGxob3VzZW1jcC1iYWNrdXBzXCIpO1xuICB9XG4gIFxuICAvKipcbiAgICogQ2hlY2sgaWYgdGhlIGRpcmVjdG9yeSBjb250YWlucyBwcm9kdWN0aW9uIGZpbGVzXG4gICAqL1xuICBwcml2YXRlIGhhc1Byb2R1Y3Rpb25GaWxlcygpOiBib29sZWFuIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcHJvZHVjdGlvbkluZGljYXRvcnMgPSBbXG4gICAgICAgICdwYWNrYWdlLmpzb24nLFxuICAgICAgICAndHNjb25maWcuanNvbicsXG4gICAgICAgICcuZ2l0JyxcbiAgICAgICAgJ3NyYycsXG4gICAgICAgICdMSUNFTlNFJ1xuICAgICAgXTtcbiAgICAgIFxuICAgICAgY29uc3QgZmlsZXMgPSBmc1N5bmMucmVhZGRpclN5bmModGhpcy5yb290RGlyKTtcbiAgICAgIGNvbnN0IGhhc1Byb2R1Y3Rpb25GaWxlID0gcHJvZHVjdGlvbkluZGljYXRvcnMuc29tZShpbmRpY2F0b3IgPT4gXG4gICAgICAgIGZpbGVzLmluY2x1ZGVzKGluZGljYXRvcilcbiAgICAgICk7XG4gICAgICBcbiAgICAgIC8vIEFkZGl0aW9uYWwgY2hlY2s6IGlmIHBhY2thZ2UuanNvbiBleGlzdHMsIGNoZWNrIGlmIGl0J3MgYSByZWFsIHByb2plY3RcbiAgICAgIGlmIChoYXNQcm9kdWN0aW9uRmlsZSAmJiBmaWxlcy5pbmNsdWRlcygncGFja2FnZS5qc29uJykpIHtcbiAgICAgICAgY29uc3QgcGFja2FnZUpzb25QYXRoID0gcGF0aC5qb2luKHRoaXMucm9vdERpciwgJ3BhY2thZ2UuanNvbicpO1xuICAgICAgICBjb25zdCBwYWNrYWdlSnNvbiA9IEpTT04ucGFyc2UoZnNTeW5jLnJlYWRGaWxlU3luYyhwYWNrYWdlSnNvblBhdGgsICd1dGYtOCcpKTtcbiAgICAgICAgLy8gSWYgaXQgaGFzIGEgbmFtZSBhbmQgZGVwZW5kZW5jaWVzLCBpdCdzIGxpa2VseSBhIHJlYWwgcHJvamVjdFxuICAgICAgICByZXR1cm4gISEocGFja2FnZUpzb24ubmFtZSAmJiBwYWNrYWdlSnNvbi5kZXBlbmRlbmNpZXMpO1xuICAgICAgfVxuICAgICAgXG4gICAgICByZXR1cm4gaGFzUHJvZHVjdGlvbkZpbGU7XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBJZiB3ZSBjYW4ndCByZWFkIHRoZSBkaXJlY3RvcnksIGFzc3VtZSBpdCdzIHNhZmVcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiB0aGlzIGFwcGVhcnMgdG8gYmUgYSBzYWZlIHRlc3QgZGlyZWN0b3J5XG4gICAqL1xuICBwcml2YXRlIGlzU2FmZVRlc3REaXJlY3RvcnkoKTogYm9vbGVhbiB7XG4gICAgY29uc3Qgc2FmZVBhdGhzID0gWyd0ZXN0JywgJ3RtcCcsICd0ZW1wJywgJy50ZXN0JywgJ19fdGVzdF9fJ107XG4gICAgY29uc3QgZGlyUGF0aCA9IHRoaXMucm9vdERpci50b0xvd2VyQ2FzZSgpO1xuICAgIFxuICAgIC8vIENoZWNrIGlmIHJ1bm5pbmcgaW4gRG9ja2VyIGNvbnRhaW5lclxuICAgIGlmICh0aGlzLmlzRG9ja2VyRW52aXJvbm1lbnQoKSkge1xuICAgICAgcmV0dXJuIHRydWU7IC8vIERvY2tlciBjb250YWluZXJzIGFyZSBpbW11dGFibGUsIHVwZGF0ZXMgZG9uJ3QgYXBwbHlcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHNhZmVQYXRocy5zb21lKHNhZmUgPT4gZGlyUGF0aC5pbmNsdWRlcyhzYWZlKSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBydW5uaW5nIGluIGEgRG9ja2VyIGNvbnRhaW5lclxuICAgKi9cbiAgcHJpdmF0ZSBpc0RvY2tlckVudmlyb25tZW50KCk6IGJvb2xlYW4ge1xuICAgIC8vIENoZWNrIGNvbW1vbiBEb2NrZXIgaW5kaWNhdG9yc1xuICAgIGlmIChwcm9jZXNzLmVudi5ET0xMSE9VU0VfRElTQUJMRV9VUERBVEVTID09PSAndHJ1ZScpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICBcbiAgICAvLyBDaGVjayBpZiBydW5uaW5nIGZyb20gL2FwcCBkaXJlY3RvcnkgKGNvbW1vbiBEb2NrZXIgcHJhY3RpY2UpXG4gICAgaWYgKHRoaXMucm9vdERpciA9PT0gJy9hcHAnKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ2hlY2sgZm9yIERvY2tlci1zcGVjaWZpYyBmaWxlc1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBoYXNEb2NrZXJFbnYgPSBmc1N5bmMuZXhpc3RzU3luYygnLy5kb2NrZXJlbnYnKTtcbiAgICAgIGlmIChoYXNEb2NrZXJFbnYpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBJZ25vcmUgZXJyb3JzXG4gICAgfVxuICAgIFxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENyZWF0ZSBhIGJhY2t1cCBvZiB0aGUgY3VycmVudCBpbnN0YWxsYXRpb25cbiAgICovXG4gIGFzeW5jIGNyZWF0ZUJhY2t1cCh2ZXJzaW9uPzogc3RyaW5nKTogUHJvbWlzZTxCYWNrdXBJbmZvPiB7XG4gICAgY29uc3QgdGltZXN0YW1wID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2UoL1s6Ll0vZywgJy0nKTtcbiAgICBjb25zdCBiYWNrdXBOYW1lID0gYGJhY2t1cC0ke3RpbWVzdGFtcH1gO1xuICAgIGNvbnN0IGJhY2t1cFBhdGggPSBwYXRoLmpvaW4odGhpcy5iYWNrdXBzRGlyLCBiYWNrdXBOYW1lKTtcbiAgICBcbiAgICAvLyBFbnN1cmUgYmFja3VwcyBkaXJlY3RvcnkgZXhpc3RzXG4gICAgYXdhaXQgZnMubWtkaXIodGhpcy5iYWNrdXBzRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBcbiAgICAvLyBVc2UgZ2l0IHRvIGNyZWF0ZSBhIGNsZWFuIGNvcHkgKHJlc3BlY3RpbmcgLmdpdGlnbm9yZSlcbiAgICBhd2FpdCBzYWZlRXhlYygnZ2l0JywgW1xuICAgICAgJ2FyY2hpdmUnLFxuICAgICAgJy0tZm9ybWF0PXRhcicsXG4gICAgICAnSEVBRCdcbiAgICBdLCB7IGN3ZDogdGhpcy5yb290RGlyIH0pLnRoZW4oYXN5bmMgKHsgc3Rkb3V0IH0pID0+IHtcbiAgICAgIC8vIENyZWF0ZSBiYWNrdXAgZGlyZWN0b3J5XG4gICAgICBhd2FpdCBmcy5ta2RpcihiYWNrdXBQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIFxuICAgICAgLy8gRXh0cmFjdCB0YXIgdG8gYmFja3VwIGRpcmVjdG9yeVxuICAgICAgY29uc3QgdGFyUGF0aCA9IHBhdGguam9pbihiYWNrdXBQYXRoLCAnYXJjaGl2ZS50YXInKTtcbiAgICAgIGF3YWl0IGZzLndyaXRlRmlsZSh0YXJQYXRoLCBzdGRvdXQpO1xuICAgICAgXG4gICAgICAvLyBFeHRyYWN0IHVzaW5nIHRhciBjb21tYW5kXG4gICAgICBhd2FpdCBzYWZlRXhlYygndGFyJywgWycteGYnLCAnYXJjaGl2ZS50YXInXSwgeyBjd2Q6IGJhY2t1cFBhdGggfSk7XG4gICAgICBhd2FpdCBmcy51bmxpbmsodGFyUGF0aCk7XG4gICAgfSk7XG4gICAgXG4gICAgLy8gQWxzbyBiYWNrdXAgbm9kZV9tb2R1bGVzIGlmIGl0IGV4aXN0c1xuICAgIGNvbnN0IG5vZGVNb2R1bGVzUGF0aCA9IHBhdGguam9pbih0aGlzLnJvb3REaXIsICdub2RlX21vZHVsZXMnKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKG5vZGVNb2R1bGVzUGF0aCk7XG4gICAgICBjb25zdCBkZXN0Tm9kZU1vZHVsZXMgPSBwYXRoLmpvaW4oYmFja3VwUGF0aCwgJ25vZGVfbW9kdWxlcycpO1xuICAgICAgXG4gICAgICAvLyBVc2UgY3Jvc3MtcGxhdGZvcm0gZmlsZSBjb3B5IGluc3RlYWQgb2YgVW5peC1zcGVjaWZpYyBjcCAtclxuICAgICAgYXdhaXQgRmlsZU9wZXJhdGlvbnMuY29weURpcmVjdG9yeShub2RlTW9kdWxlc1BhdGgsIGRlc3ROb2RlTW9kdWxlcywge1xuICAgICAgICBleGNsdWRlUGF0dGVybnM6IFsnLmJpbicsICcuY2FjaGUnXSxcbiAgICAgICAgb25Qcm9ncmVzczogKGNvcGllZCwgdG90YWwpID0+IHtcbiAgICAgICAgICBpZiAoY29waWVkICUgMTAwID09PSAwKSB7XG4gICAgICAgICAgICBsb2dnZXIuZGVidWcoYFtCYWNrdXBNYW5hZ2VyXSBCYWNraW5nIHVwIG5vZGVfbW9kdWxlczogJHtjb3BpZWR9LyR7dG90YWx9IGZpbGVzYCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgLy8gbm9kZV9tb2R1bGVzIGRvZXNuJ3QgZXhpc3Qgb3IgY29weSBmYWlsZWQsIHRoYXQncyBva2F5XG4gICAgICBsb2dnZXIuZGVidWcoJ1tCYWNrdXBNYW5hZ2VyXSBDb3VsZCBub3QgYmFja3VwIG5vZGVfbW9kdWxlczonLCBlcnJvcik7XG4gICAgfVxuICAgIFxuICAgIC8vIEJhY2t1cCBhbGwgcGVyc29uYSBmaWxlcyAoaW5jbHVkaW5nIHVzZXItY3JlYXRlZCBvbmVzIG5vdCBpbiBnaXQpXG4gICAgY29uc3QgcGVyc29uYXNEaXIgPSBwYXRoLmpvaW4odGhpcy5yb290RGlyLCAncGVyc29uYXMnKTtcbiAgICBjb25zdCBiYWNrdXBQZXJzb25hc0RpciA9IHBhdGguam9pbihiYWNrdXBQYXRoLCAncGVyc29uYXMnKTtcbiAgICBcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKHBlcnNvbmFzRGlyKTtcbiAgICAgIGF3YWl0IGZzLm1rZGlyKGJhY2t1cFBlcnNvbmFzRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIFxuICAgICAgY29uc3QgcGVyc29uYUZpbGVzID0gYXdhaXQgZnMucmVhZGRpcihwZXJzb25hc0Rpcik7XG4gICAgICBmb3IgKGNvbnN0IGZpbGUgb2YgcGVyc29uYUZpbGVzKSB7XG4gICAgICAgIGlmIChmaWxlLmVuZHNXaXRoKCcubWQnKSkge1xuICAgICAgICAgIGNvbnN0IHNvdXJjZVBhdGggPSBwYXRoLmpvaW4ocGVyc29uYXNEaXIsIGZpbGUpO1xuICAgICAgICAgIGNvbnN0IGRlc3RQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBlcnNvbmFzRGlyLCBmaWxlKTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBDb3B5IHRoZSBmaWxlLCBvdmVyd3JpdGluZyBpZiBpdCBhbHJlYWR5IGV4aXN0cyBmcm9tIGdpdCBhcmNoaXZlXG4gICAgICAgICAgYXdhaXQgZnMuY29weUZpbGUoc291cmNlUGF0aCwgZGVzdFBhdGgpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIExvZyB3YXJuaW5nIGJ1dCBkb24ndCBmYWlsIHRoZSBiYWNrdXBcbiAgICAgIGxvZ2dlci53YXJuKCdDb3VsZCBub3QgYmFja3VwIGFsbCBwZXJzb25hczonLCBlcnJvcik7XG4gICAgfVxuICAgIFxuICAgIC8vIFNhdmUgYmFja3VwIG1ldGFkYXRhXG4gICAgY29uc3QgbWV0YWRhdGEgPSB7XG4gICAgICB0aW1lc3RhbXAsXG4gICAgICB2ZXJzaW9uLFxuICAgICAgY3JlYXRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKClcbiAgICB9O1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZShcbiAgICAgIHBhdGguam9pbihiYWNrdXBQYXRoLCAnYmFja3VwLW1ldGFkYXRhLmpzb24nKSxcbiAgICAgIEpTT04uc3RyaW5naWZ5KG1ldGFkYXRhLCBudWxsLCAyKVxuICAgICk7XG4gICAgXG4gICAgcmV0dXJuIHtcbiAgICAgIHBhdGg6IGJhY2t1cFBhdGgsXG4gICAgICB0aW1lc3RhbXAsXG4gICAgICB2ZXJzaW9uXG4gICAgfTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENyZWF0ZSBhIGJhY2t1cCBzcGVjaWZpY2FsbHkgZm9yIG5wbSBpbnN0YWxsYXRpb25zXG4gICAqL1xuICBhc3luYyBjcmVhdGVOcG1CYWNrdXAobnBtR2xvYmFsUGF0aDogc3RyaW5nLCB2ZXJzaW9uPzogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICB0cnkge1xuICAgICAgLy8gQ3JlYXRlIG5wbS1zcGVjaWZpYyBiYWNrdXAgZGlyZWN0b3J5XG4gICAgICBjb25zdCBucG1CYWNrdXBzRGlyID0gcGF0aC5qb2luKHBhdGguZGlybmFtZSh0aGlzLmJhY2t1cHNEaXIpLCAnLmRvbGxob3VzZScsICdiYWNrdXBzJywgJ25wbScpO1xuICAgICAgYXdhaXQgZnMubWtkaXIobnBtQmFja3Vwc0RpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSB0aW1lc3RhbXAtYmFzZWQgYmFja3VwIGRpcmVjdG9yeVxuICAgICAgY29uc3QgdGltZXN0YW1wID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2UoLzovZywgJy0nKS5yZXBsYWNlKC9cXC4vZywgJy0nKTtcbiAgICAgIGNvbnN0IGJhY2t1cE5hbWUgPSBgbnBtLWJhY2t1cC0ke3RpbWVzdGFtcH1gO1xuICAgICAgY29uc3QgYmFja3VwUGF0aCA9IHBhdGguam9pbihucG1CYWNrdXBzRGlyLCBiYWNrdXBOYW1lKTtcbiAgICAgIFxuICAgICAgbG9nZ2VyLmluZm8oYFtCYWNrdXBNYW5hZ2VyXSBDcmVhdGluZyBucG0gYmFja3VwIGF0OiAke2JhY2t1cFBhdGh9YCk7XG4gICAgICBcbiAgICAgIC8vIENyZWF0ZSBiYWNrdXAgZGlyZWN0b3J5XG4gICAgICBhd2FpdCBmcy5ta2RpcihiYWNrdXBQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIFxuICAgICAgLy8gQ29weSB0aGUgbnBtIHBhY2thZ2UgZGlyZWN0b3J5XG4gICAgICBhd2FpdCB0aGlzLmNvcHlEaXJlY3RvcnkobnBtR2xvYmFsUGF0aCwgcGF0aC5qb2luKGJhY2t1cFBhdGgsICdwYWNrYWdlJykpO1xuICAgICAgXG4gICAgICAvLyBTYXZlIGJhY2t1cCBtZXRhZGF0YVxuICAgICAgY29uc3QgbWV0YWRhdGEgPSB7XG4gICAgICAgIHRpbWVzdGFtcCxcbiAgICAgICAgdmVyc2lvbixcbiAgICAgICAgbnBtR2xvYmFsUGF0aCxcbiAgICAgICAgaW5zdGFsbGF0aW9uVHlwZTogJ25wbScsXG4gICAgICAgIGNyZWF0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpXG4gICAgICB9O1xuICAgICAgXG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUoXG4gICAgICAgIHBhdGguam9pbihiYWNrdXBQYXRoLCAnYmFja3VwLW1ldGFkYXRhLmpzb24nKSxcbiAgICAgICAgSlNPTi5zdHJpbmdpZnkobWV0YWRhdGEsIG51bGwsIDIpXG4gICAgICApO1xuICAgICAgXG4gICAgICAvLyBVcGRhdGUgbWFuaWZlc3RcbiAgICAgIGF3YWl0IHRoaXMudXBkYXRlTnBtQmFja3VwTWFuaWZlc3QobnBtQmFja3Vwc0Rpciwge1xuICAgICAgICBiYWNrdXBOYW1lLFxuICAgICAgICB0aW1lc3RhbXAsXG4gICAgICAgIHZlcnNpb24sXG4gICAgICAgIHBhdGg6IGJhY2t1cFBhdGhcbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBDbGVhbnVwIG9sZCBucG0gYmFja3VwcyAoa2VlcCBsYXN0IDMpXG4gICAgICBhd2FpdCB0aGlzLmNsZWFudXBPbGROcG1CYWNrdXBzKG5wbUJhY2t1cHNEaXIsIDMpO1xuICAgICAgXG4gICAgICByZXR1cm4gYmFja3VwUGF0aDtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbQmFja3VwTWFuYWdlcl0gRmFpbGVkIHRvIGNyZWF0ZSBucG0gYmFja3VwOicsIGVycm9yKTtcbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENvcHkgZGlyZWN0b3J5IHJlY3Vyc2l2ZWx5IHdpdGggcHJvZ3Jlc3MgcmVwb3J0aW5nXG4gICAqIEBkZXByZWNhdGVkIFVzZSBGaWxlT3BlcmF0aW9ucy5jb3B5RGlyZWN0b3J5IGluc3RlYWRcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY29weURpcmVjdG9yeShzcmM6IHN0cmluZywgZGVzdDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgRmlsZU9wZXJhdGlvbnMuY29weURpcmVjdG9yeShzcmMsIGRlc3QsIHtcbiAgICAgIGV4Y2x1ZGVQYXR0ZXJuczogWycuZ2l0J10sXG4gICAgICBvblByb2dyZXNzOiAoY29waWVkLCB0b3RhbCwgZmlsZSkgPT4ge1xuICAgICAgICBpZiAoY29waWVkICUgNTAgPT09IDApIHtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoYFtCYWNrdXBNYW5hZ2VyXSBDb3B5aW5nOiAke2NvcGllZH0vJHt0b3RhbH0gZmlsZXNgKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG4gIFxuICAvKipcbiAgICogVXBkYXRlIG5wbSBiYWNrdXAgbWFuaWZlc3RcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdXBkYXRlTnBtQmFja3VwTWFuaWZlc3QobnBtQmFja3Vwc0Rpcjogc3RyaW5nLCBiYWNrdXBJbmZvOiB7XG4gICAgYmFja3VwTmFtZTogc3RyaW5nO1xuICAgIHRpbWVzdGFtcDogc3RyaW5nO1xuICAgIHZlcnNpb24/OiBzdHJpbmc7XG4gICAgcGF0aDogc3RyaW5nO1xuICB9KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgbWFuaWZlc3RQYXRoID0gcGF0aC5qb2luKG5wbUJhY2t1cHNEaXIsICdtYW5pZmVzdC5qc29uJyk7XG4gICAgXG4gICAgbGV0IG1hbmlmZXN0OiB7IGJhY2t1cHM6IEFycmF5PHtcbiAgICAgIGJhY2t1cE5hbWU6IHN0cmluZztcbiAgICAgIHRpbWVzdGFtcDogc3RyaW5nO1xuICAgICAgdmVyc2lvbj86IHN0cmluZztcbiAgICAgIHBhdGg6IHN0cmluZztcbiAgICB9PiB9ID0geyBiYWNrdXBzOiBbXSB9O1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUobWFuaWZlc3RQYXRoLCAndXRmLTgnKTtcbiAgICAgIG1hbmlmZXN0ID0gSlNPTi5wYXJzZShjb250ZW50KTtcbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIEZpbGUgZG9lc24ndCBleGlzdCBvciBpcyBpbnZhbGlkLCB1c2UgZGVmYXVsdFxuICAgIH1cbiAgICBcbiAgICAvLyBBZGQgbmV3IGJhY2t1cCB0byBiZWdpbm5pbmcgb2YgbGlzdFxuICAgIG1hbmlmZXN0LmJhY2t1cHMudW5zaGlmdChiYWNrdXBJbmZvKTtcbiAgICBcbiAgICAvLyBLZWVwIG9ubHkgbGFzdCAxMCBlbnRyaWVzIGluIG1hbmlmZXN0XG4gICAgbWFuaWZlc3QuYmFja3VwcyA9IG1hbmlmZXN0LmJhY2t1cHMuc2xpY2UoMCwgMTApO1xuICAgIFxuICAgIGF3YWl0IGZzLndyaXRlRmlsZShtYW5pZmVzdFBhdGgsIEpTT04uc3RyaW5naWZ5KG1hbmlmZXN0LCBudWxsLCAyKSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDbGVhbiB1cCBvbGQgbnBtIGJhY2t1cHNcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY2xlYW51cE9sZE5wbUJhY2t1cHMobnBtQmFja3Vwc0Rpcjogc3RyaW5nLCBrZWVwQ291bnQ6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcihucG1CYWNrdXBzRGlyLCB7IHdpdGhGaWxlVHlwZXM6IHRydWUgfSk7XG4gICAgICBjb25zdCBiYWNrdXBEaXJzID0gZW50cmllc1xuICAgICAgICAuZmlsdGVyKGUgPT4gZS5pc0RpcmVjdG9yeSgpICYmIGUubmFtZS5zdGFydHNXaXRoKCducG0tYmFja3VwLScpKVxuICAgICAgICAubWFwKGUgPT4gZS5uYW1lKVxuICAgICAgICAuc29ydCgpXG4gICAgICAgIC5yZXZlcnNlKCk7XG4gICAgICBcbiAgICAgIC8vIFJlbW92ZSBvbGQgYmFja3Vwc1xuICAgICAgY29uc3QgdG9SZW1vdmUgPSBiYWNrdXBEaXJzLnNsaWNlKGtlZXBDb3VudCk7XG4gICAgICBmb3IgKGNvbnN0IGRpciBvZiB0b1JlbW92ZSkge1xuICAgICAgICBjb25zdCBkaXJQYXRoID0gcGF0aC5qb2luKG5wbUJhY2t1cHNEaXIsIGRpcik7XG4gICAgICAgIGxvZ2dlci5pbmZvKGBbQmFja3VwTWFuYWdlcl0gUmVtb3Zpbmcgb2xkIG5wbSBiYWNrdXA6ICR7ZGlyfWApO1xuICAgICAgICBhd2FpdCBmcy5ybShkaXJQYXRoLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgZm9yY2U6IHRydWUgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci53YXJuKCdbQmFja3VwTWFuYWdlcl0gRmFpbGVkIHRvIGNsZWFudXAgb2xkIG5wbSBiYWNrdXBzOicsIGVycm9yKTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMaXN0IGF2YWlsYWJsZSBiYWNrdXBzXG4gICAqL1xuICBhc3luYyBsaXN0QmFja3VwcygpOiBQcm9taXNlPEJhY2t1cEluZm9bXT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLmJhY2t1cHNEaXIsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KTtcbiAgICAgIGNvbnN0IGJhY2t1cHM6IEJhY2t1cEluZm9bXSA9IFtdO1xuICAgICAgXG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgaWYgKGVudHJ5LmlzRGlyZWN0b3J5KCkgJiYgZW50cnkubmFtZS5zdGFydHNXaXRoKCdiYWNrdXAtJykpIHtcbiAgICAgICAgICBjb25zdCBiYWNrdXBQYXRoID0gcGF0aC5qb2luKHRoaXMuYmFja3Vwc0RpciwgZW50cnkubmFtZSk7XG4gICAgICAgICAgY29uc3QgdGltZXN0YW1wID0gZW50cnkubmFtZS5yZXBsYWNlKCdiYWNrdXAtJywgJycpO1xuICAgICAgICAgIFxuICAgICAgICAgIC8vIFRyeSB0byByZWFkIG1ldGFkYXRhXG4gICAgICAgICAgbGV0IHZlcnNpb246IHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgbWV0YWRhdGFQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsICdiYWNrdXAtbWV0YWRhdGEuanNvbicpO1xuICAgICAgICAgICAgY29uc3QgbWV0YWRhdGEgPSBKU09OLnBhcnNlKGF3YWl0IGZzLnJlYWRGaWxlKG1ldGFkYXRhUGF0aCwgJ3V0Zi04JykpO1xuICAgICAgICAgICAgdmVyc2lvbiA9IG1ldGFkYXRhLnZlcnNpb247XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAvLyBObyBtZXRhZGF0YSBmaWxlLCB0aGF0J3Mgb2theVxuICAgICAgICAgIH1cbiAgICAgICAgICBcbiAgICAgICAgICBiYWNrdXBzLnB1c2goe1xuICAgICAgICAgICAgcGF0aDogYmFja3VwUGF0aCxcbiAgICAgICAgICAgIHRpbWVzdGFtcCxcbiAgICAgICAgICAgIHZlcnNpb25cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBTb3J0IGJ5IHRpbWVzdGFtcCBkZXNjZW5kaW5nIChuZXdlc3QgZmlyc3QpXG4gICAgICByZXR1cm4gYmFja3Vwcy5zb3J0KChhLCBiKSA9PiBiLnRpbWVzdGFtcC5sb2NhbGVDb21wYXJlKGEudGltZXN0YW1wKSk7XG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogR2V0IHRoZSBtb3N0IHJlY2VudCBiYWNrdXBcbiAgICovXG4gIGFzeW5jIGdldExhdGVzdEJhY2t1cCgpOiBQcm9taXNlPEJhY2t1cEluZm8gfCBudWxsPiB7XG4gICAgY29uc3QgYmFja3VwcyA9IGF3YWl0IHRoaXMubGlzdEJhY2t1cHMoKTtcbiAgICByZXR1cm4gYmFja3Vwcy5sZW5ndGggPiAwID8gYmFja3Vwc1swXSA6IG51bGw7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXN0b3JlIGZyb20gYSBiYWNrdXBcbiAgICovXG4gIGFzeW5jIHJlc3RvcmVCYWNrdXAoYmFja3VwUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gVmVyaWZ5IGJhY2t1cCBleGlzdHNcbiAgICB0cnkge1xuICAgICAgYXdhaXQgZnMuYWNjZXNzKGJhY2t1cFBhdGgpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBCYWNrdXAgbm90IGZvdW5kOiAke2JhY2t1cFBhdGh9YCk7XG4gICAgfVxuICAgIFxuICAgIC8vIENyZWF0ZSBhIHRlbXBvcmFyeSBkaXJlY3RvcnkgZm9yIHRoZSBjdXJyZW50IHN0YXRlXG4gICAgY29uc3QgdGVtcERpciA9IHBhdGguam9pbih0aGlzLmJhY2t1cHNEaXIsICd0ZW1wLWN1cnJlbnQnKTtcbiAgICBhd2FpdCBmcy5ta2Rpcih0ZW1wRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICBcbiAgICAvLyBNb3ZlIGN1cnJlbnQgZmlsZXMgdG8gdGVtcCAoZXhjZXB0IC5naXQgYW5kIG5vZGVfbW9kdWxlcylcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgZnMucmVhZGRpcih0aGlzLnJvb3REaXIpO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgaWYgKGVudHJ5ICE9PSAnLmdpdCcgJiYgZW50cnkgIT09ICdub2RlX21vZHVsZXMnICYmIGVudHJ5ICE9PSAnZGlzdCcpIHtcbiAgICAgICAgY29uc3Qgc291cmNlUGF0aCA9IHBhdGguam9pbih0aGlzLnJvb3REaXIsIGVudHJ5KTtcbiAgICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4odGVtcERpciwgZW50cnkpO1xuICAgICAgICBhd2FpdCBmcy5yZW5hbWUoc291cmNlUGF0aCwgZGVzdFBhdGgpO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBDb3B5IGJhY2t1cCBmaWxlcyB0byByb290XG4gICAgY29uc3QgYmFja3VwRW50cmllcyA9IGF3YWl0IGZzLnJlYWRkaXIoYmFja3VwUGF0aCk7XG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBiYWNrdXBFbnRyaWVzKSB7XG4gICAgICBpZiAoZW50cnkgIT09ICdiYWNrdXAtbWV0YWRhdGEuanNvbicgJiYgZW50cnkgIT09ICcuZ2l0Jykge1xuICAgICAgICBjb25zdCBzb3VyY2VQYXRoID0gcGF0aC5qb2luKGJhY2t1cFBhdGgsIGVudHJ5KTtcbiAgICAgICAgY29uc3QgZGVzdFBhdGggPSBwYXRoLmpvaW4odGhpcy5yb290RGlyLCBlbnRyeSk7XG4gICAgICAgIGF3YWl0IHNhZmVFeGVjKCdjcCcsIFsnLXInLCBzb3VyY2VQYXRoLCBkZXN0UGF0aF0pO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBDbGVhbiB1cCB0ZW1wIGRpcmVjdG9yeVxuICAgIGF3YWl0IGZzLnJtKHRlbXBEaXIsIHsgcmVjdXJzaXZlOiB0cnVlLCBmb3JjZTogdHJ1ZSB9KTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENsZWFuIHVwIG9sZCBiYWNrdXBzIChrZWVwIHRoZSA1IG1vc3QgcmVjZW50KVxuICAgKi9cbiAgYXN5bmMgY2xlYW51cE9sZEJhY2t1cHMoa2VlcENvdW50OiBudW1iZXIgPSA1KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICBjb25zdCBiYWNrdXBzID0gYXdhaXQgdGhpcy5saXN0QmFja3VwcygpO1xuICAgIGxldCBkZWxldGVkQ291bnQgPSAwO1xuICAgIFxuICAgIGlmIChiYWNrdXBzLmxlbmd0aCA+IGtlZXBDb3VudCkge1xuICAgICAgY29uc3QgYmFja3Vwc1RvRGVsZXRlID0gYmFja3Vwcy5zbGljZShrZWVwQ291bnQpO1xuICAgICAgXG4gICAgICBmb3IgKGNvbnN0IGJhY2t1cCBvZiBiYWNrdXBzVG9EZWxldGUpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBmcy5ybShiYWNrdXAucGF0aCwgeyByZWN1cnNpdmU6IHRydWUsIGZvcmNlOiB0cnVlIH0pO1xuICAgICAgICAgIGRlbGV0ZWRDb3VudCsrO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ2dlci5lcnJvcihgRmFpbGVkIHRvIGRlbGV0ZSBiYWNrdXAgJHtiYWNrdXAucGF0aH06YCwgZXJyb3IpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBkZWxldGVkQ291bnQ7XG4gIH1cbn0iXX0=