@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.
357 lines • 49 kB
JavaScript
/**
* NLP Scoring Manager - Jaccard similarity and Shannon entropy for semantic analysis
*
* Implements intelligent document similarity scoring using:
* - Jaccard similarity for vocabulary overlap
* - Shannon entropy for information density
* - Combined scoring for meaningful semantic relationships
*
* Key insights from analysis:
* - High Jaccard (>60%) + Moderate entropy (4.5-6.0) = Same technical domain
* - High Jaccard + Low entropy (<3.0) = Stop word pollution, superficial
* - Low Jaccard + Similar entropy = Different domains, equally complex
*
* Part of Enhanced Capability Index (#1085)
*/
import { logger } from '../utils/logger.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
import { IndexConfigManager } from './config/IndexConfig.js';
export class NLPScoringManager {
cache;
cacheAccessOrder; // Track access order for LRU
config;
unicodeValidator;
cleanupInterval;
constructor(config, indexConfigManager = new IndexConfigManager()) {
// Get config from central manager
const indexConfig = indexConfigManager.getConfig();
this.config = {
minTokenLength: indexConfig.nlp.minTokenLength,
cacheExpiry: indexConfig.nlp.cacheExpiryMinutes * 60 * 1000,
maxCacheSize: indexConfig.memory.maxCacheSize,
entropyBands: indexConfig.nlp.entropyBands,
jaccardThresholds: indexConfig.nlp.jaccardThresholds,
...config
};
this.cache = new Map();
this.cacheAccessOrder = [];
this.unicodeValidator = new UnicodeValidator();
// Periodic cleanup of expired entries
this.cleanupInterval = setInterval(() => this.cleanExpiredCache(), 60000); // Every minute
if (typeof this.cleanupInterval.unref === 'function') {
this.cleanupInterval.unref();
}
}
/**
* Clean and tokenize text for analysis
* Works with any language - no hardcoded stop words
*/
cleanAndTokenize(text) {
// Normalize Unicode for security
const validation = UnicodeValidator.normalize(text);
if (validation.detectedIssues && validation.detectedIssues.length > 0) {
logger.warn('Unicode issues in NLP text', { issues: validation.detectedIssues });
}
text = validation.normalizedContent;
// Convert to lowercase and split on word boundaries
// Keep Unicode letter characters for multilingual support
const tokens = text.toLowerCase()
.replaceAll(/[^\p{L}\p{N}\s_-]/gu, ' ') // Unicode-aware: keep letters, numbers, underscore, hyphen
.split(/\s+/)
.filter(token => token.length >= this.config.minTokenLength);
return new Set(tokens);
}
/**
* Calculate Jaccard similarity between two text strings
*
* Jaccard = |A ∩ B| / |A ∪ B|
*
* Returns value between 0 (no overlap) and 1 (identical)
*/
calculateJaccard(text1, text2) {
const tokens1 = this.cleanAndTokenize(text1);
const tokens2 = this.cleanAndTokenize(text2);
if (tokens1.size === 0 && tokens2.size === 0) {
return 1.0; // Both empty = identical
}
if (tokens1.size === 0 || tokens2.size === 0) {
return 0.0; // One empty = no similarity
}
// Calculate intersection
const intersection = new Set([...tokens1].filter(token => tokens2.has(token)));
// Calculate union
const union = new Set([...tokens1, ...tokens2]);
return intersection.size / union.size;
}
/**
* Calculate Shannon entropy for text
*
* H(X) = -Σ p(x) * log2(p(x))
*
* Measures information density/vocabulary richness
* Higher entropy = more diverse vocabulary
*/
calculateEntropy(text) {
const tokens = Array.from(this.cleanAndTokenize(text));
if (tokens.length === 0) {
return 0;
}
// Calculate token frequencies
const frequencies = new Map();
for (const token of tokens) {
frequencies.set(token, (frequencies.get(token) || 0) + 1);
}
// Calculate probabilities and entropy
let entropy = 0;
const totalTokens = tokens.length;
for (const count of frequencies.values()) {
const probability = count / totalTokens;
if (probability > 0) {
entropy -= probability * Math.log2(probability);
}
}
return entropy;
}
/**
* Calculate combined relevance score using Jaccard and entropy
*
* Interprets the relationship between similarity and complexity
*/
scoreRelevance(text1, text2) {
// Check cache first
const cacheKey = `${text1.substring(0, 50)}:::${text2.substring(0, 50)}`;
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.config.cacheExpiry) {
// Update access order for LRU
cached.lastAccessed = Date.now();
this.updateAccessOrder(cacheKey);
return cached.result;
}
// Calculate metrics
const jaccard = this.calculateJaccard(text1, text2);
const entropy1 = this.calculateEntropy(text1);
const entropy2 = this.calculateEntropy(text2);
const avgEntropy = (entropy1 + entropy2) / 2;
// Get token sets for additional metrics
const tokens1 = this.cleanAndTokenize(text1);
const tokens2 = this.cleanAndTokenize(text2);
const intersection = new Set([...tokens1].filter(token => tokens2.has(token)));
// Interpret the combination
let combinedScore;
let interpretation;
// High Jaccard + Moderate-High entropy = Excellent match (same domain)
if (jaccard >= this.config.jaccardThresholds.high &&
avgEntropy >= this.config.entropyBands.low) {
// Scale score based on entropy quality
const entropyQuality = Math.min(1.0, (avgEntropy - 2.0) / 4.0); // 0 at entropy=2, 1 at entropy=6+
combinedScore = 0.7 + (jaccard - 0.6) * 0.5 + entropyQuality * 0.2;
if (avgEntropy >= this.config.entropyBands.moderate) {
interpretation = 'Excellent match - same technical domain with rich vocabulary';
}
else {
interpretation = 'Good match - high overlap but simpler vocabulary';
}
}
// High Jaccard + Very Low entropy = Common word pollution
else if (jaccard >= this.config.jaccardThresholds.high &&
avgEntropy < 2.0) {
combinedScore = 0.3 + jaccard * 0.2; // Penalize heavily
interpretation = 'Superficial similarity - mostly common words';
}
// Moderate Jaccard + Good entropy = Related concepts
else if (jaccard >= this.config.jaccardThresholds.moderate &&
avgEntropy >= this.config.entropyBands.low) {
combinedScore = 0.4 + jaccard * 0.4 + (avgEntropy / 10) * 0.2;
interpretation = 'Moderate match - related concepts with good complexity';
}
// Low Jaccard + Similar entropy = Different domains
else if (jaccard < this.config.jaccardThresholds.low &&
Math.abs(entropy1 - entropy2) < 1.0) {
combinedScore = jaccard * 0.5;
interpretation = 'Different domains with similar complexity';
}
// Low Jaccard + Different entropy = Unrelated
else {
combinedScore = jaccard * 0.3;
interpretation = 'Low relevance - different topics and complexity';
}
// Ensure score is between 0 and 1
combinedScore = Math.max(0, Math.min(1, combinedScore));
const result = {
jaccard,
entropy: avgEntropy,
combinedScore,
interpretation,
tokenCount: tokens1.size + tokens2.size,
overlapCount: intersection.size
};
// Cache the result with LRU management
this.addToCache(cacheKey, result);
return result;
}
/**
* Build a pairwise similarity matrix for multiple texts
*
* Useful for clustering and relationship discovery
*/
buildSimilarityMatrix(elements) {
const matrix = new Map();
const keys = Array.from(elements.keys());
for (let i = 0; i < keys.length; i++) {
const key1 = keys[i];
const text1 = elements.get(key1);
if (!matrix.has(key1)) {
matrix.set(key1, new Map());
}
for (let j = i + 1; j < keys.length; j++) {
const key2 = keys[j];
const text2 = elements.get(key2);
// Calculate similarity
const similarity = this.scoreRelevance(text1, text2);
// Store bidirectionally
matrix.get(key1).set(key2, similarity);
if (!matrix.has(key2)) {
matrix.set(key2, new Map());
}
matrix.get(key2).set(key1, similarity);
}
}
return matrix;
}
/**
* Find most similar elements to a given text
*/
findSimilar(targetText, candidates, topK = 5) {
const scores = [];
for (const [name, text] of candidates.entries()) {
const score = this.scoreRelevance(targetText, text);
scores.push({ name, score });
}
// Sort by combined score descending
scores.sort((a, b) => b.score.combinedScore - a.score.combinedScore);
return scores.slice(0, topK);
}
/**
* Extract key terms from text based on entropy contribution
*
* Terms that contribute most to entropy are likely important
*/
extractKeyTerms(text, topK = 10) {
const tokens = Array.from(this.cleanAndTokenize(text));
if (tokens.length === 0) {
return [];
}
// Calculate token frequencies
const frequencies = new Map();
for (const token of tokens) {
frequencies.set(token, (frequencies.get(token) || 0) + 1);
}
// Calculate entropy contribution for each unique token
const totalTokens = tokens.length;
const contributions = [];
for (const [token, count] of frequencies.entries()) {
const probability = count / totalTokens;
const contribution = -probability * Math.log2(probability);
contributions.push({ token, contribution });
}
// Sort by contribution descending
contributions.sort((a, b) => b.contribution - a.contribution);
return contributions.slice(0, topK).map(c => c.token);
}
/**
* Add result to cache with LRU eviction
*/
addToCache(key, result) {
const now = Date.now();
// Check if we need to evict
if (this.cache.size >= this.config.maxCacheSize && !this.cache.has(key)) {
// Find least recently used entry
let lruKey = null;
let oldestAccess = now;
for (const [k, v] of this.cache.entries()) {
if (v.lastAccessed < oldestAccess) {
oldestAccess = v.lastAccessed;
lruKey = k;
}
}
if (lruKey) {
this.cache.delete(lruKey);
const idx = this.cacheAccessOrder.indexOf(lruKey);
if (idx > -1) {
this.cacheAccessOrder.splice(idx, 1);
}
logger.debug('Evicted LRU cache entry', { key: lruKey, cacheSize: this.cache.size });
}
}
// Add new entry
this.cache.set(key, {
result,
timestamp: now,
lastAccessed: now
});
this.updateAccessOrder(key);
}
/**
* Update access order for LRU tracking
*/
updateAccessOrder(key) {
const idx = this.cacheAccessOrder.indexOf(key);
if (idx > -1) {
this.cacheAccessOrder.splice(idx, 1);
}
this.cacheAccessOrder.push(key);
}
/**
* Clean expired cache entries
*/
cleanExpiredCache() {
const now = Date.now();
let removed = 0;
for (const [key, value] of this.cache.entries()) {
if (now - value.timestamp > this.config.cacheExpiry) {
this.cache.delete(key);
const idx = this.cacheAccessOrder.indexOf(key);
if (idx > -1) {
this.cacheAccessOrder.splice(idx, 1);
}
removed++;
}
}
if (removed > 0) {
logger.debug('Cleaned expired cache entries', { removed, remaining: this.cache.size });
}
}
/**
* Clear the cache
*/
clearCache() {
const cleared = this.cache.size;
this.cache.clear();
this.cacheAccessOrder = [];
if (cleared > 0) {
logger.debug('NLP scoring cache cleared', { entriesCleared: cleared });
}
}
/**
* Get cache statistics
*/
getCacheStats() {
let oldest = null;
for (const entry of this.cache.values()) {
if (oldest === null || entry.timestamp < oldest) {
oldest = entry.timestamp;
}
}
return {
size: this.cache.size,
oldestEntry: oldest
};
}
dispose() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = undefined;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTkxQU2NvcmluZ01hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcG9ydGZvbGlvL05MUFNjb3JpbmdNYW5hZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDRDQUE0QyxDQUFDO0FBQzlFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBMkM3RCxNQUFNLE9BQU8saUJBQWlCO0lBQ3BCLEtBQUssQ0FBa0Y7SUFDdkYsZ0JBQWdCLENBQVcsQ0FBRSw2QkFBNkI7SUFDMUQsTUFBTSxDQUFnQjtJQUN0QixnQkFBZ0IsQ0FBbUI7SUFDbkMsZUFBZSxDQUFrQjtJQUV6QyxZQUNFLE1BQStCLEVBQy9CLHFCQUF5QyxJQUFJLGtCQUFrQixFQUFFO1FBRWpFLGtDQUFrQztRQUNsQyxNQUFNLFdBQVcsR0FBRyxrQkFBa0IsQ0FBQyxTQUFTLEVBQUUsQ0FBQztRQUVuRCxJQUFJLENBQUMsTUFBTSxHQUFHO1lBQ1osY0FBYyxFQUFFLFdBQVcsQ0FBQyxHQUFHLENBQUMsY0FBYztZQUM5QyxXQUFXLEVBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsR0FBRyxFQUFFLEdBQUcsSUFBSTtZQUMzRCxZQUFZLEVBQUUsV0FBVyxDQUFDLE1BQU0sQ0FBQyxZQUFZO1lBQzdDLFlBQVksRUFBRSxXQUFXLENBQUMsR0FBRyxDQUFDLFlBQVk7WUFDMUMsaUJBQWlCLEVBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUI7WUFDcEQsR0FBRyxNQUFNO1NBQ1YsQ0FBQztRQUVGLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsRUFBRSxDQUFDO1FBQzNCLElBQUks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QUM7WUFDaEIsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxFQUFFLGNBQWMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3pFLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxhQUFhO1FBQ2xCLElBQUksTUFBTSxHQUFrQixJQUFJLENBQUM7UUFFakMsS0FBSyxNQUFNLEtBQUssSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDeEMsSUFBSSxNQUFNLEtBQUssSUFBSSxJQUFJLEtBQUssQ0FBQyxTQUFTLEdBQUcsTUFBTSxFQUFFLENBQUM7Z0JBQ2hELE1BQU0sR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDO1lBQzNCLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTztZQUNMLElBQUksRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUk7WUFDckIsV0FBVyxFQUFFLE1BQU07U0FDcEIsQ0FBQztJQUNKLENBQUM7SUFFTSxPQUFPO1FBQ1osSUFBSSxJQUFJLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDekIsYUFBYSxDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQztZQUNwQyxJQUFJLENBQUMsZUFBZSxHQUFHLFNBQVMsQ0FBQztRQUNuQyxDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBOTFAgU2NvcmluZyBNYW5hZ2VyIC0gSmFjY2FyZCBzaW1pbGFyaXR5IGFuZCBTaGFubm9uIGVudHJvcHkgZm9yIHNlbWFudGljIGFuYWx5c2lzXG4gKlxuICogSW1wbGVtZW50cyBpbnRlbGxpZ2VudCBkb2N1bWVudCBzaW1pbGFyaXR5IHNjb3JpbmcgdXNpbmc6XG4gKiAtIEphY2NhcmQgc2ltaWxhcml0eSBmb3Igdm9jYWJ1bGFyeSBvdmVybGFwXG4gKiAtIFNoYW5ub24gZW50cm9weSBmb3IgaW5mb3JtYXRpb24gZGVuc2l0eVxuICogLSBDb21iaW5lZCBzY29yaW5nIGZvciBtZWFuaW5nZnVsIHNlbWFudGljIHJlbGF0aW9uc2hpcHNcbiAqXG4gKiBLZXkgaW5zaWdodHMgZnJvbSBhbmFseXNpczpcbiAqIC0gSGlnaCBKYWNjYXJkICg+NjAlKSArIE1vZGVyYXRlIGVudHJvcHkgKDQuNS02LjApID0gU2FtZSB0ZWNobmljYWwgZG9tYWluXG4gKiAtIEhpZ2ggSmFjY2FyZCArIExvdyBlbnRyb3B5ICg8My4wKSA9IFN0b3Agd29yZCBwb2xsdXRpb24sIHN1cGVyZmljaWFsXG4gKiAtIExvdyBKYWNjYXJkICsgU2ltaWxhciBlbnRyb3B5ID0gRGlmZmVyZW50IGRvbWFpbnMsIGVxdWFsbHkgY29tcGxleFxuICpcbiAqIFBhcnQgb2YgRW5oYW5jZWQgQ2FwYWJpbGl0eSBJbmRleCAoIzEwODUpXG4gKi9cblxuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IFVuaWNvZGVWYWxpZGF0b3IgfSBmcm9tICcuLi9zZWN1cml0eS92YWxpZGF0b3JzL3VuaWNvZGVWYWxpZGF0b3IuanMnO1xuaW1wb3J0IHsgSW5kZXhDb25maWdNYW5hZ2VyIH0gZnJvbSAnLi9jb25maWcvSW5kZXhDb25maWcuanMnO1xuXG4vKipcbiAqIFNjb3JpbmcgcmVzdWx0IHdpdGggZGV0YWlsZWQgbWV0cmljc1xuICovXG5leHBvcnQgaW50ZXJmYWNlIFNjb3JpbmdSZXN1bHQge1xuICBqYWNjYXJkOiBudW1iZXI7ICAgICAgICAgICAvLyAwLjAgdG8gMS4wXG4gIGVudHJvcHk6IG51bWJlcjsgICAgICAgICAgIC8vIFR5cGljYWxseSAwIHRvIDggYml0cyBmb3IgdGV4dFxuICBjb21iaW5lZFNjb3JlOiBudW1iZXI7ICAgICAvLyAwLjAgdG8gMS4wXG4gIGludGVycHJldGF0aW9uOiBzdHJpbmc7ICAgIC8vIEh1bWFuLXJlYWRhYmxlIGV4cGxhbmF0aW9uXG4gIHRva2VuQ291bnQ6IG51bWJlcjsgICAgICAgIC8vIE51bWJlciBvZiB1bmlxdWUgdG9rZW5zXG4gIG92ZXJsYXBDb3VudDogbnVtYmVyOyAgICAgIC8vIE51bWJlciBvZiBvdmVybGFwcGluZyB0b2tlbnNcbn1cblxuLyoqXG4gKiBQYWlyd2lzZSBzaW1pbGFyaXR5IGJldHdlZW4gdHdvIGVsZW1lbnRzXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgUGFpcndpc2VTaW1pbGFyaXR5IHtcbiAgZWxlbWVudDE6IHN0cmluZztcbiAgZWxlbWVudDI6IHN0cmluZztcbiAgc2ltaWxhcml0eTogU2NvcmluZ1Jlc3VsdDtcbiAgdGltZXN0YW1wOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiBmb3Igc2NvcmluZyBhbGdvcml0aG1cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTY29yaW5nQ29uZmlnIHtcbiAgbWluVG9rZW5MZW5ndGg6IG51bWJlcjtcbiAgY2FjaGVFeHBpcnk6IG51bWJlcjtcbiAgbWF4Q2FjaGVTaXplOiBudW1iZXI7ICAvLyBNYXhpbXVtIGNhY2hlIGVudHJpZXMgZm9yIExSVSBldmljdGlvbiAgICAgIC8vIG1pbGxpc2Vjb25kc1xuICBlbnRyb3B5QmFuZHM6IHtcbiAgICBsb3c6IG51bWJlcjsgICAgICAgICAgIC8vIDwgMy4wIHR5cGljYWxseSAoaGlnaCByZXBldGl0aW9uL2NvbW1vbiB3b3JkcylcbiAgICBtb2RlcmF0ZTogbnVtYmVyOyAgICAgIC8vIDMuMCAtIDYuMCB0eXBpY2FsbHkgKGJhbGFuY2VkIHZvY2FidWxhcnkpXG4gICAgaGlnaDogbnVtYmVyOyAgICAgICAgICAvLyA+IDYuMCB0eXBpY2FsbHkgKGRpdmVyc2Ugc3BlY2lhbGl6ZWQgdGVybXMpXG4gIH07XG4gIGphY2NhcmRUaHJlc2hvbGRzOiB7XG4gICAgbG93OiBudW1iZXI7ICAgICAgICAgICAvLyA8IDAuMlxuICAgIG1vZGVyYXRlOiBudW1iZXI7ICAgICAgLy8gMC4yIC0gMC42XG4gICAgaGlnaDogbnVtYmVyOyAgICAgICAgICAvLyA+IDAuNlxuICB9O1xufVxuXG5leHBvcnQgY2xhc3MgTkxQU2NvcmluZ01hbmFnZXIge1xuICBwcml2YXRlIGNhY2hlOiBNYXA8c3RyaW5nLCB7IHJlc3VsdDogU2NvcmluZ1Jlc3VsdDsgdGltZXN0YW1wOiBudW1iZXI7IGxhc3RBY2Nlc3NlZDogbnVtYmVyIH0+O1xuICBwcml2YXRlIGNhY2hlQWNjZXNzT3JkZXI6IHN0cmluZ1tdOyAgLy8gVHJhY2sgYWNjZXNzIG9yZGVyIGZvciBMUlVcbiAgcHJpdmF0ZSBjb25maWc6IFNjb3JpbmdDb25maWc7XG4gIHByaXZhdGUgdW5pY29kZVZhbGlkYXRvcjogVW5pY29kZVZhbGlkYXRvcjtcbiAgcHJpdmF0ZSBjbGVhbnVwSW50ZXJ2YWw/OiBOb2RlSlMuVGltZW91dDtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBjb25maWc/OiBQYXJ0aWFsPFNjb3JpbmdDb25maWc+LFxuICAgIGluZGV4Q29uZmlnTWFuYWdlcjogSW5kZXhDb25maWdNYW5hZ2VyID0gbmV3IEluZGV4Q29uZmlnTWFuYWdlcigpXG4gICkge1xuICAgIC8vIEdldCBjb25maWcgZnJvbSBjZW50cmFsIG1hbmFnZXJcbiAgICBjb25zdCBpbmRleENvbmZpZyA9IGluZGV4Q29uZmlnTWFuYWdlci5nZXRDb25maWcoKTtcblxuICAgIHRoaXMuY29uZmlnID0ge1xuICAgICAgbWluVG9rZW5MZW5ndGg6IGluZGV4Q29uZmlnLm5scC5taW5Ub2tlbkxlbmd0aCxcbiAgICAgIGNhY2hlRXhwaXJ5OiBpbmRleENvbmZpZy5ubHAuY2FjaGVFeHBpcnlNaW51dGVzICogNjAgKiAxMDAwLFxuICAgICAgbWF4Q2FjaGVTaXplOiBpbmRleENvbmZpZy5tZW1vcnkubWF4Q2FjaGVTaXplLFxuICAgICAgZW50cm9weUJhbmRzOiBpbmRleENvbmZpZy5ubHAuZW50cm9weUJhbmRzLFxuICAgICAgamFjY2FyZFRocmVzaG9sZHM6IGluZGV4Q29uZmlnLm5scC5qYWNjYXJkVGhyZXNob2xkcyxcbiAgICAgIC4uLmNvbmZpZ1xuICAgIH07XG5cbiAgICB0aGlzLmNhY2hlID0gbmV3IE1hcCgpO1xuICAgIHRoaXMuY2FjaGVBY2Nlc3NPcmRlciA9IFtdO1xuICAgIHRoaXMudW5pY29kZVZhbGlkYXRvciA9IG5ldyBVbmljb2RlVmFsaWRhdG9yKCk7XG5cbiAgICAvLyBQZXJpb2RpYyBjbGVhbnVwIG9mIGV4cGlyZWQgZW50cmllc1xuICAgIHRoaXMuY2xlYW51cEludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4gdGhpcy5jbGVhbkV4cGlyZWRDYWNoZSgpLCA2MDAwMCk7IC8vIEV2ZXJ5IG1pbnV0ZVxuXG4gICAgaWYgKHR5cGVvZiB0aGlzLmNsZWFudXBJbnRlcnZhbC51bnJlZiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgdGhpcy5jbGVhbnVwSW50ZXJ2YWwudW5yZWYoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2xlYW4gYW5kIHRva2VuaXplIHRleHQgZm9yIGFuYWx5c2lzXG4gICAqIFdvcmtzIHdpdGggYW55IGxhbmd1YWdlIC0gbm8gaGFyZGNvZGVkIHN0b3Agd29yZHNcbiAgICovXG4gIHByaXZhdGUgY2xlYW5BbmRUb2tlbml6ZSh0ZXh0OiBzdHJpbmcpOiBTZXQ8c3RyaW5nPiB7XG4gICAgLy8gTm9ybWFsaXplIFVuaWNvZGUgZm9yIHNlY3VyaXR5XG4gICAgY29uc3QgdmFsaWRhdGlvbiA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHRleHQpO1xuICAgIGlmICh2YWxpZGF0aW9uLmRldGVjdGVkSXNzdWVzICYmIHZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXMubGVuZ3RoID4gMCkge1xuICAgICAgbG9nZ2VyLndhcm4oJ1VuaWNvZGUgaXNzdWVzIGluIE5MUCB0ZXh0JywgeyBpc3N1ZXM6IHZhbGlkYXRpb24uZGV0ZWN0ZWRJc3N1ZXMgfSk7XG4gICAgfVxuICAgIHRleHQgPSB2YWxpZGF0aW9uLm5vcm1hbGl6ZWRDb250ZW50O1xuXG4gICAgLy8gQ29udmVydCB0byBsb3dlcmNhc2UgYW5kIHNwbGl0IG9uIHdvcmQgYm91bmRhcmllc1xuICAgIC8vIEtlZXAgVW5pY29kZSBsZXR0ZXIgY2hhcmFjdGVycyBmb3IgbXVsdGlsaW5ndWFsIHN1cHBvcnRcbiAgICBjb25zdCB0b2tlbnMgPSB0ZXh0LnRvTG93ZXJDYXNlKClcbiAgICAgIC5yZXBsYWNlQWxsKC9bXlxccHtMfVxccHtOfVxcc18tXS9ndSwgJyAnKSAgLy8gVW5pY29kZS1hd2FyZToga2VlcCBsZXR0ZXJzLCBudW1iZXJzLCB1bmRlcnNjb3JlLCBoeXBoZW5cbiAgICAgIC5zcGxpdCgvXFxzKy8pXG4gICAgICAuZmlsdGVyKHRva2VuID0+IHRva2VuLmxlbmd0aCA+PSB0aGlzLmNvbmZpZy5taW5Ub2tlbkxlbmd0aCk7XG5cbiAgICByZXR1cm4gbmV3IFNldCh0b2tlbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBKYWNjYXJkIHNpbWlsYXJpdHkgYmV0d2VlbiB0d28gdGV4dCBzdHJpbmdzXG4gICAqXG4gICAqIEphY2NhcmQgPSB8QSDiiKkgQnwgLyB8QSDiiKogQnxcbiAgICpcbiAgICogUmV0dXJucyB2YWx1ZSBiZXR3ZWVuIDAgKG5vIG92ZXJsYXApIGFuZCAxIChpZGVudGljYWwpXG4gICAqL1xuICBwdWJsaWMgY2FsY3VsYXRlSmFjY2FyZCh0ZXh0MTogc3RyaW5nLCB0ZXh0Mjogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBjb25zdCB0b2tlbnMxID0gdGhpcy5jbGVhbkFuZFRva2VuaXplKHRleHQxKTtcbiAgICBjb25zdCB0b2tlbnMyID0gdGhpcy5jbGVhbkFuZFRva2VuaXplKHRleHQyKTtcblxuICAgIGlmICh0b2tlbnMxLnNpemUgPT09IDAgJiYgdG9rZW5zMi5zaXplID09PSAwKSB7XG4gICAgICByZXR1cm4gMS4wOyAvLyBCb3RoIGVtcHR5ID0gaWRlbnRpY2FsXG4gICAgfVxuXG4gICAgaWYgKHRva2VuczEuc2l6ZSA9PT0gMCB8fCB0b2tlbnMyLnNpemUgPT09IDApIHtcbiAgICAgIHJldHVybiAwLjA7IC8vIE9uZSBlbXB0eSA9IG5vIHNpbWlsYXJpdHlcbiAgICB9XG5cbiAgICAvLyBDYWxjdWxhdGUgaW50ZXJzZWN0aW9uXG4gICAgY29uc3QgaW50ZXJzZWN0aW9uID0gbmV3IFNldChbLi4udG9rZW5zMV0uZmlsdGVyKHRva2VuID0+IHRva2VuczIuaGFzKHRva2VuKSkpO1xuXG4gICAgLy8gQ2FsY3VsYXRlIHVuaW9uXG4gICAgY29uc3QgdW5pb24gPSBuZXcgU2V0KFsuLi50b2tlbnMxLCAuLi50b2tlbnMyXSk7XG5cbiAgICByZXR1cm4gaW50ZXJzZWN0aW9uLnNpemUgLyB1bmlvbi5zaXplO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZSBTaGFubm9uIGVudHJvcHkgZm9yIHRleHRcbiAgICpcbiAgICogSChYKSA9IC3OoyBwKHgpICogbG9nMihwKHgpKVxuICAgKlxuICAgKiBNZWFzdXJlcyBpbmZvcm1hdGlvbiBkZW5zaXR5L3ZvY2FidWxhcnkgcmljaG5lc3NcbiAgICogSGlnaGVyIGVudHJvcHkgPSBtb3JlIGRpdmVyc2Ugdm9jYWJ1bGFyeVxuICAgKi9cbiAgcHVibGljIGNhbGN1bGF0ZUVudHJvcHkodGV4dDogc3RyaW5nKTogbnVtYmVyIHtcbiAgICBjb25zdCB0b2tlbnMgPSBBcnJheS5mcm9tKHRoaXMuY2xlYW5BbmRUb2tlbml6ZSh0ZXh0KSk7XG5cbiAgICBpZiAodG9rZW5zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIDA7XG4gICAgfVxuXG4gICAgLy8gQ2FsY3VsYXRlIHRva2VuIGZyZXF1ZW5jaWVzXG4gICAgY29uc3QgZnJlcXVlbmNpZXMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpO1xuICAgIGZvciAoY29uc3QgdG9rZW4gb2YgdG9rZW5zKSB7XG4gICAgICBmcmVxdWVuY2llcy5zZXQodG9rZW4sIChmcmVxdWVuY2llcy5nZXQodG9rZW4pIHx8IDApICsgMSk7XG4gICAgfVxuXG4gICAgLy8gQ2FsY3VsYXRlIHByb2JhYmlsaXRpZXMgYW5kIGVudHJvcHlcbiAgICBsZXQgZW50cm9weSA9IDA7XG4gICAgY29uc3QgdG90YWxUb2tlbnMgPSB0b2tlbnMubGVuZ3RoO1xuXG4gICAgZm9yIChjb25zdCBjb3VudCBvZiBmcmVxdWVuY2llcy52YWx1ZXMoKSkge1xuICAgICAgY29uc3QgcHJvYmFiaWxpdHkgPSBjb3VudCAvIHRvdGFsVG9rZW5zO1xuICAgICAgaWYgKHByb2JhYmlsaXR5ID4gMCkge1xuICAgICAgICBlbnRyb3B5IC09IHByb2JhYmlsaXR5ICogTWF0aC5sb2cyKHByb2JhYmlsaXR5KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZW50cm9weTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgY29tYmluZWQgcmVsZXZhbmNlIHNjb3JlIHVzaW5nIEphY2NhcmQgYW5kIGVudHJvcHlcbiAgICpcbiAgICogSW50ZXJwcmV0cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc2ltaWxhcml0eSBhbmQgY29tcGxleGl0eVxuICAgKi9cbiAgcHVibGljIHNjb3JlUmVsZXZhbmNlKHRleHQxOiBzdHJpbmcsIHRleHQyOiBzdHJpbmcpOiBTY29yaW5nUmVzdWx0IHtcbiAgICAvLyBDaGVjayBjYWNoZSBmaXJzdFxuICAgIGNvbnN0IGNhY2hlS2V5ID0gYCR7dGV4dDEuc3Vic3RyaW5nKDAsIDUwKX06Ojoke3RleHQyLnN1YnN0cmluZygwLCA1MCl9YDtcbiAgICBjb25zdCBjYWNoZWQgPSB0aGlzLmNhY2hlLmdldChjYWNoZUtleSk7XG5cbiAgICBpZiAoY2FjaGVkICYmIERhdGUubm93KCkgLSBjYWNoZWQudGltZXN0YW1wIDwgdGhpcy5jb25maWcuY2FjaGVFeHBpcnkpIHtcbiAgICAgIC8vIFVwZGF0ZSBhY2Nlc3Mgb3JkZXIgZm9yIExSVVxuICAgICAgY2FjaGVkLmxhc3RBY2Nlc3NlZCA9IERhdGUubm93KCk7XG4gICAgICB0aGlzLnVwZGF0ZUFjY2Vzc09yZGVyKGNhY2hlS2V5KTtcbiAgICAgIHJldHVybiBjYWNoZWQucmVzdWx0O1xuICAgIH1cblxuICAgIC8vIENhbGN1bGF0ZSBtZXRyaWNzXG4gICAgY29uc3QgamFjY2FyZCA9IHRoaXMuY2FsY3VsYXRlSmFjY2FyZCh0ZXh0MSwgdGV4dDIpO1xuICAgIGNvbnN0IGVudHJvcHkxID0gdGhpcy5jYWxjdWxhdGVFbnRyb3B5KHRleHQxKTtcbiAgICBjb25zdCBlbnRyb3B5MiA9IHRoaXMuY2FsY3VsYXRlRW50cm9weSh0ZXh0Mik7XG4gICAgY29uc3QgYXZnRW50cm9weSA9IChlbnRyb3B5MSArIGVudHJvcHkyKSAvIDI7XG5cbiAgICAvLyBHZXQgdG9rZW4gc2V0cyBmb3IgYWRkaXRpb25hbCBtZXRyaWNzXG4gICAgY29uc3QgdG9rZW5zMSA9IHRoaXMuY2xlYW5BbmRUb2tlbml6ZSh0ZXh0MSk7XG4gICAgY29uc3QgdG9rZW5zMiA9IHRoaXMuY2xlYW5BbmRUb2tlbml6ZSh0ZXh0Mik7XG4gICAgY29uc3QgaW50ZXJzZWN0aW9uID0gbmV3IFNldChbLi4udG9rZW5zMV0uZmlsdGVyKHRva2VuID0+IHRva2VuczIuaGFzKHRva2VuKSkpO1xuXG4gICAgLy8gSW50ZXJwcmV0IHRoZSBjb21iaW5hdGlvblxuICAgIGxldCBjb21iaW5lZFNjb3JlOiBudW1iZXI7XG4gICAgbGV0IGludGVycHJldGF0aW9uOiBzdHJpbmc7XG5cbiAgICAvLyBIaWdoIEphY2NhcmQgKyBNb2RlcmF0ZS1IaWdoIGVudHJvcHkgPSBFeGNlbGxlbnQgbWF0Y2ggKHNhbWUgZG9tYWluKVxuICAgIGlmIChqYWNjYXJkID49IHRoaXMuY29uZmlnLmphY2NhcmRUaHJlc2hvbGRzLmhpZ2ggJiZcbiAgICAgICAgYXZnRW50cm9weSA+PSB0aGlzLmNvbmZpZy5lbnRyb3B5QmFuZHMubG93KSB7XG4gICAgICAvLyBTY2FsZSBzY29yZSBiYXNlZCBvbiBlbnRyb3B5IHF1YWxpdHlcbiAgICAgIGNvbnN0IGVudHJvcHlRdWFsaXR5ID0gTWF0aC5taW4oMS4wLCAoYXZnRW50cm9weSAtIDIuMCkgLyA0LjApOyAvLyAwIGF0IGVudHJvcHk9MiwgMSBhdCBlbnRyb3B5PTYrXG4gICAgICBjb21iaW5lZFNjb3JlID0gMC43ICsgKGphY2NhcmQgLSAwLjYpICogMC41ICsgZW50cm9weVF1YWxpdHkgKiAwLjI7XG5cbiAgICAgIGlmIChhdmdFbnRyb3B5ID49IHRoaXMuY29uZmlnLmVudHJvcHlCYW5kcy5tb2RlcmF0ZSkge1xuICAgICAgICBpbnRlcnByZXRhdGlvbiA9ICdFeGNlbGxlbnQgbWF0Y2ggLSBzYW1lIHRlY2huaWNhbCBkb21haW4gd2l0aCByaWNoIHZvY2FidWxhcnknO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaW50ZXJwcmV0YXRpb24gPSAnR29vZCBtYXRjaCAtIGhpZ2ggb3ZlcmxhcCBidXQgc2ltcGxlciB2b2NhYnVsYXJ5JztcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gSGlnaCBKYWNjYXJkICsgVmVyeSBMb3cgZW50cm9weSA9IENvbW1vbiB3b3JkIHBvbGx1dGlvblxuICAgIGVsc2UgaWYgKGphY2NhcmQgPj0gdGhpcy5jb25maWcuamFjY2FyZFRocmVzaG9sZHMuaGlnaCAmJlxuICAgICAgICAgICAgIGF2Z0VudHJvcHkgPCAyLjApIHtcbiAgICAgIGNvbWJpbmVkU2NvcmUgPSAwLjMgKyBqYWNjYXJkICogMC4yOyAvLyBQZW5hbGl6ZSBoZWF2aWx5XG4gICAgICBpbnRlcnByZXRhdGlvbiA9ICdTdXBlcmZpY2lhbCBzaW1pbGFyaXR5IC0gbW9zdGx5IGNvbW1vbiB3b3Jkcyc7XG4gICAgfVxuICAgIC8vIE1vZGVyYXRlIEphY2NhcmQgKyBHb29kIGVudHJvcHkgPSBSZWxhdGVkIGNvbmNlcHRzXG4gICAgZWxzZSBpZiAoamFjY2FyZCA+PSB0aGlzLmNvbmZpZy5qYWNjYXJkVGhyZXNob2xkcy5tb2RlcmF0ZSAmJlxuICAgICAgICAgICAgIGF2Z0VudHJvcHkgPj0gdGhpcy5jb25maWcuZW50cm9weUJhbmRzLmxvdykge1xuICAgICAgY29tYmluZWRTY29yZSA9IDAuNCArIGphY2NhcmQgKiAwLjQgKyAoYXZnRW50cm9weSAvIDEwKSAqIDAuMjtcbiAgICAgIGludGVycHJldGF0aW9uID0gJ01vZGVyYXRlIG1hdGNoIC0gcmVsYXRlZCBjb25jZXB0cyB3aXRoIGdvb2QgY29tcGxleGl0eSc7XG4gICAgfVxuICAgIC8vIExvdyBKYWNjYXJkICsgU2ltaWxhciBlbnRyb3B5ID0gRGlmZmVyZW50IGRvbWFpbnNcbiAgICBlbHNlIGlmIChqYWNjYXJkIDwgdGhpcy5jb25maWcuamFjY2FyZFRocmVzaG9sZHMubG93ICYmXG4gICAgICAgICAgICAgTWF0aC5hYnMoZW50cm9weTEgLSBlbnRyb3B5MikgPCAxLjApIHtcbiAgICAgIGNvbWJpbmVkU2NvcmUgPSBqYWNjYXJkICogMC41O1xuICAgICAgaW50ZXJwcmV0YXRpb24gPSAnRGlmZmVyZW50IGRvbWFpbnMgd2l0aCBzaW1pbGFyIGNvbXBsZXhpdHknO1xuICAgIH1cbiAgICAvLyBMb3cgSmFjY2FyZCArIERpZmZlcmVudCBlbnRyb3B5ID0gVW5yZWxhdGVkXG4gICAgZWxzZSB7XG4gICAgICBjb21iaW5lZFNjb3JlID0gamFjY2FyZCAqIDAuMztcbiAgICAgIGludGVycHJldGF0aW9uID0gJ0xvdyByZWxldmFuY2UgLSBkaWZmZXJlbnQgdG9waWNzIGFuZCBjb21wbGV4aXR5JztcbiAgICB9XG5cbiAgICAvLyBFbnN1cmUgc2NvcmUgaXMgYmV0d2VlbiAwIGFuZCAxXG4gICAgY29tYmluZWRTY29yZSA9IE1hdGgubWF4KDAsIE1hdGgubWluKDEsIGNvbWJpbmVkU2NvcmUpKTtcblxuICAgIGNvbnN0IHJlc3VsdDogU2NvcmluZ1Jlc3VsdCA9IHtcbiAgICAgIGphY2NhcmQsXG4gICAgICBlbnRyb3B5OiBhdmdFbnRyb3B5LFxuICAgICAgY29tYmluZWRTY29yZSxcbiAgICAgIGludGVycHJldGF0aW9uLFxuICAgICAgdG9rZW5Db3VudDogdG9rZW5zMS5zaXplICsgdG9rZW5zMi5zaXplLFxuICAgICAgb3ZlcmxhcENvdW50OiBpbnRlcnNlY3Rpb24uc2l6ZVxuICAgIH07XG5cbiAgICAvLyBDYWNoZSB0aGUgcmVzdWx0IHdpdGggTFJVIG1hbmFnZW1lbnRcbiAgICB0aGlzLmFkZFRvQ2FjaGUoY2FjaGVLZXksIHJlc3VsdCk7XG5cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkIGEgcGFpcndpc2Ugc2ltaWxhcml0eSBtYXRyaXggZm9yIG11bHRpcGxlIHRleHRzXG4gICAqXG4gICAqIFVzZWZ1bCBmb3IgY2x1c3RlcmluZyBhbmQgcmVsYXRpb25zaGlwIGRpc2NvdmVyeVxuICAgKi9cbiAgcHVibGljIGJ1aWxkU2ltaWxhcml0eU1hdHJpeChcbiAgICBlbGVtZW50czogTWFwPHN0cmluZywgc3RyaW5nPlxuICApOiBNYXA8c3RyaW5nLCBNYXA8c3RyaW5nLCBTY29yaW5nUmVzdWx0Pj4ge1xuICAgIGNvbnN0IG1hdHJpeCA9IG5ldyBNYXA8c3RyaW5nLCBNYXA8c3RyaW5nLCBTY29yaW5nUmVzdWx0Pj4oKTtcbiAgICBjb25zdCBrZXlzID0gQXJyYXkuZnJvbShlbGVtZW50cy5rZXlzKCkpO1xuXG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBrZXlzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBrZXkxID0ga2V5c1tpXTtcbiAgICAgIGNvbnN0IHRleHQxID0gZWxlbWVudHMuZ2V0KGtleTEpITtcblxuICAgICAgaWYgKCFtYXRyaXguaGFzKGtleTEpKSB7XG4gICAgICAgIG1hdHJpeC5zZXQoa2V5MSwgbmV3IE1hcCgpKTtcbiAgICAgIH1cblxuICAgICAgZm9yIChsZXQgaiA9IGkgKyAxOyBqIDwga2V5cy5sZW5ndGg7IGorKykge1xuICAgICAgICBjb25zdCBrZXkyID0ga2V5c1tqXTtcbiAgICAgICAgY29uc3QgdGV4dDIgPSBlbGVtZW50cy5nZXQoa2V5MikhO1xuXG4gICAgICAgIC8vIENhbGN1bGF0ZSBzaW1pbGFyaXR5XG4gICAgICAgIGNvbnN0IHNpbWlsYXJpdHkgPSB0aGlzLnNjb3JlUmVsZXZhbmNlKHRleHQxLCB0ZXh0Mik7XG5cbiAgICAgICAgLy8gU3RvcmUgYmlkaXJlY3Rpb25hbGx5XG4gICAgICAgIG1hdHJpeC5nZXQoa2V5MSkhLnNldChrZXkyLCBzaW1pbGFyaXR5KTtcblxuICAgICAgICBpZiAoIW1hdHJpeC5oYXMoa2V5MikpIHtcbiAgICAgICAgICBtYXRyaXguc2V0KGtleTIsIG5ldyBNYXAoKSk7XG4gICAgICAgIH1cbiAgICAgICAgbWF0cml4LmdldChrZXkyKSEuc2V0KGtleTEsIHNpbWlsYXJpdHkpO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBtYXRyaXg7XG4gIH1cblxuICAvKipcbiAgICogRmluZCBtb3N0IHNpbWlsYXIgZWxlbWVudHMgdG8gYSBnaXZlbiB0ZXh0XG4gICAqL1xuICBwdWJsaWMgZmluZFNpbWlsYXIoXG4gICAgdGFyZ2V0VGV4dDogc3RyaW5nLFxuICAgIGNhbmRpZGF0ZXM6IE1hcDxzdHJpbmcsIHN0cmluZz4sXG4gICAgdG9wSzogbnVtYmVyID0gNVxuICApOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgc2NvcmU6IFNjb3JpbmdSZXN1bHQgfT4ge1xuICAgIGNvbnN0IHNjb3JlczogQXJyYXk8eyBuYW1lOiBzdHJpbmc7IHNjb3JlOiBTY29yaW5nUmVzdWx0IH0+ID0gW107XG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCB0ZXh0XSBvZiBjYW5kaWRhdGVzLmVudHJpZXMoKSkge1xuICAgICAgY29uc3Qgc2NvcmUgPSB0aGlzLnNjb3JlUmVsZXZhbmNlKHRhcmdldFRleHQsIHRleHQpO1xuICAgICAgc2NvcmVzLnB1c2goeyBuYW1lLCBzY29yZSB9KTtcbiAgICB9XG5cbiAgICAvLyBTb3J0IGJ5IGNvbWJpbmVkIHNjb3JlIGRlc2NlbmRpbmdcbiAgICBzY29yZXMuc29ydCgoYSwgYikgPT4gYi5zY29yZS5jb21iaW5lZFNjb3JlIC0gYS5zY29yZS5jb21iaW5lZFNjb3JlKTtcblxuICAgIHJldHVybiBzY29yZXMuc2xpY2UoMCwgdG9wSyk7XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdCBrZXkgdGVybXMgZnJvbSB0ZXh0IGJhc2VkIG9uIGVudHJvcHkgY29udHJpYnV0aW9uXG4gICAqXG4gICAqIFRlcm1zIHRoYXQgY29udHJpYnV0ZSBtb3N0IHRvIGVudHJvcHkgYXJlIGxpa2VseSBpbXBvcnRhbnRcbiAgICovXG4gIHB1YmxpYyBleHRyYWN0S2V5VGVybXModGV4dDogc3RyaW5nLCB0b3BLOiBudW1iZXIgPSAxMCk6IHN0cmluZ1tdIHtcbiAgICBjb25zdCB0b2tlbnMgPSBBcnJheS5mcm9tKHRoaXMuY2xlYW5BbmRUb2tlbml6ZSh0ZXh0KSk7XG5cbiAgICBpZiAodG9rZW5zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cblxuICAgIC8vIENhbGN1bGF0ZSB0b2tlbiBmcmVxdWVuY2llc1xuICAgIGNvbnN0IGZyZXF1ZW5jaWVzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgICBmb3IgKGNvbnN0IHRva2VuIG9mIHRva2Vucykge1xuICAgICAgZnJlcXVlbmNpZXMuc2V0KHRva2VuLCAoZnJlcXVlbmNpZXMuZ2V0KHRva2VuKSB8fCAwKSArIDEpO1xuICAgIH1cblxuICAgIC8vIENhbGN1bGF0ZSBlbnRyb3B5IGNvbnRyaWJ1dGlvbiBmb3IgZWFjaCB1bmlxdWUgdG9rZW5cbiAgICBjb25zdCB0b3RhbFRva2VucyA9IHRva2Vucy5sZW5ndGg7XG4gICAgY29uc3QgY29udHJpYnV0aW9uczogQXJyYXk8eyB0b2tlbjogc3RyaW5nOyBjb250cmlidXRpb246IG51bWJlciB9PiA9IFtdO1xuXG4gICAgZm9yIChjb25zdCBbdG9rZW4sIGNvdW50XSBvZiBmcmVxdWVuY2llcy5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IHByb2JhYmlsaXR5ID0gY291bnQgLyB0b3RhbFRva2VucztcbiAgICAgIGNvbnN0IGNvbnRyaWJ1dGlvbiA9IC1wcm9iYWJpbGl0eSAqIE1hdGgubG9nMihwcm9iYWJpbGl0eSk7XG4gICAgICBjb250cmlidXRpb25zLnB1c2goeyB0b2tlbiwgY29udHJpYnV0aW9uIH0pO1xuICAgIH1cblxuICAgIC8vIFNvcnQgYnkgY29udHJpYnV0aW9uIGRlc2NlbmRpbmdcbiAgICBjb250cmlidXRpb25zLnNvcnQoKGEsIGIpID0+IGIuY29udHJpYnV0aW9uIC0gYS5jb250cmlidXRpb24pO1xuXG4gICAgcmV0dXJuIGNvbnRyaWJ1dGlvbnMuc2xpY2UoMCwgdG9wSykubWFwKGMgPT4gYy50b2tlbik7XG4gIH1cblxuICAvKipcbiAgICogQWRkIHJlc3VsdCB0byBjYWNoZSB3aXRoIExSVSBldmljdGlvblxuICAgKi9cbiAgcHJpdmF0ZSBhZGRUb0NhY2hlKGtleTogc3RyaW5nLCByZXN1bHQ6IFNjb3JpbmdSZXN1bHQpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuXG4gICAgLy8gQ2hlY2sgaWYgd2UgbmVlZCB0byBldmljdFxuICAgIGlmICh0aGlzLmNhY2hlLnNpemUgPj0gdGhpcy5jb25maWcubWF4Q2FjaGVTaXplICYmICF0aGlzLmNhY2hlLmhhcyhrZXkpKSB7XG4gICAgICAvLyBGaW5kIGxlYXN0IHJlY2VudGx5IHVzZWQgZW50cnlcbiAgICAgIGxldCBscnVLZXk6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuICAgICAgbGV0IG9sZGVzdEFjY2VzcyA9IG5vdztcblxuICAgICAgZm9yIChjb25zdCBbaywgdl0gb2YgdGhpcy5jYWNoZS5lbnRyaWVzKCkpIHtcbiAgICAgICAgaWYgKHYubGFzdEFjY2Vzc2VkIDwgb2xkZXN0QWNjZXNzKSB7XG4gICAgICAgICAgb2xkZXN0QWNjZXNzID0gdi5sYXN0QWNjZXNzZWQ7XG4gICAgICAgICAgbHJ1S2V5ID0gaztcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAobHJ1S2V5KSB7XG4gICAgICAgIHRoaXMuY2FjaGUuZGVsZXRlKGxydUtleSk7XG4gICAgICAgIGNvbnN0IGlkeCA9IHRoaXMuY2FjaGVBY2Nlc3NPcmRlci5pbmRleE9mKGxydUtleSk7XG4gICAgICAgIGlmIChpZHggPiAtMSkge1xuICAgICAgICAgIHRoaXMuY2FjaGVBY2Nlc3NPcmRlci5zcGxpY2UoaWR4LCAxKTtcbiAgICAgICAgfVxuICAgICAgICBsb2dnZXIuZGVidWcoJ0V2aWN0ZWQgTFJVIGNhY2hlIGVudHJ5JywgeyBrZXk6IGxydUtleSwgY2FjaGVTaXplOiB0aGlzLmNhY2hlLnNpemUgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQWRkIG5ldyBlbnRyeVxuICAgIHRoaXMuY2FjaGUuc2V0KGtleSwge1xuICAgICAgcmVzdWx0LFxuICAgICAgdGltZXN0YW1wOiBub3csXG4gICAgICBsYXN0QWNjZXNzZWQ6IG5vd1xuICAgIH0pO1xuICAgIHRoaXMudXBkYXRlQWNjZXNzT3JkZXIoa2V5KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGUgYWNjZXNzIG9yZGVyIGZvciBMUlUgdHJhY2tpbmdcbiAgICovXG4gIHByaXZhdGUgdXBkYXRlQWNjZXNzT3JkZXIoa2V5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBpZHggPSB0aGlzLmNhY2hlQWNjZXNzT3JkZXIuaW5kZXhPZihrZXkpO1xuICAgIGlmIChpZHggPiAtMSkge1xuICAgICAgdGhpcy5jYWNoZUFjY2Vzc09yZGVyLnNwbGljZShpZHgsIDEpO1xuICAgIH1cbiAgICB0aGlzLmNhY2hlQWNjZXNzT3JkZXIucHVzaChrZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFuIGV4cGlyZWQgY2FjaGUgZW50cmllc1xuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbkV4cGlyZWRDYWNoZSgpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIGxldCByZW1vdmVkID0gMDtcblxuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIHRoaXMuY2FjaGUuZW50cmllcygpKSB7XG4gICAgICBpZiAobm93IC0gdmFsdWUudGltZXN0YW1wID4gdGhpcy5jb25maWcuY2FjaGVFeHBpcnkpIHtcbiAgICAgICAgdGhpcy5jYWNoZS5kZWxldGUoa2V5KTtcbiAgICAgICAgY29uc3QgaWR4ID0gdGhpcy5jYWNoZUFjY2Vzc09yZGVyLmluZGV4T2Yoa2V5KTtcbiAgICAgICAgaWYgKGlkeCA+IC0xKSB7XG4gICAgICAgICAgdGhpcy5jYWNoZUFjY2Vzc09yZGVyLnNwbGljZShpZHgsIDEpO1xuICAgICAgICB9XG4gICAgICAgIHJlbW92ZWQrKztcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAocmVtb3ZlZCA+IDApIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnQ2xlYW5lZCBleHBpcmVkIGNhY2hlIGVudHJpZXMnLCB7IHJlbW92ZWQsIHJlbWFpbmluZzogdGhpcy5jYWNoZS5zaXplIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgY2FjaGVcbiAgICovXG4gIHB1YmxpYyBjbGVhckNhY2hlKCk6IHZvaWQge1xuICAgIGNvbnN0IGNsZWFyZWQgPSB0aGlzLmNhY2hlLnNpemU7XG4gICAgdGhpcy5jYWNoZS5jbGVhcigpO1xuICAgIHRoaXMuY2FjaGVBY2Nlc3NPcmRlciA9IFtdO1xuICAgIGlmIChjbGVhcmVkID4gMCkge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdOTFAgc2NvcmluZyBjYWNoZSBjbGVhcmVkJywgeyBlbnRyaWVzQ2xlYXJlZDogY2xlYXJlZCB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGNhY2hlIHN0YXRpc3RpY3NcbiAgICovXG4gIHB1YmxpYyBnZXRDYWNoZVN0YXRzKCk6IHsgc2l6ZTogbnVtYmVyOyBvbGRlc3RFbnRyeTogbnVtYmVyIHwgbnVsbCB9IHtcbiAgICBsZXQgb2xkZXN0OiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5jYWNoZS52YWx1ZXMoKSkge1xuICAgICAgaWYgKG9sZGVzdCA9PT0gbnVsbCB8fCBlbnRyeS50aW1lc3RhbXAgPCBvbGRlc3QpIHtcbiAgICAgICAgb2xkZXN0ID0gZW50cnkudGltZXN0YW1wO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBzaXplOiB0aGlzLmNhY2hlLnNpemUsXG4gICAgICBvbGRlc3RFbnRyeTogb2xkZXN0XG4gICAgfTtcbiAgfVxuXG4gIHB1YmxpYyBkaXNwb3NlKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNsZWFudXBJbnRlcnZhbCkge1xuICAgICAgY2xlYXJJbnRlcnZhbCh0aGlzLmNsZWFudXBJbnRlcnZhbCk7XG4gICAgICB0aGlzLmNsZWFudXBJbnRlcnZhbCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cbn1cbiJdfQ==