@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.
312 lines • 47.8 kB
JavaScript
/**
* Migration Manager - Handles migration from legacy structure to portfolio structure
*/
import * as path from 'path';
import { ElementType } from './types.js';
import { logger } from '../utils/logger.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
import { ContentValidator } from '../security/contentValidator.js';
import { SecurityMonitor } from '../security/securityMonitor.js';
import { SECURITY_LIMITS } from '../security/constants.js';
export class MigrationManager {
portfolioManager;
fileLockManager;
fileOperations;
constructor(portfolioManager, fileLockManager, fileOperationsService) {
this.portfolioManager = portfolioManager;
this.fileLockManager = fileLockManager;
this.fileOperations = fileOperationsService;
}
/**
* Check if migration is needed
*/
async needsMigration() {
const hasLegacy = await this.portfolioManager.hasLegacyPersonas();
const portfolioExists = await this.portfolioManager.exists();
// Need migration if we have legacy personas but no portfolio yet
return hasLegacy && !portfolioExists;
}
/**
* Perform migration from legacy to portfolio structure
*/
async migrate(options) {
const result = {
success: true,
migratedCount: 0,
errors: [],
backedUp: false
};
try {
// Check if migration is needed
if (!await this.needsMigration()) {
logger.info('[MigrationManager] No migration needed');
return result;
}
logger.info('[MigrationManager] Starting migration from legacy personas to portfolio structure');
// SECURITY FIX: DMCP-SEC-006 - Add security audit logging
SecurityMonitor.logSecurityEvent({
type: 'PORTFOLIO_INITIALIZATION',
severity: 'LOW',
source: 'migration_manager',
details: 'Starting migration from legacy personas to portfolio structure',
metadata: { backup: !!options?.backup }
});
// Create backup if requested
if (options?.backup) {
const backupPath = await this.createBackup();
result.backedUp = true;
result.backupPath = backupPath;
logger.info(`[MigrationManager] Created backup at: ${backupPath}`);
// SECURITY FIX: DMCP-SEC-006 - Log backup creation for audit trail
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'migration_manager',
details: `Created backup during migration: ${backupPath}`,
metadata: { backupPath, operation: 'migration_backup' }
});
}
// Initialize portfolio structure
await this.portfolioManager.initialize();
// Get legacy personas
const legacyDir = this.portfolioManager.getLegacyPersonasDir();
const files = await this.fileOperations.listDirectory(legacyDir);
const personaFiles = files.filter(file => file.endsWith('.md'));
logger.info(`[MigrationManager] Found ${personaFiles.length} personas to migrate`);
// Migrate each persona
for (const file of personaFiles) {
try {
await this.migratePersona(file);
result.migratedCount++;
// SECURITY FIX: DMCP-SEC-006 - Log each successful migration for audit trail
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'migration_manager',
details: `Successfully migrated persona: ${file}`,
metadata: { filename: file, operation: 'persona_migration' }
});
}
catch (error) {
const errorMsg = `Failed to migrate ${file}: ${error instanceof Error ? error.message : String(error)}`;
logger.error(`[MigrationManager] ${errorMsg}`);
result.errors.push(errorMsg);
result.success = false;
// SECURITY FIX: DMCP-SEC-006 - Log individual migration failures for audit trail
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'MEDIUM',
source: 'migration_manager',
details: `Failed to migrate persona: ${errorMsg}`,
metadata: {
filename: file,
operation: 'persona_migration_failed',
errorType: error instanceof Error ? error.name : 'unknown'
}
});
}
}
// If all migrations successful, optionally clean up legacy directory
if (result.success && result.migratedCount > 0) {
logger.info(`[MigrationManager] Successfully migrated ${result.migratedCount} personas`);
// SECURITY FIX: DMCP-SEC-006 - Log successful migration completion for audit trail
SecurityMonitor.logSecurityEvent({
type: 'PORTFOLIO_POPULATED',
severity: 'LOW',
source: 'migration_manager',
details: `Migration completed successfully: ${result.migratedCount} personas migrated`,
metadata: {
migratedCount: result.migratedCount,
backedUp: result.backedUp,
backupPath: result.backupPath
}
});
// Note: We don't automatically delete the legacy directory
// User should manually remove it after confirming migration success
}
}
catch (error) {
result.success = false;
const errorMsg = `Migration failed: ${error instanceof Error ? error.message : String(error)}`;
result.errors.push(errorMsg);
// SECURITY FIX: DMCP-SEC-006 - Log migration failures for security audit trail
SecurityMonitor.logSecurityEvent({
type: 'DIRECTORY_MIGRATION',
severity: 'HIGH',
source: 'migration_manager',
details: `Migration failed: ${errorMsg}`,
metadata: {
errorType: error instanceof Error ? error.name : 'unknown',
migratedCount: result.migratedCount,
errorCount: result.errors.length
}
});
// Log with full error details including stack trace
if (error instanceof Error) {
logger.error(`[MigrationManager] ${errorMsg}`, {
stack: error.stack,
name: error.name,
cause: error.cause
});
}
else {
logger.error(`[MigrationManager] ${errorMsg}`, { rawError: error });
}
}
return result;
}
/**
* Migrate a single persona file
*/
async migratePersona(filename) {
// Normalize filename to prevent Unicode attacks
const filenameValidation = UnicodeValidator.normalize(filename);
const normalizedFilename = filenameValidation.normalizedContent;
if (normalizedFilename !== filename) {
logger.warn(`[MigrationManager] Filename normalized from "${filename}" to "${normalizedFilename}"`);
}
if (!filenameValidation.isValid) {
logger.warn(`[MigrationManager] Filename has Unicode issues: ${filenameValidation.detectedIssues?.join(', ')}`);
// SECURITY FIX: DMCP-SEC-006 - Log Unicode issues for security audit trail
SecurityMonitor.logSecurityEvent({
type: 'UNICODE_VALIDATION_ERROR',
severity: 'MEDIUM',
source: 'migration_manager',
details: `Unicode issues detected in filename during migration: ${filenameValidation.detectedIssues?.join(', ')}`,
metadata: {
originalFilename: filename,
normalizedFilename,
detectedIssues: filenameValidation.detectedIssues
}
});
}
const legacyPath = path.join(this.portfolioManager.getLegacyPersonasDir(), filename);
const newPath = this.portfolioManager.getElementPath(ElementType.PERSONA, normalizedFilename);
// Read the content
const content = await this.fileOperations.readFile(legacyPath, {
source: 'MigrationManager.migratePersona'
});
// Validate content size before processing
const contentSize = Buffer.byteLength(content, 'utf8');
if (contentSize > SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES) {
const maxSizeKB = Math.round(SECURITY_LIMITS.MAX_PERSONA_SIZE_BYTES / 1024);
const actualSizeKB = Math.round(contentSize / 1024);
throw new Error(`Content size (${actualSizeKB}KB) exceeds maximum allowed size (${maxSizeKB}KB) for persona files`);
}
// Normalize content to prevent Unicode issues
const contentValidation = UnicodeValidator.normalize(content);
const normalizedContent = contentValidation.normalizedContent;
if (!contentValidation.isValid) {
logger.warn(`[MigrationManager] Content has Unicode issues in ${filename}: ${contentValidation.detectedIssues?.join(', ')}`);
// SECURITY FIX: DMCP-SEC-006 - Log Unicode content issues for security audit trail
SecurityMonitor.logSecurityEvent({
type: 'UNICODE_VALIDATION_ERROR',
severity: 'MEDIUM',
source: 'migration_manager',
details: `Unicode issues detected in content during migration: ${contentValidation.detectedIssues?.join(', ')}`,
metadata: {
filename,
detectedIssues: contentValidation.detectedIssues,
contentLength: content.length
}
});
}
// SECURITY FIX: Add comprehensive content validation before write
// FIXED: CVE-2025-XXXX - Direct file write without security validation in migration
// Original issue: Line 147 used direct fs.writeFile without comprehensive validation
// Security impact: Could allow malicious content to be written during migration
// Fix: Added ContentValidator.validateAndSanitize with critical threat blocking
const validationResult = ContentValidator.validateAndSanitize(normalizedContent);
if (!validationResult.isValid && validationResult.severity === 'critical') {
const patterns = validationResult.detectedPatterns?.join(', ') || 'unknown patterns';
throw new Error(`Critical security threat in migrated content for ${filename}: ${patterns}`);
}
const validatedContent = validationResult.sanitizedContent || normalizedContent;
// SECURITY FIX: Replace direct write with atomic operation
// FIXED: Race condition vulnerability in file writes during migration
// Original issue: Line 147 used non-atomic fs.writeFile operation
// Security impact: Race conditions could cause data corruption or partial writes
// Fix: Replaced with FileOperationsService.writeFile for guaranteed atomicity
await this.fileOperations.writeFile(newPath, validatedContent, {
source: 'MigrationManager.migratePersona'
});
// SECURITY FIX: DMCP-SEC-006 - Log file operations for security audit trail
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'migration_manager',
details: `Persona file migrated with security validation: ${normalizedFilename}`,
metadata: {
originalFilename: filename,
normalizedFilename,
sourcePath: legacyPath,
destinationPath: newPath,
contentLength: validatedContent.length,
unicodeNormalized: normalizedFilename !== filename,
unicodeIssues: !contentValidation.isValid
}
});
logger.debug(`[MigrationManager] Migrated: ${filename}`);
}
/**
* Create backup of legacy personas
*/
async createBackup() {
const legacyDir = this.portfolioManager.getLegacyPersonasDir();
const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-');
const backupDir = `${legacyDir}_backup_${timestamp}`;
// Create backup directory
await this.fileOperations.createDirectory(backupDir);
// Copy all files
const files = await this.fileOperations.listDirectory(legacyDir);
let copiedCount = 0;
for (const file of files) {
const srcPath = path.join(legacyDir, file);
const destPath = path.join(backupDir, file);
const stats = await this.fileOperations.stat(srcPath);
if (stats.isFile()) {
await this.fileOperations.copyFile(srcPath, destPath, {
source: 'MigrationManager.createBackup'
});
copiedCount++;
}
}
// SECURITY FIX: DMCP-SEC-006 - Log backup operation details for audit trail
SecurityMonitor.logSecurityEvent({
type: 'FILE_COPIED',
severity: 'LOW',
source: 'migration_manager',
details: `Backup created: ${copiedCount} files copied to ${backupDir}`,
metadata: {
backupDir,
legacyDir,
filesCopied: copiedCount,
operation: 'backup_creation'
}
});
return backupDir;
}
/**
* Get migration status report
*/
async getMigrationStatus() {
const hasLegacyPersonas = await this.portfolioManager.hasLegacyPersonas();
let legacyPersonaCount = 0;
if (hasLegacyPersonas) {
const legacyDir = this.portfolioManager.getLegacyPersonasDir();
const files = await this.fileOperations.listDirectory(legacyDir);
legacyPersonaCount = files.filter(file => file.endsWith('.md')).length;
}
const portfolioExists = await this.portfolioManager.exists();
const portfolioStats = portfolioExists
? await this.portfolioManager.getStatistics()
: Object.values(ElementType).reduce((acc, type) => ({ ...acc, [type]: 0 }), {});
return {
hasLegacyPersonas,
legacyPersonaCount,
portfolioExists,
portfolioStats
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWlncmF0aW9uTWFuYWdlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wb3J0Zm9saW8vTWlncmF0aW9uTWFuYWdlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUVILE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDekMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRW5FLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVqRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFVM0QsTUFBTSxPQUFPLGdCQUFnQjtJQUNuQixnQkFBZ0IsQ0FBbUI7SUFDbkMsZUFBZSxDQUFrQjtJQUNqQyxjQUFjLENBQXdCO0lBRTlDLFlBQ0UsZ0JBQWtDLEVBQ2xDLGVBQWdDLEVBQ2hDLHFCQUE0QztRQUU1QyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsZ0JBQWdCLENBQUM7UUFDekMsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7UUFDdkMsSUFBSSxDQUFDLGNBQWMsR0FBRyxxQkFBcUIsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsY0FBYztRQUN6QixNQUFNLFNBQVMsR0FBRyxNQUFNLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQ2xFLE1BQU0sZUFBZSxHQUFHLE1BQU0sSUFBSSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sRUFBRSxDQUFDO1FBRTdELGlFQUFpRTtRQUNqRSxPQUFPLFNBQVMsSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUN2QyxD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LG9CQUFvQixFQUFFLENBQUM7WUFDL0QsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUNqRSxrQkFBa0IsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQztRQUN6RSxDQUFDO1FBRUQsTUFBTSxlQUFlLEdBQUcsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDN0QsTUFBTSxjQUFjLEdBQUcsZUFBZTtZQUNwQyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsYUFBYSxFQUFFO1lBQzdDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFnQyxDQUFDO1FBRWpILE9BQU87WUFDTCxpQkFBaUI7WUFDakIsa0JBQWtCO1lBQ2xCLGVBQWU7WUFDZixjQUFjO1NBQ2YsQ0FBQztJQUNKLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTWlncmF0aW9uIE1hbmFnZXIgLSBIYW5kbGVzIG1pZ3JhdGlvbiBmcm9tIGxlZ2FjeSBzdHJ1Y3R1cmUgdG8gcG9ydGZvbGlvIHN0cnVjdHVyZVxuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBQb3J0Zm9saW9NYW5hZ2VyIH0gZnJvbSAnLi9Qb3J0Zm9saW9NYW5hZ2VyLmpzJztcbmltcG9ydCB7IEVsZW1lbnRUeXBlIH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBDb250ZW50VmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvY29udGVudFZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBGaWxlTG9ja01hbmFnZXIgfSBmcm9tICcuLi9zZWN1cml0eS9maWxlTG9ja01hbmFnZXIuanMnO1xuaW1wb3J0IHsgU2VjdXJpdHlNb25pdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvc2VjdXJpdHlNb25pdG9yLmpzJztcbmltcG9ydCB7IEZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5pbXBvcnQgeyBTRUNVUklUWV9MSU1JVFMgfSBmcm9tICcuLi9zZWN1cml0eS9jb25zdGFudHMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIE1pZ3JhdGlvblJlc3VsdCB7XG4gIHN1Y2Nlc3M6IGJvb2xlYW47XG4gIG1pZ3JhdGVkQ291bnQ6IG51bWJlcjtcbiAgZXJyb3JzOiBzdHJpbmdbXTtcbiAgYmFja2VkVXA6IGJvb2xlYW47XG4gIGJhY2t1cFBhdGg/OiBzdHJpbmc7XG59XG5cbmV4cG9ydCBjbGFzcyBNaWdyYXRpb25NYW5hZ2VyIHtcbiAgcHJpdmF0ZSBwb3J0Zm9saW9NYW5hZ2VyOiBQb3J0Zm9saW9NYW5hZ2VyO1xuICBwcml2YXRlIGZpbGVMb2NrTWFuYWdlcjogRmlsZUxvY2tNYW5hZ2VyO1xuICBwcml2YXRlIGZpbGVPcGVyYXRpb25zOiBGaWxlT3BlcmF0aW9uc1NlcnZpY2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcG9ydGZvbGlvTWFuYWdlcjogUG9ydGZvbGlvTWFuYWdlcixcbiAgICBmaWxlTG9ja01hbmFnZXI6IEZpbGVMb2NrTWFuYWdlcixcbiAgICBmaWxlT3BlcmF0aW9uc1NlcnZpY2U6IEZpbGVPcGVyYXRpb25zU2VydmljZVxuICApIHtcbiAgICB0aGlzLnBvcnRmb2xpb01hbmFnZXIgPSBwb3J0Zm9saW9NYW5hZ2VyO1xuICAgIHRoaXMuZmlsZUxvY2tNYW5hZ2VyID0gZmlsZUxvY2tNYW5hZ2VyO1xuICAgIHRoaXMuZmlsZU9wZXJhdGlvbnMgPSBmaWxlT3BlcmF0aW9uc1NlcnZpY2U7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBDaGVjayBpZiBtaWdyYXRpb24gaXMgbmVlZGVkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgbmVlZHNNaWdyYXRpb24oKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgY29uc3QgaGFzTGVnYWN5ID0gYXdhaXQgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmhhc0xlZ2FjeVBlcnNvbmFzKCk7XG4gICAgY29uc3QgcG9ydGZvbGlvRXhpc3RzID0gYXdhaXQgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmV4aXN0cygpO1xuICAgIFxuICAgIC8vIE5lZWQgbWlncmF0aW9uIGlmIHdlIGhhdmUgbGVnYWN5IHBlcnNvbmFzIGJ1dCBubyBwb3J0Zm9saW8geWV0XG4gICAgcmV0dXJuIGhhc0xlZ2FjeSAmJiAhcG9ydGZvbGlvRXhpc3RzO1xuICB9XG4gIFxuICAvKipcbiAgICogUGVyZm9ybSBtaWdyYXRpb24gZnJvbSBsZWdhY3kgdG8gcG9ydGZvbGlvIHN0cnVjdHVyZVxuICAgKi9cbiAgcHVibGljIGFzeW5jIG1pZ3JhdGUob3B0aW9ucz86IHsgYmFja3VwPzogYm9vbGVhbiB9KTogUHJvbWlzZTxNaWdyYXRpb25SZXN1bHQ+IHtcbiAgICBjb25zdCByZXN1bHQ6IE1pZ3JhdGlvblJlc3VsdCA9IHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBtaWdyYXRlZENvdW50OiAwLFxuICAgICAgZXJyb3JzOiBbXSxcbiAgICAgIGJhY2tlZFVwOiBmYWxzZVxuICAgIH07XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIC8vIENoZWNrIGlmIG1pZ3JhdGlvbiBpcyBuZWVkZWRcbiAgICAgIGlmICghYXdhaXQgdGhpcy5uZWVkc01pZ3JhdGlvbigpKSB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdbTWlncmF0aW9uTWFuYWdlcl0gTm8gbWlncmF0aW9uIG5lZWRlZCcpO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgfVxuICAgICAgXG4gICAgICBsb2dnZXIuaW5mbygnW01pZ3JhdGlvbk1hbmFnZXJdIFN0YXJ0aW5nIG1pZ3JhdGlvbiBmcm9tIGxlZ2FjeSBwZXJzb25hcyB0byBwb3J0Zm9saW8gc3RydWN0dXJlJyk7XG4gICAgICBcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWDogRE1DUC1TRUMtMDA2IC0gQWRkIHNlY3VyaXR5IGF1ZGl0IGxvZ2dpbmdcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1BPUlRGT0xJT19JTklUSUFMSVpBVElPTicsXG4gICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgc291cmNlOiAnbWlncmF0aW9uX21hbmFnZXInLFxuICAgICAgICBkZXRhaWxzOiAnU3RhcnRpbmcgbWlncmF0aW9uIGZyb20gbGVnYWN5IHBlcnNvbmFzIHRvIHBvcnRmb2xpbyBzdHJ1Y3R1cmUnLFxuICAgICAgICBtZXRhZGF0YTogeyBiYWNrdXA6ICEhb3B0aW9ucz8uYmFja3VwIH1cbiAgICAgIH0pO1xuICAgICAgXG4gICAgICAvLyBDcmVhdGUgYmFja3VwIGlmIHJlcXVlc3RlZFxuICAgICAgaWYgKG9wdGlvbnM/LmJhY2t1cCkge1xuICAgICAgICBjb25zdCBiYWNrdXBQYXRoID0gYXdhaXQgdGhpcy5jcmVhdGVCYWNrdXAoKTtcbiAgICAgICAgcmVzdWx0LmJhY2tlZFVwID0gdHJ1ZTtcbiAgICAgICAgcmVzdWx0LmJhY2t1cFBhdGggPSBiYWNrdXBQYXRoO1xuICAgICAgICBsb2dnZXIuaW5mbyhgW01pZ3JhdGlvbk1hbmFnZXJdIENyZWF0ZWQgYmFja3VwIGF0OiAke2JhY2t1cFBhdGh9YCk7XG4gICAgICAgIFxuICAgICAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBiYWNrdXAgY3JlYXRpb24gZm9yIGF1ZGl0IHRyYWlsXG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnRklMRV9DT1BJRUQnLFxuICAgICAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgICAgICBzb3VyY2U6ICdtaWdyYXRpb25fbWFuYWdlcicsXG4gICAgICAgICAgZGV0YWlsczogYENyZWF0ZWQgYmFja3VwIGR1cmluZyBtaWdyYXRpb246ICR7YmFja3VwUGF0aH1gLFxuICAgICAgICAgIG1ldGFkYXRhOiB7IGJhY2t1cFBhdGgsIG9wZXJhdGlvbjogJ21pZ3JhdGlvbl9iYWNrdXAnIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICBcbiAgICAgIC8vIEluaXRpYWxpemUgcG9ydGZvbGlvIHN0cnVjdHVyZVxuICAgICAgYXdhaXQgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmluaXRpYWxpemUoKTtcbiAgICAgIFxuICAgICAgLy8gR2V0IGxlZ2FjeSBwZXJzb25hc1xuICAgICAgY29uc3QgbGVnYWN5RGlyID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldExlZ2FjeVBlcnNvbmFzRGlyKCk7XG4gICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMubGlzdERpcmVjdG9yeShsZWdhY3lEaXIpO1xuICAgICAgY29uc3QgcGVyc29uYUZpbGVzID0gZmlsZXMuZmlsdGVyKGZpbGUgPT4gZmlsZS5lbmRzV2l0aCgnLm1kJykpO1xuICAgICAgXG4gICAgICBsb2dnZXIuaW5mbyhgW01pZ3JhdGlvbk1hbmFnZXJdIEZvdW5kICR7cGVyc29uYUZpbGVzLmxlbmd0aH0gcGVyc29uYXMgdG8gbWlncmF0ZWApO1xuICAgICAgXG4gICAgICAvLyBNaWdyYXRlIGVhY2ggcGVyc29uYVxuICAgICAgZm9yIChjb25zdCBmaWxlIG9mIHBlcnNvbmFGaWxlcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHRoaXMubWlncmF0ZVBlcnNvbmEoZmlsZSk7XG4gICAgICAgICAgcmVzdWx0Lm1pZ3JhdGVkQ291bnQrKztcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBlYWNoIHN1Y2Nlc3NmdWwgbWlncmF0aW9uIGZvciBhdWRpdCB0cmFpbFxuICAgICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICAgIHR5cGU6ICdGSUxFX0NPUElFRCcsXG4gICAgICAgICAgICBzZXZlcml0eTogJ0xPVycsXG4gICAgICAgICAgICBzb3VyY2U6ICdtaWdyYXRpb25fbWFuYWdlcicsXG4gICAgICAgICAgICBkZXRhaWxzOiBgU3VjY2Vzc2Z1bGx5IG1pZ3JhdGVkIHBlcnNvbmE6ICR7ZmlsZX1gLFxuICAgICAgICAgICAgbWV0YWRhdGE6IHsgZmlsZW5hbWU6IGZpbGUsIG9wZXJhdGlvbjogJ3BlcnNvbmFfbWlncmF0aW9uJyB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgY29uc3QgZXJyb3JNc2cgPSBgRmFpbGVkIHRvIG1pZ3JhdGUgJHtmaWxlfTogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YDtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoYFtNaWdyYXRpb25NYW5hZ2VyXSAke2Vycm9yTXNnfWApO1xuICAgICAgICAgIHJlc3VsdC5lcnJvcnMucHVzaChlcnJvck1zZyk7XG4gICAgICAgICAgcmVzdWx0LnN1Y2Nlc3MgPSBmYWxzZTtcbiAgICAgICAgICBcbiAgICAgICAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBpbmRpdmlkdWFsIG1pZ3JhdGlvbiBmYWlsdXJlcyBmb3IgYXVkaXQgdHJhaWxcbiAgICAgICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgICAgICB0eXBlOiAnRklMRV9DT1BJRUQnLFxuICAgICAgICAgICAgc2V2ZXJpdHk6ICdNRURJVU0nLFxuICAgICAgICAgICAgc291cmNlOiAnbWlncmF0aW9uX21hbmFnZXInLFxuICAgICAgICAgICAgZGV0YWlsczogYEZhaWxlZCB0byBtaWdyYXRlIHBlcnNvbmE6ICR7ZXJyb3JNc2d9YCxcbiAgICAgICAgICAgIG1ldGFkYXRhOiB7IFxuICAgICAgICAgICAgICBmaWxlbmFtZTogZmlsZSwgXG4gICAgICAgICAgICAgIG9wZXJhdGlvbjogJ3BlcnNvbmFfbWlncmF0aW9uX2ZhaWxlZCcsXG4gICAgICAgICAgICAgIGVycm9yVHlwZTogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm5hbWUgOiAndW5rbm93bidcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgXG4gICAgICAvLyBJZiBhbGwgbWlncmF0aW9ucyBzdWNjZXNzZnVsLCBvcHRpb25hbGx5IGNsZWFuIHVwIGxlZ2FjeSBkaXJlY3RvcnlcbiAgICAgIGlmIChyZXN1bHQuc3VjY2VzcyAmJiByZXN1bHQubWlncmF0ZWRDb3VudCA+IDApIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oYFtNaWdyYXRpb25NYW5hZ2VyXSBTdWNjZXNzZnVsbHkgbWlncmF0ZWQgJHtyZXN1bHQubWlncmF0ZWRDb3VudH0gcGVyc29uYXNgKTtcbiAgICAgICAgXG4gICAgICAgIC8vIFNFQ1VSSVRZIEZJWDogRE1DUC1TRUMtMDA2IC0gTG9nIHN1Y2Nlc3NmdWwgbWlncmF0aW9uIGNvbXBsZXRpb24gZm9yIGF1ZGl0IHRyYWlsXG4gICAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgICB0eXBlOiAnUE9SVEZPTElPX1BPUFVMQVRFRCcsXG4gICAgICAgICAgc2V2ZXJpdHk6ICdMT1cnLFxuICAgICAgICAgIHNvdXJjZTogJ21pZ3JhdGlvbl9tYW5hZ2VyJyxcbiAgICAgICAgICBkZXRhaWxzOiBgTWlncmF0aW9uIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHk6ICR7cmVzdWx0Lm1pZ3JhdGVkQ291bnR9IHBlcnNvbmFzIG1pZ3JhdGVkYCxcbiAgICAgICAgICBtZXRhZGF0YTogeyBcbiAgICAgICAgICAgIG1pZ3JhdGVkQ291bnQ6IHJlc3VsdC5taWdyYXRlZENvdW50LCBcbiAgICAgICAgICAgIGJhY2tlZFVwOiByZXN1bHQuYmFja2VkVXAsXG4gICAgICAgICAgICBiYWNrdXBQYXRoOiByZXN1bHQuYmFja3VwUGF0aFxuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIFxuICAgICAgICAvLyBOb3RlOiBXZSBkb24ndCBhdXRvbWF0aWNhbGx5IGRlbGV0ZSB0aGUgbGVnYWN5IGRpcmVjdG9yeVxuICAgICAgICAvLyBVc2VyIHNob3VsZCBtYW51YWxseSByZW1vdmUgaXQgYWZ0ZXIgY29uZmlybWluZyBtaWdyYXRpb24gc3VjY2Vzc1xuICAgICAgfVxuICAgICAgXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHJlc3VsdC5zdWNjZXNzID0gZmFsc2U7XG4gICAgICBjb25zdCBlcnJvck1zZyA9IGBNaWdyYXRpb24gZmFpbGVkOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKX1gO1xuICAgICAgcmVzdWx0LmVycm9ycy5wdXNoKGVycm9yTXNnKTtcbiAgICAgIFxuICAgICAgLy8gU0VDVVJJVFkgRklYOiBETUNQLVNFQy0wMDYgLSBMb2cgbWlncmF0aW9uIGZhaWx1cmVzIGZvciBzZWN1cml0eSBhdWRpdCB0cmFpbFxuICAgICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgICB0eXBlOiAnRElSRUNUT1JZX01JR1JBVElPTicsXG4gICAgICAgIHNldmVyaXR5OiAnSElHSCcsXG4gICAgICAgIHNvdXJjZTogJ21pZ3JhdGlvbl9tYW5hZ2VyJyxcbiAgICAgICAgZGV0YWlsczogYE1pZ3JhdGlvbiBmYWlsZWQ6ICR7ZXJyb3JNc2d9YCxcbiAgICAgICAgbWV0YWRhdGE6IHsgXG4gICAgICAgICAgZXJyb3JUeXBlOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubmFtZSA6ICd1bmtub3duJyxcbiAgICAgICAgICBtaWdyYXRlZENvdW50OiByZXN1bHQubWlncmF0ZWRDb3VudCxcbiAgICAgICAgICBlcnJvckNvdW50OiByZXN1bHQuZXJyb3JzLmxlbmd0aFxuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIFxuICAgICAgLy8gTG9nIHdpdGggZnVsbCBlcnJvciBkZXRhaWxzIGluY2x1ZGluZyBzdGFjayB0cmFjZVxuICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKGBbTWlncmF0aW9uTWFuYWdlcl0gJHtlcnJvck1zZ31gLCB7IFxuICAgICAgICAgIHN0YWNrOiBlcnJvci5zdGFjayxcbiAgICAgICAgICBuYW1lOiBlcnJvci5uYW1lLFxuICAgICAgICAgIGNhdXNlOiBlcnJvci5jYXVzZVxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcihgW01pZ3JhdGlvbk1hbmFnZXJdICR7ZXJyb3JNc2d9YCwgeyByYXdFcnJvcjogZXJyb3IgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIFxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBNaWdyYXRlIGEgc2luZ2xlIHBlcnNvbmEgZmlsZVxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBtaWdyYXRlUGVyc29uYShmaWxlbmFtZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gTm9ybWFsaXplIGZpbGVuYW1lIHRvIHByZXZlbnQgVW5pY29kZSBhdHRhY2tzXG4gICAgY29uc3QgZmlsZW5hbWVWYWxpZGF0aW9uID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoZmlsZW5hbWUpO1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRGaWxlbmFtZSA9IGZpbGVuYW1lVmFsaWRhdGlvbi5ub3JtYWxpemVkQ29udGVudDtcbiAgICBcbiAgICBpZiAobm9ybWFsaXplZEZpbGVuYW1lICE9PSBmaWxlbmFtZSkge1xuICAgICAgbG9nZ2VyLndhcm4oYFtNaWdyYXRpb25NYW5hZ2VyXSBGaWxlbmFtZSBub3JtYWxpemVkIGZyb20gXCIke2ZpbGVuYW1lfVwiIHRvIFwiJHtub3JtYWxpemVkRmlsZW5hbWV9XCJgKTtcbiAgICB9XG4gICAgXG4gICAgaWYgKCFmaWxlbmFtZVZhbGlkYXRpb24uaXNWYWxpZCkge1xuICAgICAgbG9nZ2VyLndhcm4oYFtNaWdyYXRpb25NYW5hZ2VyXSBGaWxlbmFtZSBoYXMgVW5pY29kZSBpc3N1ZXM6ICR7ZmlsZW5hbWVWYWxpZGF0aW9uLmRldGVjdGVkSXNzdWVzPy5qb2luKCcsICcpfWApO1xuICAgICAgXG4gICAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBVbmljb2RlIGlzc3VlcyBmb3Igc2VjdXJpdHkgYXVkaXQgdHJhaWxcbiAgICAgIFNlY3VyaXR5TW9uaXRvci5sb2dTZWN1cml0eUV2ZW50KHtcbiAgICAgICAgdHlwZTogJ1VOSUNPREVfVkFMSURBVElPTl9FUlJPUicsXG4gICAgICAgIHNldmVyaXR5OiAnTUVESVVNJyxcbiAgICAgICAgc291cmNlOiAnbWlncmF0aW9uX21hbmFnZXInLFxuICAgICAgICBkZXRhaWxzOiBgVW5pY29kZSBpc3N1ZXMgZGV0ZWN0ZWQgaW4gZmlsZW5hbWUgZHVyaW5nIG1pZ3JhdGlvbjogJHtmaWxlbmFtZVZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXM/LmpvaW4oJywgJyl9YCxcbiAgICAgICAgbWV0YWRhdGE6IHsgXG4gICAgICAgICAgb3JpZ2luYWxGaWxlbmFtZTogZmlsZW5hbWUsXG4gICAgICAgICAgbm9ybWFsaXplZEZpbGVuYW1lLFxuICAgICAgICAgIGRldGVjdGVkSXNzdWVzOiBmaWxlbmFtZVZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXNcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IGxlZ2FjeVBhdGggPSBwYXRoLmpvaW4odGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldExlZ2FjeVBlcnNvbmFzRGlyKCksIGZpbGVuYW1lKTtcbiAgICBjb25zdCBuZXdQYXRoID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldEVsZW1lbnRQYXRoKEVsZW1lbnRUeXBlLlBFUlNPTkEsIG5vcm1hbGl6ZWRGaWxlbmFtZSk7XG4gICAgXG4gICAgLy8gUmVhZCB0aGUgY29udGVudFxuICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLnJlYWRGaWxlKGxlZ2FjeVBhdGgsIHtcbiAgICAgIHNvdXJjZTogJ01pZ3JhdGlvbk1hbmFnZXIubWlncmF0ZVBlcnNvbmEnXG4gICAgfSk7XG5cbiAgICAvLyBWYWxpZGF0ZSBjb250ZW50IHNpemUgYmVmb3JlIHByb2Nlc3NpbmdcbiAgICBjb25zdCBjb250ZW50U2l6ZSA9IEJ1ZmZlci5ieXRlTGVuZ3RoKGNvbnRlbnQsICd1dGY4Jyk7XG4gICAgaWYgKGNvbnRlbnRTaXplID4gU0VDVVJJVFlfTElNSVRTLk1BWF9QRVJTT05BX1NJWkVfQllURVMpIHtcbiAgICAgIGNvbnN0IG1heFNpemVLQiA9IE1hdGgucm91bmQoU0VDVVJJVFlfTElNSVRTLk1BWF9QRVJTT05BX1NJWkVfQllURVMgLyAxMDI0KTtcbiAgICAgIGNvbnN0IGFjdHVhbFNpemVLQiA9IE1hdGgucm91bmQoY29udGVudFNpemUgLyAxMDI0KTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYENvbnRlbnQgc2l6ZSAoJHthY3R1YWxTaXplS0J9S0IpIGV4Y2VlZHMgbWF4aW11bSBhbGxvd2VkIHNpemUgKCR7bWF4U2l6ZUtCfUtCKSBmb3IgcGVyc29uYSBmaWxlc2BcbiAgICAgICk7XG4gICAgfVxuXG4gICAgLy8gTm9ybWFsaXplIGNvbnRlbnQgdG8gcHJldmVudCBVbmljb2RlIGlzc3Vlc1xuICAgIGNvbnN0IGNvbnRlbnRWYWxpZGF0aW9uID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUoY29udGVudCk7XG4gICAgY29uc3Qgbm9ybWFsaXplZENvbnRlbnQgPSBjb250ZW50VmFsaWRhdGlvbi5ub3JtYWxpemVkQ29udGVudDtcbiAgICBcbiAgICBpZiAoIWNvbnRlbnRWYWxpZGF0aW9uLmlzVmFsaWQpIHtcbiAgICAgIGxvZ2dlci53YXJuKGBbTWlncmF0aW9uTWFuYWdlcl0gQ29udGVudCBoYXMgVW5pY29kZSBpc3N1ZXMgaW4gJHtmaWxlbmFtZX06ICR7Y29udGVudFZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXM/LmpvaW4oJywgJyl9YCk7XG4gICAgICBcbiAgICAgIC8vIFNFQ1VSSVRZIEZJWDogRE1DUC1TRUMtMDA2IC0gTG9nIFVuaWNvZGUgY29udGVudCBpc3N1ZXMgZm9yIHNlY3VyaXR5IGF1ZGl0IHRyYWlsXG4gICAgICBTZWN1cml0eU1vbml0b3IubG9nU2VjdXJpdHlFdmVudCh7XG4gICAgICAgIHR5cGU6ICdVTklDT0RFX1ZBTElEQVRJT05fRVJST1InLFxuICAgICAgICBzZXZlcml0eTogJ01FRElVTScsXG4gICAgICAgIHNvdXJjZTogJ21pZ3JhdGlvbl9tYW5hZ2VyJyxcbiAgICAgICAgZGV0YWlsczogYFVuaWNvZGUgaXNzdWVzIGRldGVjdGVkIGluIGNvbnRlbnQgZHVyaW5nIG1pZ3JhdGlvbjogJHtjb250ZW50VmFsaWRhdGlvbi5kZXRlY3RlZElzc3Vlcz8uam9pbignLCAnKX1gLFxuICAgICAgICBtZXRhZGF0YTogeyBcbiAgICAgICAgICBmaWxlbmFtZSxcbiAgICAgICAgICBkZXRlY3RlZElzc3VlczogY29udGVudFZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXMsXG4gICAgICAgICAgY29udGVudExlbmd0aDogY29udGVudC5sZW5ndGhcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICAgIFxuICAgIC8vIFNFQ1VSSVRZIEZJWDogQWRkIGNvbXByZWhlbnNpdmUgY29udGVudCB2YWxpZGF0aW9uIGJlZm9yZSB3cml0ZVxuICAgIC8vIEZJWEVEOiBDVkUtMjAyNS1YWFhYIC0gRGlyZWN0IGZpbGUgd3JpdGUgd2l0aG91dCBzZWN1cml0eSB2YWxpZGF0aW9uIGluIG1pZ3JhdGlvblxuICAgIC8vIE9yaWdpbmFsIGlzc3VlOiBMaW5lIDE0NyB1c2VkIGRpcmVjdCBmcy53cml0ZUZpbGUgd2l0aG91dCBjb21wcmVoZW5zaXZlIHZhbGlkYXRpb25cbiAgICAvLyBTZWN1cml0eSBpbXBhY3Q6IENvdWxkIGFsbG93IG1hbGljaW91cyBjb250ZW50IHRvIGJlIHdyaXR0ZW4gZHVyaW5nIG1pZ3JhdGlvblxuICAgIC8vIEZpeDogQWRkZWQgQ29udGVudFZhbGlkYXRvci52YWxpZGF0ZUFuZFNhbml0aXplIHdpdGggY3JpdGljYWwgdGhyZWF0IGJsb2NraW5nXG4gICAgY29uc3QgdmFsaWRhdGlvblJlc3VsdCA9IENvbnRlbnRWYWxpZGF0b3IudmFsaWRhdGVBbmRTYW5pdGl6ZShub3JtYWxpemVkQ29udGVudCk7XG4gICAgaWYgKCF2YWxpZGF0aW9uUmVzdWx0LmlzVmFsaWQgJiYgdmFsaWRhdGlvblJlc3VsdC5zZXZlcml0eSA9PT0gJ2NyaXRpY2FsJykge1xuICAgICAgY29uc3QgcGF0dGVybnMgPSB2YWxpZGF0aW9uUmVzdWx0LmRldGVjdGVkUGF0dGVybnM/LmpvaW4oJywgJykgfHwgJ3Vua25vd24gcGF0dGVybnMnO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBDcml0aWNhbCBzZWN1cml0eSB0aHJlYXQgaW4gbWlncmF0ZWQgY29udGVudCBmb3IgJHtmaWxlbmFtZX06ICR7cGF0dGVybnN9YCk7XG4gICAgfVxuICAgIFxuICAgIGNvbnN0IHZhbGlkYXRlZENvbnRlbnQgPSB2YWxpZGF0aW9uUmVzdWx0LnNhbml0aXplZENvbnRlbnQgfHwgbm9ybWFsaXplZENvbnRlbnQ7XG4gICAgXG4gICAgLy8gU0VDVVJJVFkgRklYOiBSZXBsYWNlIGRpcmVjdCB3cml0ZSB3aXRoIGF0b21pYyBvcGVyYXRpb25cbiAgICAvLyBGSVhFRDogUmFjZSBjb25kaXRpb24gdnVsbmVyYWJpbGl0eSBpbiBmaWxlIHdyaXRlcyBkdXJpbmcgbWlncmF0aW9uXG4gICAgLy8gT3JpZ2luYWwgaXNzdWU6IExpbmUgMTQ3IHVzZWQgbm9uLWF0b21pYyBmcy53cml0ZUZpbGUgb3BlcmF0aW9uXG4gICAgLy8gU2VjdXJpdHkgaW1wYWN0OiBSYWNlIGNvbmRpdGlvbnMgY291bGQgY2F1c2UgZGF0YSBjb3JydXB0aW9uIG9yIHBhcnRpYWwgd3JpdGVzXG4gICAgLy8gRml4OiBSZXBsYWNlZCB3aXRoIEZpbGVPcGVyYXRpb25zU2VydmljZS53cml0ZUZpbGUgZm9yIGd1YXJhbnRlZWQgYXRvbWljaXR5XG4gICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy53cml0ZUZpbGUobmV3UGF0aCwgdmFsaWRhdGVkQ29udGVudCwge1xuICAgICAgc291cmNlOiAnTWlncmF0aW9uTWFuYWdlci5taWdyYXRlUGVyc29uYSdcbiAgICB9KTtcbiAgICBcbiAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBmaWxlIG9wZXJhdGlvbnMgZm9yIHNlY3VyaXR5IGF1ZGl0IHRyYWlsXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0ZJTEVfQ09QSUVEJyxcbiAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgIHNvdXJjZTogJ21pZ3JhdGlvbl9tYW5hZ2VyJyxcbiAgICAgIGRldGFpbHM6IGBQZXJzb25hIGZpbGUgbWlncmF0ZWQgd2l0aCBzZWN1cml0eSB2YWxpZGF0aW9uOiAke25vcm1hbGl6ZWRGaWxlbmFtZX1gLFxuICAgICAgbWV0YWRhdGE6IHsgXG4gICAgICAgIG9yaWdpbmFsRmlsZW5hbWU6IGZpbGVuYW1lLFxuICAgICAgICBub3JtYWxpemVkRmlsZW5hbWUsXG4gICAgICAgIHNvdXJjZVBhdGg6IGxlZ2FjeVBhdGgsXG4gICAgICAgIGRlc3RpbmF0aW9uUGF0aDogbmV3UGF0aCxcbiAgICAgICAgY29udGVudExlbmd0aDogdmFsaWRhdGVkQ29udGVudC5sZW5ndGgsXG4gICAgICAgIHVuaWNvZGVOb3JtYWxpemVkOiBub3JtYWxpemVkRmlsZW5hbWUgIT09IGZpbGVuYW1lLFxuICAgICAgICB1bmljb2RlSXNzdWVzOiAhY29udGVudFZhbGlkYXRpb24uaXNWYWxpZFxuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIGxvZ2dlci5kZWJ1ZyhgW01pZ3JhdGlvbk1hbmFnZXJdIE1pZ3JhdGVkOiAke2ZpbGVuYW1lfWApO1xuICB9XG4gIFxuICAvKipcbiAgICogQ3JlYXRlIGJhY2t1cCBvZiBsZWdhY3kgcGVyc29uYXNcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY3JlYXRlQmFja3VwKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgY29uc3QgbGVnYWN5RGlyID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldExlZ2FjeVBlcnNvbmFzRGlyKCk7XG4gICAgY29uc3QgdGltZXN0YW1wID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnJlcGxhY2VBbGwoL1s6Ll0vZywgJy0nKTtcbiAgICBjb25zdCBiYWNrdXBEaXIgPSBgJHtsZWdhY3lEaXJ9X2JhY2t1cF8ke3RpbWVzdGFtcH1gO1xuICAgIFxuICAgIC8vIENyZWF0ZSBiYWNrdXAgZGlyZWN0b3J5XG4gICAgYXdhaXQgdGhpcy5maWxlT3BlcmF0aW9ucy5jcmVhdGVEaXJlY3RvcnkoYmFja3VwRGlyKTtcblxuICAgIC8vIENvcHkgYWxsIGZpbGVzXG4gICAgY29uc3QgZmlsZXMgPSBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLmxpc3REaXJlY3RvcnkobGVnYWN5RGlyKTtcbiAgICBsZXQgY29waWVkQ291bnQgPSAwO1xuXG4gICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICBjb25zdCBzcmNQYXRoID0gcGF0aC5qb2luKGxlZ2FjeURpciwgZmlsZSk7XG4gICAgICBjb25zdCBkZXN0UGF0aCA9IHBhdGguam9pbihiYWNrdXBEaXIsIGZpbGUpO1xuXG4gICAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMuc3RhdChzcmNQYXRoKTtcbiAgICAgIGlmIChzdGF0cy5pc0ZpbGUoKSkge1xuICAgICAgICBhd2FpdCB0aGlzLmZpbGVPcGVyYXRpb25zLmNvcHlGaWxlKHNyY1BhdGgsIGRlc3RQYXRoLCB7XG4gICAgICAgICAgc291cmNlOiAnTWlncmF0aW9uTWFuYWdlci5jcmVhdGVCYWNrdXAnXG4gICAgICAgIH0pO1xuICAgICAgICBjb3BpZWRDb3VudCsrO1xuICAgICAgfVxuICAgIH1cbiAgICBcbiAgICAvLyBTRUNVUklUWSBGSVg6IERNQ1AtU0VDLTAwNiAtIExvZyBiYWNrdXAgb3BlcmF0aW9uIGRldGFpbHMgZm9yIGF1ZGl0IHRyYWlsXG4gICAgU2VjdXJpdHlNb25pdG9yLmxvZ1NlY3VyaXR5RXZlbnQoe1xuICAgICAgdHlwZTogJ0ZJTEVfQ09QSUVEJyxcbiAgICAgIHNldmVyaXR5OiAnTE9XJyxcbiAgICAgIHNvdXJjZTogJ21pZ3JhdGlvbl9tYW5hZ2VyJyxcbiAgICAgIGRldGFpbHM6IGBCYWNrdXAgY3JlYXRlZDogJHtjb3BpZWRDb3VudH0gZmlsZXMgY29waWVkIHRvICR7YmFja3VwRGlyfWAsXG4gICAgICBtZXRhZGF0YTogeyBcbiAgICAgICAgYmFja3VwRGlyLFxuICAgICAgICBsZWdhY3lEaXIsXG4gICAgICAgIGZpbGVzQ29waWVkOiBjb3BpZWRDb3VudCxcbiAgICAgICAgb3BlcmF0aW9uOiAnYmFja3VwX2NyZWF0aW9uJ1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIHJldHVybiBiYWNrdXBEaXI7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgbWlncmF0aW9uIHN0YXR1cyByZXBvcnRcbiAgICovXG4gIHB1YmxpYyBhc3luYyBnZXRNaWdyYXRpb25TdGF0dXMoKTogUHJvbWlzZTx7XG4gICAgaGFzTGVnYWN5UGVyc29uYXM6IGJvb2xlYW47XG4gICAgbGVnYWN5UGVyc29uYUNvdW50OiBudW1iZXI7XG4gICAgcG9ydGZvbGlvRXhpc3RzOiBib29sZWFuO1xuICAgIHBvcnRmb2xpb1N0YXRzOiBSZWNvcmQ8RWxlbWVudFR5cGUsIG51bWJlcj47XG4gIH0+IHtcbiAgICBjb25zdCBoYXNMZWdhY3lQZXJzb25hcyA9IGF3YWl0IHRoaXMucG9ydGZvbGlvTWFuYWdlci5oYXNMZWdhY3lQZXJzb25hcygpO1xuICAgIGxldCBsZWdhY3lQZXJzb25hQ291bnQgPSAwO1xuICAgIFxuICAgIGlmIChoYXNMZWdhY3lQZXJzb25hcykge1xuICAgICAgY29uc3QgbGVnYWN5RGlyID0gdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmdldExlZ2FjeVBlcnNvbmFzRGlyKCk7XG4gICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IHRoaXMuZmlsZU9wZXJhdGlvbnMubGlzdERpcmVjdG9yeShsZWdhY3lEaXIpO1xuICAgICAgbGVnYWN5UGVyc29uYUNvdW50ID0gZmlsZXMuZmlsdGVyKGZpbGUgPT4gZmlsZS5lbmRzV2l0aCgnLm1kJykpLmxlbmd0aDtcbiAgICB9XG4gICAgXG4gICAgY29uc3QgcG9ydGZvbGlvRXhpc3RzID0gYXdhaXQgdGhpcy5wb3J0Zm9saW9NYW5hZ2VyLmV4aXN0cygpO1xuICAgIGNvbnN0IHBvcnRmb2xpb1N0YXRzID0gcG9ydGZvbGlvRXhpc3RzIFxuICAgICAgPyBhd2FpdCB0aGlzLnBvcnRmb2xpb01hbmFnZXIuZ2V0U3RhdGlzdGljcygpXG4gICAgICA6IE9iamVjdC52YWx1ZXMoRWxlbWVudFR5cGUpLnJlZHVjZSgoYWNjLCB0eXBlKSA9PiAoeyAuLi5hY2MsIFt0eXBlXTogMCB9KSwge30pIGFzIFJlY29yZDxFbGVtZW50VHlwZSwgbnVtYmVyPjtcbiAgICBcbiAgICByZXR1cm4ge1xuICAgICAgaGFzTGVnYWN5UGVyc29uYXMsXG4gICAgICBsZWdhY3lQZXJzb25hQ291bnQsXG4gICAgICBwb3J0Zm9saW9FeGlzdHMsXG4gICAgICBwb3J0Zm9saW9TdGF0c1xuICAgIH07XG4gIH1cbn0iXX0=