@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.
321 lines • 38.7 kB
JavaScript
/**
* Background Validation Service for Memory Security
*
* Part of Issue #1314 Phase 1: Memory Security Architecture
*
* PURPOSE:
* Asynchronously validates UNTRUSTED memory entries and updates their trust levels
* without blocking memory creation. Runs outside the LLM request path to avoid
* token costs and latency.
*
* ARCHITECTURE:
* - Layer 2 in the Memory Security Architecture (see docs/development/MEMORY_SECURITY_ARCHITECTURE.md)
* - Runs server-side, not in LLM context
* - No token cost for validation
* - Updates trust levels in-place
*
* TRUST LEVEL TRANSITIONS:
* UNTRUSTED → VALIDATED (clean content, no patterns)
* UNTRUSTED → FLAGGED (dangerous patterns detected, needs encryption)
* UNTRUSTED → QUARANTINED (explicitly malicious, critical threat)
*
* @module BackgroundValidator
*/
import { logger } from '../../utils/logger.js';
import { ContentValidator } from '../contentValidator.js';
import { TRUST_LEVELS } from '../../elements/memories/constants.js';
/**
* Default configuration for background validation
*/
const DEFAULT_CONFIG = {
enabled: true,
intervalSeconds: 300, // 5 minutes
batchSize: 10,
validationTimeoutMs: 5000,
};
/**
* Background validation service for memory entries
*
* This service runs outside the LLM request path to validate UNTRUSTED
* memory entries and update their trust levels based on security analysis.
*
* REFACTOR NOTE:
* Converted to full DI architecture. Removed singleton export.
* PatternExtractor is now injected as a dependency instead of static calls.
*/
export class BackgroundValidator {
patternExtractor;
memoryManager;
config;
intervalHandle;
isProcessing = false;
constructor(patternExtractor, memoryManager, // MemoryManager - using any to avoid circular import
config) {
this.patternExtractor = patternExtractor;
this.memoryManager = memoryManager;
this.config = { ...DEFAULT_CONFIG, ...config };
logger.info('BackgroundValidator initialized', {
enabled: this.config.enabled,
intervalSeconds: this.config.intervalSeconds,
batchSize: this.config.batchSize,
});
}
/**
* Start the background validation service
* Begins periodic validation of UNTRUSTED memories
*/
start() {
if (!this.config.enabled) {
logger.info('Background validation disabled in config');
return;
}
if (this.intervalHandle) {
logger.warn('Background validation already running');
return;
}
logger.info('Starting background validation service', {
intervalSeconds: this.config.intervalSeconds,
});
// Run immediately on start
void this.processUntrustedMemories();
// Then run periodically
this.intervalHandle = setInterval(() => {
void this.processUntrustedMemories();
}, this.config.intervalSeconds * 1000);
}
/**
* Stop the background validation service
*/
stop() {
if (this.intervalHandle) {
clearInterval(this.intervalHandle);
this.intervalHandle = undefined;
logger.info('Background validation service stopped');
}
}
/**
* Process all UNTRUSTED memory entries
* This is the main background validation loop
*/
async processUntrustedMemories() {
if (this.isProcessing) {
logger.debug('Validation already in progress, skipping this run');
return;
}
this.isProcessing = true;
try {
// PHASE 1 INCOMPLETE: Memory discovery integration pending
// Issue #1314 Phase 1 - This will be connected to Memory loading system
// in a follow-up PR once Memory.find() API is available
const untrustedMemories = await this.findMemoriesWithUntrustedEntries();
if (untrustedMemories.length === 0) {
return;
}
logger.info('Found untrusted memories to validate', {
count: untrustedMemories.length,
batchSize: this.config.batchSize,
});
// Process in batches
const batches = this.createBatches(untrustedMemories, this.config.batchSize);
for (const batch of batches) {
await this.processBatch(batch);
}
logger.info('Validation pass complete');
}
catch (error) {
logger.error('Error during background validation', { error });
}
finally {
this.isProcessing = false;
}
}
/**
* Find all memories that have UNTRUSTED entries
*
* DI REFACTOR: Use injected MemoryManager instead of static Memory methods
* Loads memories from filesystem and filters by trust level
*/
async findMemoriesWithUntrustedEntries() {
// Load all memories using the injected MemoryManager
const allMemories = await this.memoryManager.list();
// Filter to only memories with UNTRUSTED entries
const untrustedMemories = [];
const limit = this.config.batchSize * 10;
for (const memory of allMemories) {
const hasUntrustedEntries = memory.getEntriesByTrustLevel(TRUST_LEVELS.UNTRUSTED).length > 0;
if (hasUntrustedEntries) {
untrustedMemories.push(memory);
// Apply limit
if (untrustedMemories.length >= limit) {
break;
}
}
}
if (untrustedMemories.length > 0) {
logger.debug('Found memories with untrusted entries', {
count: untrustedMemories.length,
limit
});
}
return untrustedMemories;
}
/**
* Process a batch of memories for validation
*/
async processBatch(memories) {
logger.debug('Processing batch', { size: memories.length });
for (const memory of memories) {
try {
await this.validateMemory(memory);
}
catch (error) {
logger.error('Error validating memory', {
memoryId: memory.id,
error,
});
}
}
}
/**
* Validate all UNTRUSTED entries in a memory
* FIX #1320: Now uses public Memory API and saves changes
* FIX (Claude Bot Review): Removed type casting for memory.id
*/
async validateMemory(memory) {
logger.debug('Validating memory', { memoryId: memory.id });
let updatedCount = 0;
// FIX #1320: Use public API instead of casting to any
const entries = memory.getEntriesByTrustLevel(TRUST_LEVELS.UNTRUSTED);
for (const entry of entries) {
try {
const updated = await this.validateEntry(entry);
if (updated) {
updatedCount++;
}
}
catch (error) {
logger.error('Error validating entry', {
entryId: entry.id,
error,
});
}
}
if (updatedCount > 0) {
logger.info('Updated trust levels in memory', {
memoryId: memory.id,
updatedCount,
});
// DI REFACTOR: Use injected MemoryManager to save
try {
await this.memoryManager.save(memory);
logger.debug('Memory saved successfully', {
memoryId: memory.id
});
}
catch (error) {
logger.error('Failed to save memory after validation', {
memoryId: memory.id,
error
});
// Don't throw - continue with other memories
}
}
}
/**
* Validate a single memory entry and update its trust level
*
* @param entry - The memory entry to validate
* @returns true if the entry was updated, false otherwise
*/
async validateEntry(entry) {
logger.debug('Validating entry', { entryId: entry.id });
// Validate content using ContentValidator
const validationResult = ContentValidator.validateAndSanitize(entry.content, {
skipSizeCheck: true,
});
// Determine new trust level based on validation results
const newTrustLevel = this.determineTrustLevel(validationResult);
if (newTrustLevel === entry.trustLevel) {
logger.debug('Trust level unchanged', {
entryId: entry.id,
trustLevel: entry.trustLevel,
});
return false;
}
// Update trust level
entry.trustLevel = newTrustLevel;
logger.info('Updated entry trust level', {
entryId: entry.id,
oldTrustLevel: TRUST_LEVELS.UNTRUSTED,
newTrustLevel,
detectedPatterns: validationResult.detectedPatterns?.length || 0,
});
// If FLAGGED, extract patterns and create sanitized content
if (newTrustLevel === TRUST_LEVELS.FLAGGED) {
logger.debug('Entry flagged, extracting patterns', {
entryId: entry.id,
patterns: validationResult.detectedPatterns,
});
// Phase 1: Extract patterns and create sanitized content
const extractionResult = this.patternExtractor.extractPatterns(entry.content, validationResult);
// Store sanitized patterns and content in entry metadata
// Phase 2 will add encryption to these patterns
entry.sanitizedPatterns = extractionResult.patterns;
entry.sanitizedContent = extractionResult.sanitizedContent;
logger.info('Patterns extracted from entry', {
entryId: entry.id,
patternCount: extractionResult.patternCount,
});
}
return true;
}
/**
* Determine the appropriate trust level based on validation results
*/
determineTrustLevel(validationResult) {
// If validation passed with no patterns, mark as VALIDATED
if (validationResult.isValid && (!validationResult.detectedPatterns || validationResult.detectedPatterns.length === 0)) {
return TRUST_LEVELS.VALIDATED;
}
// If critical/high severity threats detected, mark as FLAGGED
if (validationResult.severity === 'critical' || validationResult.severity === 'high') {
// PHASE 1 INCOMPLETE: QUARANTINED trust level logic deferred (Issue #1314)
// Will add distinction between FLAGGED (dangerous) vs QUARANTINED (malicious)
// For now, all high/critical severity goes to FLAGGED
return TRUST_LEVELS.FLAGGED;
}
// Medium/low severity or minor issues - keep as UNTRUSTED for now
// (Could be VALIDATED in a more lenient policy)
return TRUST_LEVELS.UNTRUSTED;
}
/**
* Split an array into batches of specified size
*/
createBatches(items, batchSize) {
const batches = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
return batches;
}
/**
* Get current validation statistics
*/
getStats() {
return {
enabled: this.config.enabled,
isProcessing: this.isProcessing,
intervalSeconds: this.config.intervalSeconds,
batchSize: this.config.batchSize,
};
}
/**
* Dispose of the validator and clean up resources
* Implements cleanup for proper DI lifecycle management
*/
async dispose() {
this.stop();
logger.debug('BackgroundValidator disposed');
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQmFja2dyb3VuZFZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zZWN1cml0eS92YWxpZGF0aW9uL0JhY2tncm91bmRWYWxpZGF0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FzQkc7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGdCQUFnQixFQUFnQyxNQUFNLHdCQUF3QixDQUFDO0FBQ3hGLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxzQ0FBc0MsQ0FBQztBQXFCcEU7O0dBRUc7QUFDSCxNQUFNLGNBQWMsR0FBOEI7SUFDaEQsT0FBTyxFQUFFLElBQUk7SUFDYixlQUFlLEVBQUUsR0FBRyxFQUFHLFlBQVk7SUFDbkMsU0FBUyxFQUFFLEVBQUU7SUFDYixtQkFBbUIsRUFBRSxJQUFJO0NBQzFCLENBQUM7QUFrQ0Y7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxPQUFPLG1CQUFtQjtJQU1YO0lBQ0E7SUFORixNQUFNLENBQTRCO0lBQzNDLGNBQWMsQ0FBa0I7SUFDaEMsWUFBWSxHQUFZLEtBQUssQ0FBQztJQUV0QyxZQUNtQixnQkFBa0MsRUFDbEMsYUFBa0IsRUFBRSxxREFBcUQ7SUFDMUYsTUFBMkM7UUFGMUIscUJBQWdCLEdBQWhCLGdCQUFnQixDQUFrQjtRQUNsQyxrQkFBYSxHQUFiLGFBQWEsQ0FBSztRQUduQyxJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsR0FBRyxjQUFjLEVBQUUsR0FBRyxNQUFNLEVBQUUsQ0FBQztRQUMvQyxNQUFNLENBQUMsSUFBSSxDQUFDLGlDQUFpQyxFQUFFO1lBQzdDLE9BQU8sRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU87WUFDNUIsZUFBZSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZTtZQUM1QyxTQUFTLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FB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LElBQUksZ0JBQWdCLENBQUMsUUFBUSxLQUFLLE1BQU0sRUFBRSxDQUFDO1lBQ3JGLDJFQUEyRTtZQUMzRSw4RUFBOEU7WUFDOUUsc0RBQXNEO1lBQ3RELE9BQU8sWUFBWSxDQUFDLE9BQU8sQ0FBQztRQUM5QixDQUFDO1FBRUQsa0VBQWtFO1FBQ2xFLGdEQUFnRDtRQUNoRCxPQUFPLFlBQVksQ0FBQyxTQUFTLENBQUM7SUFDaEMsQ0FBQztJQUVEOztPQUVHO0lBQ0ssYUFBYSxDQUFJLEtBQVUsRUFBRSxTQUFpQjtRQUNwRCxNQUFNLE9BQU8sR0FBVSxFQUFFLENBQUM7UUFDMUIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxJQUFJLFNBQVMsRUFBRSxDQUFDO1lBQ2pELE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDOUMsQ0FBQztRQUNELE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7T0FFRztJQUNILFFBQVE7UUFDTixPQUFPO1lBQ0wsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTztZQUM1QixZQUFZLEVBQUUsSUFBSSxDQUFDLFlBQVk7WUFDL0IsZUFBZSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZTtZQUM1QyxTQUFTLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTO1NBQ2pDLENBQUM7SUFDSixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLE9BQU87UUFDWCxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDWixNQUFNLENBQUMsS0FBSyxDQUFDLDhCQUE4QixDQUFDLENBQUM7SUFDL0MsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBCYWNrZ3JvdW5kIFZhbGlkYXRpb24gU2VydmljZSBmb3IgTWVtb3J5IFNlY3VyaXR5XG4gKlxuICogUGFydCBvZiBJc3N1ZSAjMTMxNCBQaGFzZSAxOiBNZW1vcnkgU2VjdXJpdHkgQXJjaGl0ZWN0dXJlXG4gKlxuICogUFVSUE9TRTpcbiAqIEFzeW5jaHJvbm91c2x5IHZhbGlkYXRlcyBVTlRSVVNURUQgbWVtb3J5IGVudHJpZXMgYW5kIHVwZGF0ZXMgdGhlaXIgdHJ1c3QgbGV2ZWxzXG4gKiB3aXRob3V0IGJsb2NraW5nIG1lbW9yeSBjcmVhdGlvbi4gUnVucyBvdXRzaWRlIHRoZSBMTE0gcmVxdWVzdCBwYXRoIHRvIGF2b2lkXG4gKiB0b2tlbiBjb3N0cyBhbmQgbGF0ZW5jeS5cbiAqXG4gKiBBUkNISVRFQ1RVUkU6XG4gKiAtIExheWVyIDIgaW4gdGhlIE1lbW9yeSBTZWN1cml0eSBBcmNoaXRlY3R1cmUgKHNlZSBkb2NzL2RldmVsb3BtZW50L01FTU9SWV9TRUNVUklUWV9BUkNISVRFQ1RVUkUubWQpXG4gKiAtIFJ1bnMgc2VydmVyLXNpZGUsIG5vdCBpbiBMTE0gY29udGV4dFxuICogLSBObyB0b2tlbiBjb3N0IGZvciB2YWxpZGF0aW9uXG4gKiAtIFVwZGF0ZXMgdHJ1c3QgbGV2ZWxzIGluLXBsYWNlXG4gKlxuICogVFJVU1QgTEVWRUwgVFJBTlNJVElPTlM6XG4gKiBVTlRSVVNURUQg4oaSIFZBTElEQVRFRCAoY2xlYW4gY29udGVudCwgbm8gcGF0dGVybnMpXG4gKiBVTlRSVVNURUQg4oaSIEZMQUdHRUQgKGRhbmdlcm91cyBwYXR0ZXJucyBkZXRlY3RlZCwgbmVlZHMgZW5jcnlwdGlvbilcbiAqIFVOVFJVU1RFRCDihpIgUVVBUkFOVElORUQgKGV4cGxpY2l0bHkgbWFsaWNpb3VzLCBjcml0aWNhbCB0aHJlYXQpXG4gKlxuICogQG1vZHVsZSBCYWNrZ3JvdW5kVmFsaWRhdG9yXG4gKi9cblxuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IENvbnRlbnRWYWxpZGF0b3IsIHR5cGUgQ29udGVudFZhbGlkYXRpb25SZXN1bHQgfSBmcm9tICcuLi9jb250ZW50VmFsaWRhdG9yLmpzJztcbmltcG9ydCB7IFRSVVNUX0xFVkVMUyB9IGZyb20gJy4uLy4uL2VsZW1lbnRzL21lbW9yaWVzL2NvbnN0YW50cy5qcyc7XG5pbXBvcnQgeyBQYXR0ZXJuRXh0cmFjdG9yIH0gZnJvbSAnLi9QYXR0ZXJuRXh0cmFjdG9yLmpzJztcbmltcG9ydCB0eXBlIHsgTWVtb3J5IH0gZnJvbSAnLi4vLi4vZWxlbWVudHMvbWVtb3JpZXMvTWVtb3J5LmpzJztcblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIGZvciBiYWNrZ3JvdW5kIHZhbGlkYXRpb24gYmVoYXZpb3JcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBCYWNrZ3JvdW5kVmFsaWRhdG9yQ29uZmlnIHtcbiAgLyoqIEVuYWJsZSBiYWNrZ3JvdW5kIHZhbGlkYXRpb24gKGRlZmF1bHQ6IHRydWUpICovXG4gIGVuYWJsZWQ6IGJvb2xlYW47XG5cbiAgLyoqIEludGVydmFsIGluIHNlY29uZHMgYmV0d2VlbiB2YWxpZGF0aW9uIHJ1bnMgKGRlZmF1bHQ6IDMwMCA9IDUgbWludXRlcykgKi9cbiAgaW50ZXJ2YWxTZWNvbmRzOiBudW1iZXI7XG5cbiAgLyoqIE1heGltdW0gbnVtYmVyIG9mIG1lbW9yaWVzIHRvIHByb2Nlc3MgcGVyIGJhdGNoIChkZWZhdWx0OiAxMCkgKi9cbiAgYmF0Y2hTaXplOiBudW1iZXI7XG5cbiAgLyoqIE1heGltdW0gdGltZSBpbiBtcyBmb3IgYSBzaW5nbGUgdmFsaWRhdGlvbiBvcGVyYXRpb24gKGRlZmF1bHQ6IDUwMDApICovXG4gIHZhbGlkYXRpb25UaW1lb3V0TXM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBEZWZhdWx0IGNvbmZpZ3VyYXRpb24gZm9yIGJhY2tncm91bmQgdmFsaWRhdGlvblxuICovXG5jb25zdCBERUZBVUxUX0NPTkZJRzogQmFja2dyb3VuZFZhbGlkYXRvckNvbmZpZyA9IHtcbiAgZW5hYmxlZDogdHJ1ZSxcbiAgaW50ZXJ2YWxTZWNvbmRzOiAzMDAsICAvLyA1IG1pbnV0ZXNcbiAgYmF0Y2hTaXplOiAxMCxcbiAgdmFsaWRhdGlvblRpbWVvdXRNczogNTAwMCxcbn07XG5cbi8qKlxuICogUGF0dGVybiBpbmZvcm1hdGlvbiBmb3IgZW5jcnlwdGVkIHN0b3JhZ2VcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTYW5pdGl6ZWRQYXR0ZXJuIHtcbiAgLyoqIFVuaXF1ZSByZWZlcmVuY2UgSUQgZm9yIHRoaXMgcGF0dGVybiAqL1xuICByZWY6IHN0cmluZztcblxuICAvKiogSHVtYW4tcmVhZGFibGUgZGVzY3JpcHRpb24gb2YgdGhlIHBhdHRlcm4gKi9cbiAgZGVzY3JpcHRpb246IHN0cmluZztcblxuICAvKiogU2V2ZXJpdHkgbGV2ZWwgb2YgdGhlIHBhdHRlcm4gKi9cbiAgc2V2ZXJpdHk6ICdjcml0aWNhbCcgfCAnaGlnaCcgfCAnbWVkaXVtJyB8ICdsb3cnO1xuXG4gIC8qKiBMb2NhdGlvbiBpbiBvcmlnaW5hbCBjb250ZW50IChvZmZzZXQgYW5kIGxlbmd0aCkgKi9cbiAgbG9jYXRpb246IHN0cmluZztcblxuICAvKiogRW5jcnlwdGVkIHBhdHRlcm4gKEFFUy0yNTYtR0NNKSAtIFBoYXNlIDIgKi9cbiAgZW5jcnlwdGVkUGF0dGVybj86IHN0cmluZztcblxuICAvKiogRW5jcnlwdGlvbiBhbGdvcml0aG0gdXNlZCAtIFBoYXNlIDIgKi9cbiAgYWxnb3JpdGhtPzogc3RyaW5nO1xuXG4gIC8qKiBJbml0aWFsaXphdGlvbiB2ZWN0b3IgZm9yIGRlY3J5cHRpb24gLSBQaGFzZSAyICovXG4gIGl2Pzogc3RyaW5nO1xuXG4gIC8qKiBHQ00gYXV0aGVudGljYXRpb24gdGFnIGZvciBpbnRlZ3JpdHkgdmVyaWZpY2F0aW9uIC0gUGhhc2UgMiAqL1xuICBhdXRoVGFnPzogc3RyaW5nO1xuXG4gIC8qKiBTYWZldHkgaW5zdHJ1Y3Rpb24gZm9yIHBhdHRlcm4gdXNhZ2UgKi9cbiAgc2FmZXR5SW5zdHJ1Y3Rpb246IHN0cmluZztcbn1cblxuLyoqXG4gKiBCYWNrZ3JvdW5kIHZhbGlkYXRpb24gc2VydmljZSBmb3IgbWVtb3J5IGVudHJpZXNcbiAqXG4gKiBUaGlzIHNlcnZpY2UgcnVucyBvdXRzaWRlIHRoZSBMTE0gcmVxdWVzdCBwYXRoIHRvIHZhbGlkYXRlIFVOVFJVU1RFRFxuICogbWVtb3J5IGVudHJpZXMgYW5kIHVwZGF0ZSB0aGVpciB0cnVzdCBsZXZlbHMgYmFzZWQgb24gc2VjdXJpdHkgYW5hbHlzaXMuXG4gKlxuICogUkVGQUNUT1IgTk9URTpcbiAqIENvbnZlcnRlZCB0byBmdWxsIERJIGFyY2hpdGVjdHVyZS4gUmVtb3ZlZCBzaW5nbGV0b24gZXhwb3J0LlxuICogUGF0dGVybkV4dHJhY3RvciBpcyBub3cgaW5qZWN0ZWQgYXMgYSBkZXBlbmRlbmN5IGluc3RlYWQgb2Ygc3RhdGljIGNhbGxzLlxuICovXG5leHBvcnQgY2xhc3MgQmFja2dyb3VuZFZhbGlkYXRvciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBCYWNrZ3JvdW5kVmFsaWRhdG9yQ29uZmlnO1xuICBwcml2YXRlIGludGVydmFsSGFuZGxlPzogTm9kZUpTLlRpbWVvdXQ7XG4gIHByaXZhdGUgaXNQcm9jZXNzaW5nOiBib29sZWFuID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBwYXR0ZXJuRXh0cmFjdG9yOiBQYXR0ZXJuRXh0cmFjdG9yLFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbWVtb3J5TWFuYWdlcjogYW55LCAvLyBNZW1vcnlNYW5hZ2VyIC0gdXNpbmcgYW55IHRvIGF2b2lkIGNpcmN1bGFyIGltcG9ydFxuICAgIGNvbmZpZz86IFBhcnRpYWw8QmFja2dyb3VuZFZhbGlkYXRvckNvbmZpZz5cbiAgKSB7XG4gICAgdGhpcy5jb25maWcgPSB7IC4uLkRFRkFVTFRfQ09ORklHLCAuLi5jb25maWcgfTtcbiAgICBsb2dnZXIuaW5mbygnQmFja2dyb3VuZFZhbGlkYXRvciBpbml0aWFsaXplZCcsIHtcbiAgICAgIGVuYWJsZWQ6IHRoaXMuY29uZmlnLmVuYWJsZWQsXG4gICAgICBpbnRlcnZhbFNlY29uZHM6IHRoaXMuY29uZmlnLmludGVydmFsU2Vjb25kcyxcbiAgICAgIGJhdGNoU2l6ZTogdGhpcy5jb25maWcuYmF0Y2hTaXplLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0IHRoZSBiYWNrZ3JvdW5kIHZhbGlkYXRpb24gc2VydmljZVxuICAgKiBCZWdpbnMgcGVyaW9kaWMgdmFsaWRhdGlvbiBvZiBVTlRSVVNURUQgbWVtb3JpZXNcbiAgICovXG4gIHN0YXJ0KCk6IHZvaWQge1xuICAgIGlmICghdGhpcy5jb25maWcuZW5hYmxlZCkge1xuICAgICAgbG9nZ2VyLmluZm8oJ0JhY2tncm91bmQgdmFsaWRhdGlvbiBkaXNhYmxlZCBpbiBjb25maWcnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5pbnRlcnZhbEhhbmRsZSkge1xuICAgICAgbG9nZ2VyLndhcm4oJ0JhY2tncm91bmQgdmFsaWRhdGlvbiBhbHJlYWR5IHJ1bm5pbmcnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBsb2dnZXIuaW5mbygnU3RhcnRpbmcgYmFja2dyb3VuZCB2YWxpZGF0aW9uIHNlcnZpY2UnLCB7XG4gICAgICBpbnRlcnZhbFNlY29uZHM6IHRoaXMuY29uZmlnLmludGVydmFsU2Vjb25kcyxcbiAgICB9KTtcblxuICAgIC8vIFJ1biBpbW1lZGlhdGVseSBvbiBzdGFydFxuICAgIHZvaWQgdGhpcy5wcm9jZXNzVW50cnVzdGVkTWVtb3JpZXMoKTtcblxuICAgIC8vIFRoZW4gcnVuIHBlcmlvZGljYWxseVxuICAgIHRoaXMuaW50ZXJ2YWxIYW5kbGUgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICB2b2lkIHRoaXMucHJvY2Vzc1VudHJ1c3RlZE1lbW9yaWVzKCk7XG4gICAgfSwgdGhpcy5jb25maWcuaW50ZXJ2YWxTZWNvbmRzICogMTAwMCk7XG4gIH1cblxuICAvKipcbiAgICogU3RvcCB0aGUgYmFja2dyb3VuZCB2YWxpZGF0aW9uIHNlcnZpY2VcbiAgICovXG4gIHN0b3AoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuaW50ZXJ2YWxIYW5kbGUpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5pbnRlcnZhbEhhbmRsZSk7XG4gICAgICB0aGlzLmludGVydmFsSGFuZGxlID0gdW5kZWZpbmVkO1xuICAgICAgbG9nZ2VyLmluZm8oJ0JhY2tncm91bmQgdmFsaWRhdGlvbiBzZXJ2aWNlIHN0b3BwZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUHJvY2VzcyBhbGwgVU5UUlVTVEVEIG1lbW9yeSBlbnRyaWVzXG4gICAqIFRoaXMgaXMgdGhlIG1haW4gYmFja2dyb3VuZCB2YWxpZGF0aW9uIGxvb3BcbiAgICovXG4gIGFzeW5jIHByb2Nlc3NVbnRydXN0ZWRNZW1vcmllcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5pc1Byb2Nlc3NpbmcpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnVmFsaWRhdGlvbiBhbHJlYWR5IGluIHByb2dyZXNzLCBza2lwcGluZyB0aGlzIHJ1bicpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRoaXMuaXNQcm9jZXNzaW5nID0gdHJ1ZTtcblxuICAgIHRyeSB7XG4gICAgICAvLyBQSEFTRSAxIElOQ09NUExFVEU6IE1lbW9yeSBkaXNjb3ZlcnkgaW50ZWdyYXRpb24gcGVuZGluZ1xuICAgICAgLy8gSXNzdWUgIzEzMTQgUGhhc2UgMSAtIFRoaXMgd2lsbCBiZSBjb25uZWN0ZWQgdG8gTWVtb3J5IGxvYWRpbmcgc3lzdGVtXG4gICAgICAvLyBpbiBhIGZvbGxvdy11cCBQUiBvbmNlIE1lbW9yeS5maW5kKCkgQVBJIGlzIGF2YWlsYWJsZVxuICAgICAgY29uc3QgdW50cnVzdGVkTWVtb3JpZXMgPSBhd2FpdCB0aGlzLmZpbmRNZW1vcmllc1dpdGhVbnRydXN0ZWRFbnRyaWVzKCk7XG5cbiAgICAgIGlmICh1bnRydXN0ZWRNZW1vcmllcy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBsb2dnZXIuaW5mbygnRm91bmQgdW50cnVzdGVkIG1lbW9yaWVzIHRvIHZhbGlkYXRlJywge1xuICAgICAgICBjb3VudDogdW50cnVzdGVkTWVtb3JpZXMubGVuZ3RoLFxuICAgICAgICBiYXRjaFNpemU6IHRoaXMuY29uZmlnLmJhdGNoU2l6ZSxcbiAgICAgIH0pO1xuXG4gICAgICAvLyBQcm9jZXNzIGluIGJhdGNoZXNcbiAgICAgIGNvbnN0IGJhdGNoZXMgPSB0aGlzLmNyZWF0ZUJhdGNoZXModW50cnVzdGVkTWVtb3JpZXMsIHRoaXMuY29uZmlnLmJhdGNoU2l6ZSk7XG5cbiAgICAgIGZvciAoY29uc3QgYmF0Y2ggb2YgYmF0Y2hlcykge1xuICAgICAgICBhd2FpdCB0aGlzLnByb2Nlc3NCYXRjaChiYXRjaCk7XG4gICAgICB9XG5cbiAgICAgIGxvZ2dlci5pbmZvKCdWYWxpZGF0aW9uIHBhc3MgY29tcGxldGUnKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdFcnJvciBkdXJpbmcgYmFja2dyb3VuZCB2YWxpZGF0aW9uJywgeyBlcnJvciB9KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy5pc1Byb2Nlc3NpbmcgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRmluZCBhbGwgbWVtb3JpZXMgdGhhdCBoYXZlIFVOVFJVU1RFRCBlbnRyaWVzXG4gICAqXG4gICAqIERJIFJFRkFDVE9SOiBVc2UgaW5qZWN0ZWQgTWVtb3J5TWFuYWdlciBpbnN0ZWFkIG9mIHN0YXRpYyBNZW1vcnkgbWV0aG9kc1xuICAgKiBMb2FkcyBtZW1vcmllcyBmcm9tIGZpbGVzeXN0ZW0gYW5kIGZpbHRlcnMgYnkgdHJ1c3QgbGV2ZWxcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgZmluZE1lbW9yaWVzV2l0aFVudHJ1c3RlZEVudHJpZXMoKTogUHJvbWlzZTxNZW1vcnlbXT4ge1xuICAgIC8vIExvYWQgYWxsIG1lbW9yaWVzIHVzaW5nIHRoZSBpbmplY3RlZCBNZW1vcnlNYW5hZ2VyXG4gICAgY29uc3QgYWxsTWVtb3JpZXMgPSBhd2FpdCB0aGlzLm1lbW9yeU1hbmFnZXIubGlzdCgpO1xuXG4gICAgLy8gRmlsdGVyIHRvIG9ubHkgbWVtb3JpZXMgd2l0aCBVTlRSVVNURUQgZW50cmllc1xuICAgIGNvbnN0IHVudHJ1c3RlZE1lbW9yaWVzOiBNZW1vcnlbXSA9IFtdO1xuICAgIGNvbnN0IGxpbWl0ID0gdGhpcy5jb25maWcuYmF0Y2hTaXplICogMTA7XG5cbiAgICBmb3IgKGNvbnN0IG1lbW9yeSBvZiBhbGxNZW1vcmllcykge1xuICAgICAgY29uc3QgaGFzVW50cnVzdGVkRW50cmllcyA9IG1lbW9yeS5nZXRFbnRyaWVzQnlUcnVzdExldmVsKFRSVVNUX0xFVkVMUy5VTlRSVVNURUQpLmxlbmd0aCA+IDA7XG4gICAgICBpZiAoaGFzVW50cnVzdGVkRW50cmllcykge1xuICAgICAgICB1bnRydXN0ZWRNZW1vcmllcy5wdXNoKG1lbW9yeSk7XG5cbiAgICAgICAgLy8gQXBwbHkgbGltaXRcbiAgICAgICAgaWYgKHVudHJ1c3RlZE1lbW9yaWVzLmxlbmd0aCA+PSBsaW1pdCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHVudHJ1c3RlZE1lbW9yaWVzLmxlbmd0aCA+IDApIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRm91bmQgbWVtb3JpZXMgd2l0aCB1bnRydXN0ZWQgZW50cmllcycsIHtcbiAgICAgICAgY291bnQ6IHVudHJ1c3RlZE1lbW9yaWVzLmxlbmd0aCxcbiAgICAgICAgbGltaXRcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB1bnRydXN0ZWRNZW1vcmllcztcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9jZXNzIGEgYmF0Y2ggb2YgbWVtb3JpZXMgZm9yIHZhbGlkYXRpb25cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcHJvY2Vzc0JhdGNoKG1lbW9yaWVzOiBNZW1vcnlbXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGxvZ2dlci5kZWJ1ZygnUHJvY2Vzc2luZyBiYXRjaCcsIHsgc2l6ZTogbWVtb3JpZXMubGVuZ3RoIH0pO1xuXG4gICAgZm9yIChjb25zdCBtZW1vcnkgb2YgbWVtb3JpZXMpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHRoaXMudmFsaWRhdGVNZW1vcnkobWVtb3J5KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgdmFsaWRhdGluZyBtZW1vcnknLCB7XG4gICAgICAgICAgbWVtb3J5SWQ6IG1lbW9yeS5pZCxcbiAgICAgICAgICBlcnJvcixcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGFsbCBVTlRSVVNURUQgZW50cmllcyBpbiBhIG1lbW9yeVxuICAgKiBGSVggIzEzMjA6IE5vdyB1c2VzIHB1YmxpYyBNZW1vcnkgQVBJIGFuZCBzYXZlcyBjaGFuZ2VzXG4gICAqIEZJWCAoQ2xhdWRlIEJvdCBSZXZpZXcpOiBSZW1vdmVkIHR5cGUgY2FzdGluZyBmb3IgbWVtb3J5LmlkXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHZhbGlkYXRlTWVtb3J5KG1lbW9yeTogTWVtb3J5KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbG9nZ2VyLmRlYnVnKCdWYWxpZGF0aW5nIG1lbW9yeScsIHsgbWVtb3J5SWQ6IG1lbW9yeS5pZCB9KTtcblxuICAgIGxldCB1cGRhdGVkQ291bnQgPSAwO1xuXG4gICAgLy8gRklYICMxMzIwOiBVc2UgcHVibGljIEFQSSBpbnN0ZWFkIG9mIGNhc3RpbmcgdG8gYW55XG4gICAgY29uc3QgZW50cmllcyA9IG1lbW9yeS5nZXRFbnRyaWVzQnlUcnVzdExldmVsKFRSVVNUX0xFVkVMUy5VTlRSVVNURUQpO1xuXG4gICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB1cGRhdGVkID0gYXdhaXQgdGhpcy52YWxpZGF0ZUVudHJ5KGVudHJ5KTtcbiAgICAgICAgaWYgKHVwZGF0ZWQpIHtcbiAgICAgICAgICB1cGRhdGVkQ291bnQrKztcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdFcnJvciB2YWxpZGF0aW5nIGVudHJ5Jywge1xuICAgICAgICAgIGVudHJ5SWQ6IGVudHJ5LmlkLFxuICAgICAgICAgIGVycm9yLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodXBkYXRlZENvdW50ID4gMCkge1xuICAgICAgbG9nZ2VyLmluZm8oJ1VwZGF0ZWQgdHJ1c3QgbGV2ZWxzIGluIG1lbW9yeScsIHtcbiAgICAgICAgbWVtb3J5SWQ6IG1lbW9yeS5pZCxcbiAgICAgICAgdXBkYXRlZENvdW50LFxuICAgICAgfSk7XG5cbiAgICAgIC8vIERJIFJFRkFDVE9SOiBVc2UgaW5qZWN0ZWQgTWVtb3J5TWFuYWdlciB0byBzYXZlXG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLm1lbW9yeU1hbmFnZXIuc2F2ZShtZW1vcnkpO1xuICAgICAgICBsb2dnZXIuZGVidWcoJ01lbW9yeSBzYXZlZCBzdWNjZXNzZnVsbHknLCB7XG4gICAgICAgICAgbWVtb3J5SWQ6IG1lbW9yeS5pZFxuICAgICAgICB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIHNhdmUgbWVtb3J5IGFmdGVyIHZhbGlkYXRpb24nLCB7XG4gICAgICAgICAgbWVtb3J5SWQ6IG1lbW9yeS5pZCxcbiAgICAgICAgICBlcnJvclxuICAgICAgICB9KTtcbiAgICAgICAgLy8gRG9uJ3QgdGhyb3cgLSBjb250aW51ZSB3aXRoIG90aGVyIG1lbW9yaWVzXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFZhbGlkYXRlIGEgc2luZ2xlIG1lbW9yeSBlbnRyeSBhbmQgdXBkYXRlIGl0cyB0cnVzdCBsZXZlbFxuICAgKlxuICAgKiBAcGFyYW0gZW50cnkgLSBUaGUgbWVtb3J5IGVudHJ5IHRvIHZhbGlkYXRlXG4gICAqIEByZXR1cm5zIHRydWUgaWYgdGhlIGVudHJ5IHdhcyB1cGRhdGVkLCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgdmFsaWRhdGVFbnRyeShlbnRyeTogYW55KTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgbG9nZ2VyLmRlYnVnKCdWYWxpZGF0aW5nIGVudHJ5JywgeyBlbnRyeUlkOiBlbnRyeS5pZCB9KTtcblxuICAgIC8vIFZhbGlkYXRlIGNvbnRlbnQgdXNpbmcgQ29udGVudFZhbGlkYXRvclxuICAgIGNvbnN0IHZhbGlkYXRpb25SZXN1bHQgPSBDb250ZW50VmFsaWRhdG9yLnZhbGlkYXRlQW5kU2FuaXRpemUoZW50cnkuY29udGVudCwge1xuICAgICAgc2tpcFNpemVDaGVjazogdHJ1ZSxcbiAgICB9KTtcblxuICAgIC8vIERldGVybWluZSBuZXcgdHJ1c3QgbGV2ZWwgYmFzZWQgb24gdmFsaWRhdGlvbiByZXN1bHRzXG4gICAgY29uc3QgbmV3VHJ1c3RMZXZlbCA9IHRoaXMuZGV0ZXJtaW5lVHJ1c3RMZXZlbCh2YWxpZGF0aW9uUmVzdWx0KTtcblxuICAgIGlmIChuZXdUcnVzdExldmVsID09PSBlbnRyeS50cnVzdExldmVsKSB7XG4gICAgICBsb2dnZXIuZGVidWcoJ1RydXN0IGxldmVsIHVuY2hhbmdlZCcsIHtcbiAgICAgICAgZW50cnlJZDogZW50cnkuaWQsXG4gICAgICAgIHRydXN0TGV2ZWw6IGVudHJ5LnRydXN0TGV2ZWwsXG4gICAgICB9KTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICAvLyBVcGRhdGUgdHJ1c3QgbGV2ZWxcbiAgICBlbnRyeS50cnVzdExldmVsID0gbmV3VHJ1c3RMZXZlbDtcblxuICAgIGxvZ2dlci5pbmZvKCdVcGRhdGVkIGVudHJ5IHRydXN0IGxldmVsJywge1xuICAgICAgZW50cnlJZDogZW50cnkuaWQsXG4gICAgICBvbGRUcnVzdExldmVsOiBUUlVTVF9MRVZFTFMuVU5UUlVTVEVELFxuICAgICAgbmV3VHJ1c3RMZXZlbCxcbiAgICAgIGRldGVjdGVkUGF0dGVybnM6IHZhbGlkYXRpb25SZXN1bHQuZGV0ZWN0ZWRQYXR0ZXJucz8ubGVuZ3RoIHx8IDAsXG4gICAgfSk7XG5cbiAgICAvLyBJZiBGTEFHR0VELCBleHRyYWN0IHBhdHRlcm5zIGFuZCBjcmVhdGUgc2FuaXRpemVkIGNvbnRlbnRcbiAgICBpZiAobmV3VHJ1c3RMZXZlbCA9PT0gVFJVU1RfTEVWRUxTLkZMQUdHRUQpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRW50cnkgZmxhZ2dlZCwgZXh0cmFjdGluZyBwYXR0ZXJucycsIHtcbiAgICAgICAgZW50cnlJZDogZW50cnkuaWQsXG4gICAgICAgIHBhdHRlcm5zOiB2YWxpZGF0aW9uUmVzdWx0LmRldGVjdGVkUGF0dGVybnMsXG4gICAgICB9KTtcblxuICAgICAgLy8gUGhhc2UgMTogRXh0cmFjdCBwYXR0ZXJucyBhbmQgY3JlYXRlIHNhbml0aXplZCBjb250ZW50XG4gICAgICBjb25zdCBleHRyYWN0aW9uUmVzdWx0ID0gdGhpcy5wYXR0ZXJuRXh0cmFjdG9yLmV4dHJhY3RQYXR0ZXJucyhcbiAgICAgICAgZW50cnkuY29udGVudCxcbiAgICAgICAgdmFsaWRhdGlvblJlc3VsdFxuICAgICAgKTtcblxuICAgICAgLy8gU3RvcmUgc2FuaXRpemVkIHBhdHRlcm5zIGFuZCBjb250ZW50IGluIGVudHJ5IG1ldGFkYXRhXG4gICAgICAvLyBQaGFzZSAyIHdpbGwgYWRkIGVuY3J5cHRpb24gdG8gdGhlc2UgcGF0dGVybnNcbiAgICAgIGVudHJ5LnNhbml0aXplZFBhdHRlcm5zID0gZXh0cmFjdGlvblJlc3VsdC5wYXR0ZXJucztcbiAgICAgIGVudHJ5LnNhbml0aXplZENvbnRlbnQgPSBleHRyYWN0aW9uUmVzdWx0LnNhbml0aXplZENvbnRlbnQ7XG5cbiAgICAgIGxvZ2dlci5pbmZvKCdQYXR0ZXJucyBleHRyYWN0ZWQgZnJvbSBlbnRyeScsIHtcbiAgICAgICAgZW50cnlJZDogZW50cnkuaWQsXG4gICAgICAgIHBhdHRlcm5Db3VudDogZXh0cmFjdGlvblJlc3VsdC5wYXR0ZXJuQ291bnQsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmUgdGhlIGFwcHJvcHJpYXRlIHRydXN0IGxldmVsIGJhc2VkIG9uIHZhbGlkYXRpb24gcmVzdWx0c1xuICAgKi9cbiAgcHJpdmF0ZSBkZXRlcm1pbmVUcnVzdExldmVsKHZhbGlkYXRpb25SZXN1bHQ6IENvbnRlbnRWYWxpZGF0aW9uUmVzdWx0KTogc3RyaW5nIHtcbiAgICAvLyBJZiB2YWxpZGF0aW9uIHBhc3NlZCB3aXRoIG5vIHBhdHRlcm5zLCBtYXJrIGFzIFZBTElEQVRFRFxuICAgIGlmICh2YWxpZGF0aW9uUmVzdWx0LmlzVmFsaWQgJiYgKCF2YWxpZGF0aW9uUmVzdWx0LmRldGVjdGVkUGF0dGVybnMgfHwgdmFsaWRhdGlvblJlc3VsdC5kZXRlY3RlZFBhdHRlcm5zLmxlbmd0aCA9PT0gMCkpIHtcbiAgICAgIHJldHVybiBUUlVTVF9MRVZFTFMuVkFMSURBVEVEO1xuICAgIH1cblxuICAgIC8vIElmIGNyaXRpY2FsL2hpZ2ggc2V2ZXJpdHkgdGhyZWF0cyBkZXRlY3RlZCwgbWFyayBhcyBGTEFHR0VEXG4gICAgaWYgKHZhbGlkYXRpb25SZXN1bHQuc2V2ZXJpdHkgPT09ICdjcml0aWNhbCcgfHwgdmFsaWRhdGlvblJlc3VsdC5zZXZlcml0eSA9PT0gJ2hpZ2gnKSB7XG4gICAgICAvLyBQSEFTRSAxIElOQ09NUExFVEU6IFFVQVJBTlRJTkVEIHRydXN0IGxldmVsIGxvZ2ljIGRlZmVycmVkIChJc3N1ZSAjMTMxNClcbiAgICAgIC8vIFdpbGwgYWRkIGRpc3RpbmN0aW9uIGJldHdlZW4gRkxBR0dFRCAoZGFuZ2Vyb3VzKSB2cyBRVUFSQU5USU5FRCAobWFsaWNpb3VzKVxuICAgICAgLy8gRm9yIG5vdywgYWxsIGhpZ2gvY3JpdGljYWwgc2V2ZXJpdHkgZ29lcyB0byBGTEFHR0VEXG4gICAgICByZXR1cm4gVFJVU1RfTEVWRUxTLkZMQUdHRUQ7XG4gICAgfVxuXG4gICAgLy8gTWVkaXVtL2xvdyBzZXZlcml0eSBvciBtaW5vciBpc3N1ZXMgLSBrZWVwIGFzIFVOVFJVU1RFRCBmb3Igbm93XG4gICAgLy8gKENvdWxkIGJlIFZBTElEQVRFRCBpbiBhIG1vcmUgbGVuaWVudCBwb2xpY3kpXG4gICAgcmV0dXJuIFRSVVNUX0xFVkVMUy5VTlRSVVNURUQ7XG4gIH1cblxuICAvKipcbiAgICogU3BsaXQgYW4gYXJyYXkgaW50byBiYXRjaGVzIG9mIHNwZWNpZmllZCBzaXplXG4gICAqL1xuICBwcml2YXRlIGNyZWF0ZUJhdGNoZXM8VD4oaXRlbXM6IFRbXSwgYmF0Y2hTaXplOiBudW1iZXIpOiBUW11bXSB7XG4gICAgY29uc3QgYmF0Y2hlczogVFtdW10gPSBbXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGl0ZW1zLmxlbmd0aDsgaSArPSBiYXRjaFNpemUpIHtcbiAgICAgIGJhdGNoZXMucHVzaChpdGVtcy5zbGljZShpLCBpICsgYmF0Y2hTaXplKSk7XG4gICAgfVxuICAgIHJldHVybiBiYXRjaGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBjdXJyZW50IHZhbGlkYXRpb24gc3RhdGlzdGljc1xuICAgKi9cbiAgZ2V0U3RhdHMoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGVuYWJsZWQ6IHRoaXMuY29uZmlnLmVuYWJsZWQsXG4gICAgICBpc1Byb2Nlc3Npbmc6IHRoaXMuaXNQcm9jZXNzaW5nLFxuICAgICAgaW50ZXJ2YWxTZWNvbmRzOiB0aGlzLmNvbmZpZy5pbnRlcnZhbFNlY29uZHMsXG4gICAgICBiYXRjaFNpemU6IHRoaXMuY29uZmlnLmJhdGNoU2l6ZSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIERpc3Bvc2Ugb2YgdGhlIHZhbGlkYXRvciBhbmQgY2xlYW4gdXAgcmVzb3VyY2VzXG4gICAqIEltcGxlbWVudHMgY2xlYW51cCBmb3IgcHJvcGVyIERJIGxpZmVjeWNsZSBtYW5hZ2VtZW50XG4gICAqL1xuICBhc3luYyBkaXNwb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuc3RvcCgpO1xuICAgIGxvZ2dlci5kZWJ1ZygnQmFja2dyb3VuZFZhbGlkYXRvciBkaXNwb3NlZCcpO1xuICB9XG59XG4iXX0=