@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.
352 lines • 49.8 kB
JavaScript
/**
* MemoryStorageLayer - IStorageLayer implementation for multi-directory memory scanning.
*
* Memories use pure YAML across a multi-directory layout:
* system/ — System-provided memories
* adapters/ — Adapter-specific memories
* YYYY-MM-DD/ — User-created date-folder memories
* (root) — Legacy root-level memories
*
* Reuses the same internal components as ElementStorageLayer (MetadataIndex,
* StorageManifest, FileStorageBackend) but with fundamentally different scan
* logic: multi-directory enumeration + _index.json cold-start persistence.
*/
import * as path from 'path';
import { logger } from '../utils/logger.js';
import { StorageManifest } from './StorageManifest.js';
import { MetadataIndex } from './MetadataIndex.js';
import { FileStorageBackend } from './FileStorageBackend.js';
import { MemoryMetadataExtractor } from './MemoryMetadataExtractor.js';
import { MemoryIndexFile } from './MemoryIndexFile.js';
import { UnicodeValidator } from '../security/validators/unicodeValidator.js';
const EMPTY_DIFF = { added: [], modified: [], removed: [], unchanged: [] };
const DATE_FOLDER_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
export class MemoryStorageLayer {
fileOps;
backend;
manifest = new StorageManifest();
index = new MetadataIndex();
indexFile;
memoriesDir;
scanCooldownMs;
fileFilter;
lastScanTimestamp = 0;
scanInProgress = null;
coldStartDone = false;
constructor(fileOps, options) {
this.fileOps = fileOps;
this.memoriesDir = options.memoriesDir;
this.scanCooldownMs = options.scanCooldownMs ?? 1000;
this.fileFilter = options.fileFilter;
if (options.storageBackend) {
this.backend = options.storageBackend;
}
else {
this.backend = new FileStorageBackend(fileOps);
}
const indexPath = path.join(this.memoriesDir, '_index.json');
this.indexFile = new MemoryIndexFile(indexPath, fileOps, {
debounceMs: options.indexDebounceMs ?? 2000,
});
}
// ---- IStorageLayer implementation ----
async scan() {
// Cold start: try loading from _index.json first
if (!this.coldStartDone) {
this.coldStartDone = true;
return this.coldStart();
}
const now = Date.now();
if (now - this.lastScanTimestamp < this.scanCooldownMs) {
logger.debug(`MemoryStorageLayer.scan: COOLDOWN ACTIVE — skipping disk I/O (${this.scanCooldownMs - (now - this.lastScanTimestamp)}ms remaining)`);
return EMPTY_DIFF;
}
// Deduplicate concurrent scans
if (this.scanInProgress) {
return this.scanInProgress;
}
this.scanInProgress = this.performScan();
try {
return await this.scanInProgress;
}
finally {
this.scanInProgress = null;
}
}
async listSummaries() {
await this.scan();
return this.deduplicateByName(this.index.getAll());
}
async getIndexedPaths() {
await this.scan();
const deduplicated = this.deduplicateByName(this.index.getAll());
return deduplicated.map(entry => entry.filePath);
}
/**
* Deduplicate index entries by normalized name.
* When the same memory appears in multiple date folders, keep the one
* with the highest mtimeMs (most recently modified).
*
* Issue #654: Same memory files duplicated across ~150 date folders
* inflated counts from ~750 unique to ~3,100+ loaded memories.
*/
deduplicateByName(entries) {
const byName = new Map();
for (const entry of entries) {
const key = UnicodeValidator.normalize(entry.name).normalizedContent.toLowerCase();
const existing = byName.get(key);
if (!existing || entry.mtimeMs > existing.mtimeMs) {
byName.set(key, entry);
}
}
return [...byName.values()];
}
getPathByName(name) {
const normalizedName = UnicodeValidator.normalize(name).normalizedContent;
return this.index.getPathByName(normalizedName);
}
hasCompletedScan() {
return this.lastScanTimestamp > 0;
}
async notifySaved(relativePath, absolutePath) {
try {
const content = await this.backend.readFile(absolutePath);
const meta = await this.backend.stat(absolutePath);
const extracted = MemoryMetadataExtractor.extractMetadata(content, relativePath);
const entry = {
filePath: relativePath,
name: extracted.name ?? 'unnamed',
description: extracted.description ?? '',
version: extracted.version ?? '1.0.0',
author: extracted.author ?? '',
tags: extracted.tags ?? [],
mtimeMs: meta.mtimeMs,
sizeBytes: meta.sizeBytes,
autoLoad: extracted.autoLoad,
priority: extracted.priority,
memoryType: extracted.memoryType,
totalEntries: extracted.totalEntries,
};
this.index.set(entry);
this.manifest.set(relativePath, meta.mtimeMs);
this.indexFile.scheduleWrite(this.index.getAll());
}
catch (error) {
logger.debug('MemoryStorageLayer.notifySaved failed, invalidating', {
relativePath,
error: error instanceof Error ? error.message : String(error),
});
this.invalidate();
}
}
notifyDeleted(relativePath) {
this.index.remove(relativePath);
this.manifest.remove(relativePath);
this.indexFile.scheduleWrite(this.index.getAll());
}
invalidate() {
this.lastScanTimestamp = 0;
}
clear() {
this.indexFile.dispose(); // Cancel pending debounced write (prevents ENOTEMPTY in tests)
this.index.clear();
this.manifest.clear();
this.lastScanTimestamp = 0;
}
// ---- Memory-specific methods ----
/**
* Return index entries where autoLoad === true, sorted by priority ascending.
* Pure in-memory — does NOT trigger a scan.
*/
getAutoLoadEntries() {
return this.index.getAll()
.filter(entry => entry.autoLoad === true)
.sort((a, b) => (a.priority ?? 999) - (b.priority ?? 999));
}
/**
* Full disk scan of all subdirectories + write _index.json.
*/
async rebuildIndex() {
logger.info('MemoryStorageLayer: rebuilding index from disk...');
this.index.clear();
this.manifest.clear();
await this.performScan();
await this.indexFile.write(this.index.getAll());
logger.info(`MemoryStorageLayer: rebuild complete — ${this.index.size} entries indexed`);
}
/**
* Flush pending _index.json write and release resources.
*/
async dispose() {
await this.indexFile.flush();
this.indexFile.dispose();
}
// ---- Private ----
/**
* Cold start: try to restore from _index.json, then run incremental scan.
* Falls back to full rebuild if index is missing/corrupt.
*/
async coldStart() {
try {
const cached = await this.indexFile.read();
if (cached) {
// Populate index and manifest from cached data
for (const [relPath, entry] of Object.entries(cached.entries)) {
this.index.set(entry);
this.manifest.set(relPath, entry.mtimeMs);
}
logger.info(`MemoryStorageLayer: cold start — loaded ${Object.keys(cached.entries).length} entries from _index.json`);
// Run incremental scan to catch changes since _index.json was written
return this.performScan();
}
}
catch (error) {
logger.debug('MemoryStorageLayer: cold start _index.json read failed, rebuilding', {
error: error instanceof Error ? error.message : String(error),
});
}
// No valid _index.json — full rebuild
await this.rebuildIndex();
return EMPTY_DIFF;
}
/**
* Discover all subdirectories to scan.
* Returns ['system', 'adapters', ...dateFolders, ''] where '' = root.
*/
async discoverSubdirectories() {
const subdirs = [];
try {
const entries = await this.fileOps.listDirectory(this.memoriesDir);
const dateFolders = [];
for (const entry of entries) {
// Include known fixed directories
if (entry === 'system' || entry === 'adapters') {
subdirs.push(entry);
}
else if (DATE_FOLDER_PATTERN.test(entry)) {
dateFolders.push(entry);
}
// Skip backups/, _index.json, and other non-memory directories
}
// Sort date folders chronologically (oldest first → newest last)
// so that nameToPath in MetadataIndex ends up pointing to the newest copy
dateFolders.sort((a, b) => a.localeCompare(b));
subdirs.push(...dateFolders);
}
catch (error) {
if (error.code !== 'ENOENT') {
logger.debug('MemoryStorageLayer: failed to list subdirectories', {
error: error instanceof Error ? error.message : String(error),
});
}
}
// Always include root (empty string = root directory)
subdirs.push('');
return subdirs;
}
/**
* Perform a full incremental scan across all subdirectories.
*/
async performScan() {
try {
const exists = await this.backend.directoryExists(this.memoriesDir);
if (!exists) {
const removedPaths = this.index.getPaths();
this.index.clear();
this.manifest.clear();
this.lastScanTimestamp = Date.now();
return {
added: [],
modified: [],
removed: removedPaths,
unchanged: [],
};
}
// 1. Discover subdirectories
const subdirs = await this.discoverSubdirectories();
// 2. Enumerate all .yaml files across subdirectories
const allRelativePaths = [];
for (const subdir of subdirs) {
const absDir = subdir ? path.join(this.memoriesDir, subdir) : this.memoriesDir;
try {
const files = await this.backend.listFiles(absDir, '.yaml');
for (const file of files) {
// Apply file filter (e.g., exclude backup files)
if (this.fileFilter && !this.fileFilter(file)) {
continue;
}
// Prefix with subdir for relative path
const relPath = subdir ? `${subdir}/${file}` : file;
allRelativePaths.push(relPath);
}
}
catch (error) {
// Directory might not exist (e.g., no system/ folder yet)
if (error.code !== 'ENOENT') {
logger.debug(`MemoryStorageLayer: failed to list ${absDir}`, {
error: error instanceof Error ? error.message : String(error),
});
}
}
}
// 3. Stat all files
const stats = await this.backend.statMany(this.memoriesDir, allRelativePaths);
// 4. Diff against manifest
const diff = this.manifest.diff(stats);
const hasChanges = diff.added.length > 0 || diff.modified.length > 0 || diff.removed.length > 0;
if (hasChanges) {
logger.debug(`MemoryStorageLayer.scan: DISK SCAN — ${allRelativePaths.length} files, ${diff.added.length} added, ${diff.modified.length} modified, ${diff.removed.length} removed`);
}
// 5. For added/modified: read file, extract metadata, update index
const toIndex = [...diff.added, ...diff.modified];
let indexUpdated = false;
await Promise.all(toIndex.map(async (relPath) => {
try {
const absPath = path.join(this.memoriesDir, relPath);
const content = await this.backend.readFile(absPath);
const extracted = MemoryMetadataExtractor.extractMetadata(content, relPath);
const meta = stats.get(relPath);
this.index.set({
filePath: relPath,
name: extracted.name ?? 'unnamed',
description: extracted.description ?? '',
version: extracted.version ?? '1.0.0',
author: extracted.author ?? '',
tags: extracted.tags ?? [],
mtimeMs: meta?.mtimeMs ?? 0,
sizeBytes: meta?.sizeBytes ?? 0,
autoLoad: extracted.autoLoad,
priority: extracted.priority,
memoryType: extracted.memoryType,
totalEntries: extracted.totalEntries,
});
indexUpdated = true;
}
catch (error) {
logger.debug(`MemoryStorageLayer: failed to index ${relPath}`, {
error: error instanceof Error ? error.message : String(error),
});
}
}));
// 6. For removed: remove from index
for (const relPath of diff.removed) {
this.index.remove(relPath);
indexUpdated = true;
}
// 7. Update manifest and timestamp
this.manifest.update(stats);
this.lastScanTimestamp = Date.now();
// 8. Schedule debounced _index.json write if index changed
if (indexUpdated) {
this.indexFile.scheduleWrite(this.index.getAll());
}
return diff;
}
catch (error) {
logger.error('MemoryStorageLayer.performScan failed', error);
this.lastScanTimestamp = Date.now(); // Prevent retry storms
return EMPTY_DIFF;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVtb3J5U3RvcmFnZUxheWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3N0b3JhZ2UvTWVtb3J5U3RvcmFnZUxheWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7R0FZRztBQUVILE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUk1QyxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdELE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ3ZFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUV2RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQWU5RSxNQUFNLFVBQVUsR0FBdUIsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFDL0YsTUFBTSxtQkFBbUIsR0FBRyxxQkFBcUIsQ0FBQztBQUVsRCxNQUFNLE9BQU8sa0JBQWtCO0lBY1Y7SUFiRixPQUFPLENBQWtCO0lBQ3pCLFFBQVEsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO0lBQ2pDLEtBQUssR0FBRyxJQUFJLGFBQWEsRUFBRSxDQUFDO0lBQzVCLFNBQVMsQ0FBa0I7SUFDM0IsV0FBVyxDQUFTO0lBQ3BCLGNBQWMsQ0FBUztJQUN2QixVQUFVLENBQWlDO0lBRXBELGlCQUFpQixHQUFHLENBQUMsQ0FBQztJQUN0QixjQUFjLEdBQXVDLElBQUksQ0FBQztJQUMxRCxhQUFhLEdBQUcs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QUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUMzQixZQUFZLEdBQUcsSUFBSSxDQUFDO1lBQ3RCLENBQUM7WUFFRCxtQ0FBbUM7WUFDbkMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDNUIsSUFBSSxDQUFDLGlCQUFpQixHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUVwQywyREFBMkQ7WUFDM0QsSUFBSSxZQUFZLEVBQUUsQ0FBQztnQkFDakIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQ3BELENBQUM7WUFFRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyx1Q0FBdUMsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM3RCxJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsdUJBQXVCO1lBQzVELE9BQU8sVUFBVSxDQUFDO1FBQ3BCLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIE1lbW9yeVN0b3JhZ2VMYXllciAtIElTdG9yYWdlTGF5ZXIgaW1wbGVtZW50YXRpb24gZm9yIG11bHRpLWRpcmVjdG9yeSBtZW1vcnkgc2Nhbm5pbmcuXG4gKlxuICogTWVtb3JpZXMgdXNlIHB1cmUgWUFNTCBhY3Jvc3MgYSBtdWx0aS1kaXJlY3RvcnkgbGF5b3V0OlxuICogICBzeXN0ZW0vICAgICAgIOKAlCBTeXN0ZW0tcHJvdmlkZWQgbWVtb3JpZXNcbiAqICAgYWRhcHRlcnMvICAgICDigJQgQWRhcHRlci1zcGVjaWZpYyBtZW1vcmllc1xuICogICBZWVlZLU1NLURELyAgIOKAlCBVc2VyLWNyZWF0ZWQgZGF0ZS1mb2xkZXIgbWVtb3JpZXNcbiAqICAgKHJvb3QpICAgICAgICDigJQgTGVnYWN5IHJvb3QtbGV2ZWwgbWVtb3JpZXNcbiAqXG4gKiBSZXVzZXMgdGhlIHNhbWUgaW50ZXJuYWwgY29tcG9uZW50cyBhcyBFbGVtZW50U3RvcmFnZUxheWVyIChNZXRhZGF0YUluZGV4LFxuICogU3RvcmFnZU1hbmlmZXN0LCBGaWxlU3RvcmFnZUJhY2tlbmQpIGJ1dCB3aXRoIGZ1bmRhbWVudGFsbHkgZGlmZmVyZW50IHNjYW5cbiAqIGxvZ2ljOiBtdWx0aS1kaXJlY3RvcnkgZW51bWVyYXRpb24gKyBfaW5kZXguanNvbiBjb2xkLXN0YXJ0IHBlcnNpc3RlbmNlLlxuICovXG5cbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuaW1wb3J0IHR5cGUgeyBJU3RvcmFnZUxheWVyIH0gZnJvbSAnLi9JU3RvcmFnZUxheWVyLmpzJztcbmltcG9ydCB0eXBlIHsgSVN0b3JhZ2VCYWNrZW5kIH0gZnJvbSAnLi9JU3RvcmFnZUJhY2tlbmQuanMnO1xuaW1wb3J0IHR5cGUgeyBFbGVtZW50SW5kZXhFbnRyeSwgTWFuaWZlc3REaWZmUmVzdWx0IH0gZnJvbSAnLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBTdG9yYWdlTWFuaWZlc3QgfSBmcm9tICcuL1N0b3JhZ2VNYW5pZmVzdC5qcyc7XG5pbXBvcnQgeyBNZXRhZGF0YUluZGV4IH0gZnJvbSAnLi9NZXRhZGF0YUluZGV4LmpzJztcbmltcG9ydCB7IEZpbGVTdG9yYWdlQmFja2VuZCB9IGZyb20gJy4vRmlsZVN0b3JhZ2VCYWNrZW5kLmpzJztcbmltcG9ydCB7IE1lbW9yeU1ldGFkYXRhRXh0cmFjdG9yIH0gZnJvbSAnLi9NZW1vcnlNZXRhZGF0YUV4dHJhY3Rvci5qcyc7XG5pbXBvcnQgeyBNZW1vcnlJbmRleEZpbGUgfSBmcm9tICcuL01lbW9yeUluZGV4RmlsZS5qcyc7XG5pbXBvcnQgdHlwZSB7IEZpbGVPcGVyYXRpb25zU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL0ZpbGVPcGVyYXRpb25zU2VydmljZS5qcyc7XG5pbXBvcnQgeyBVbmljb2RlVmFsaWRhdG9yIH0gZnJvbSAnLi4vc2VjdXJpdHkvdmFsaWRhdG9ycy91bmljb2RlVmFsaWRhdG9yLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBNZW1vcnlTdG9yYWdlTGF5ZXJPcHRpb25zIHtcbiAgLyoqIEFic29sdXRlIHBhdGggdG8gdGhlIG1lbW9yaWVzIGRpcmVjdG9yeSAqL1xuICBtZW1vcmllc0Rpcjogc3RyaW5nO1xuICAvKiogTWluaW11bSBtaWxsaXNlY29uZHMgYmV0d2VlbiBmdWxsIHNjYW5zIChkZWZhdWx0OiAxMDAwKSAqL1xuICBzY2FuQ29vbGRvd25Ncz86IG51bWJlcjtcbiAgLyoqIE92ZXJyaWRlIHRoZSBzdG9yYWdlIGJhY2tlbmQgKHVzZWZ1bCBmb3IgdGVzdGluZykgKi9cbiAgc3RvcmFnZUJhY2tlbmQ/OiBJU3RvcmFnZUJhY2tlbmQ7XG4gIC8qKiBEZWJvdW5jZSBpbnRlcnZhbCBmb3IgX2luZGV4Lmpzb24gd3JpdGVzIChkZWZhdWx0OiAyMDAwKSAqL1xuICBpbmRleERlYm91bmNlTXM/OiBudW1iZXI7XG4gIC8qKiBGaWxlIGZpbHRlciBwcmVkaWNhdGUgKGUuZy4sIGV4Y2x1ZGUgYmFja3VwIGZpbGVzKSAqL1xuICBmaWxlRmlsdGVyPzogKGZpbGVuYW1lOiBzdHJpbmcpID0+IGJvb2xlYW47XG59XG5cbmNvbnN0IEVNUFRZX0RJRkY6IE1hbmlmZXN0RGlmZlJlc3VsdCA9IHsgYWRkZWQ6IFtdLCBtb2RpZmllZDogW10sIHJlbW92ZWQ6IFtdLCB1bmNoYW5nZWQ6IFtdIH07XG5jb25zdCBEQVRFX0ZPTERFUl9QQVRURVJOID0gL15cXGR7NH0tXFxkezJ9LVxcZHsyfSQvO1xuXG5leHBvcnQgY2xhc3MgTWVtb3J5U3RvcmFnZUxheWVyIGltcGxlbWVudHMgSVN0b3JhZ2VMYXllciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYmFja2VuZDogSVN0b3JhZ2VCYWNrZW5kO1xuICBwcml2YXRlIHJlYWRvbmx5IG1hbmlmZXN0ID0gbmV3IFN0b3JhZ2VNYW5pZmVzdCgpO1xuICBwcml2YXRlIHJlYWRvbmx5IGluZGV4ID0gbmV3IE1ldGFkYXRhSW5kZXgoKTtcbiAgcHJpdmF0ZSByZWFkb25seSBpbmRleEZpbGU6IE1lbW9yeUluZGV4RmlsZTtcbiAgcHJpdmF0ZSByZWFkb25seSBtZW1vcmllc0Rpcjogc3RyaW5nO1xuICBwcml2YXRlIHJlYWRvbmx5IHNjYW5Db29sZG93bk1zOiBudW1iZXI7XG4gIHByaXZhdGUgcmVhZG9ubHkgZmlsZUZpbHRlcj86IChmaWxlbmFtZTogc3RyaW5nKSA9PiBib29sZWFuO1xuXG4gIHByaXZhdGUgbGFzdFNjYW5UaW1lc3RhbXAgPSAwO1xuICBwcml2YXRlIHNjYW5JblByb2dyZXNzOiBQcm9taXNlPE1hbmlmZXN0RGlmZlJlc3VsdD4gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBjb2xkU3RhcnREb25lID0gZmFsc2U7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBmaWxlT3BzOiBGaWxlT3BlcmF0aW9uc1NlcnZpY2UsXG4gICAgb3B0aW9uczogTWVtb3J5U3RvcmFnZUxheWVyT3B0aW9uc1xuICApIHtcbiAgICB0aGlzLm1lbW9yaWVzRGlyID0gb3B0aW9ucy5tZW1vcmllc0RpcjtcbiAgICB0aGlzLnNjYW5Db29sZG93bk1zID0gb3B0aW9ucy5zY2FuQ29vbGRvd25NcyA/PyAxMDAwO1xuICAgIHRoaXMuZmlsZUZpbHRlciA9IG9wdGlvbnMuZmlsZUZpbHRlcjtcblxuICAgIGlmIChvcHRpb25zLnN0b3JhZ2VCYWNrZW5kKSB7XG4gICAgICB0aGlzLmJhY2tlbmQgPSBvcHRpb25zLnN0b3JhZ2VCYWNrZW5kO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmJhY2tlbmQgPSBuZXcgRmlsZVN0b3JhZ2VCYWNrZW5kKGZpbGVPcHMpO1xuICAgIH1cblxuICAgIGNvbnN0IGluZGV4UGF0aCA9IHBhdGguam9pbih0aGlzLm1lbW9yaWVzRGlyLCAnX2luZGV4Lmpzb24nKTtcbiAgICB0aGlzLmluZGV4RmlsZSA9IG5ldyBNZW1vcnlJbmRleEZpbGUoaW5kZXhQYXRoLCBmaWxlT3BzLCB7XG4gICAgICBkZWJvdW5jZU1zOiBvcHRpb25zLmluZGV4RGVib3VuY2VNcyA/PyAyMDAwLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gLS0tLSBJU3RvcmFnZUxheWVyIGltcGxlbWVudGF0aW9uIC0tLS1cblxuICBhc3luYyBzY2FuKCk6IFByb21pc2U8TWFuaWZlc3REaWZmUmVzdWx0PiB7XG4gICAgLy8gQ29sZCBzdGFydDogdHJ5IGxvYWRpbmcgZnJvbSBfaW5kZXguanNvbiBmaXJzdFxuICAgIGlmICghdGhpcy5jb2xkU3RhcnREb25lKSB7XG4gICAgICB0aGlzLmNvbGRTdGFydERvbmUgPSB0cnVlO1xuICAgICAgcmV0dXJuIHRoaXMuY29sZFN0YXJ0KCk7XG4gICAgfVxuXG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICBpZiAobm93IC0gdGhpcy5sYXN0U2NhblRpbWVzdGFtcCA8IHRoaXMuc2NhbkNvb2xkb3duTXMpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgTWVtb3J5U3RvcmFnZUxheWVyLnNjYW46IENPT0xET1dOIEFDVElWRSDigJQgc2tpcHBpbmcgZGlzayBJL08gKCR7dGhpcy5zY2FuQ29vbGRvd25NcyAtIChub3cgLSB0aGlzLmxhc3RTY2FuVGltZXN0YW1wKX1tcyByZW1haW5pbmcpYCk7XG4gICAgICByZXR1cm4gRU1QVFlfRElGRjtcbiAgICB9XG5cbiAgICAvLyBEZWR1cGxpY2F0ZSBjb25jdXJyZW50IHNjYW5zXG4gICAgaWYgKHRoaXMuc2NhbkluUHJvZ3Jlc3MpIHtcbiAgICAgIHJldHVybiB0aGlzLnNjYW5JblByb2dyZXNzO1xuICAgIH1cblxuICAgIHRoaXMuc2NhbkluUHJvZ3Jlc3MgPSB0aGlzLnBlcmZvcm1TY2FuKCk7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnNjYW5JblByb2dyZXNzO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnNjYW5JblByb2dyZXNzID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICBhc3luYyBsaXN0U3VtbWFyaWVzKCk6IFByb21pc2U8RWxlbWVudEluZGV4RW50cnlbXT4ge1xuICAgIGF3YWl0IHRoaXMuc2NhbigpO1xuICAgIHJldHVybiB0aGlzLmRlZHVwbGljYXRlQnlOYW1lKHRoaXMuaW5kZXguZ2V0QWxsKCkpO1xuICB9XG5cbiAgYXN5bmMgZ2V0SW5kZXhlZFBhdGhzKCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBhd2FpdCB0aGlzLnNjYW4oKTtcbiAgICBjb25zdCBkZWR1cGxpY2F0ZWQgPSB0aGlzLmRlZHVwbGljYXRlQnlOYW1lKHRoaXMuaW5kZXguZ2V0QWxsKCkpO1xuICAgIHJldHVybiBkZWR1cGxpY2F0ZWQubWFwKGVudHJ5ID0+IGVudHJ5LmZpbGVQYXRoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZWR1cGxpY2F0ZSBpbmRleCBlbnRyaWVzIGJ5IG5vcm1hbGl6ZWQgbmFtZS5cbiAgICogV2hlbiB0aGUgc2FtZSBtZW1vcnkgYXBwZWFycyBpbiBtdWx0aXBsZSBkYXRlIGZvbGRlcnMsIGtlZXAgdGhlIG9uZVxuICAgKiB3aXRoIHRoZSBoaWdoZXN0IG10aW1lTXMgKG1vc3QgcmVjZW50bHkgbW9kaWZpZWQpLlxuICAgKlxuICAgKiBJc3N1ZSAjNjU0OiBTYW1lIG1lbW9yeSBmaWxlcyBkdXBsaWNhdGVkIGFjcm9zcyB+MTUwIGRhdGUgZm9sZGVyc1xuICAgKiBpbmZsYXRlZCBjb3VudHMgZnJvbSB+NzUwIHVuaXF1ZSB0byB+MywxMDArIGxvYWRlZCBtZW1vcmllcy5cbiAgICovXG4gIHByaXZhdGUgZGVkdXBsaWNhdGVCeU5hbWUoZW50cmllczogRWxlbWVudEluZGV4RW50cnlbXSk6IEVsZW1lbnRJbmRleEVudHJ5W10ge1xuICAgIGNvbnN0IGJ5TmFtZSA9IG5ldyBNYXA8c3RyaW5nLCBFbGVtZW50SW5kZXhFbnRyeT4oKTtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgIGNvbnN0IGtleSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKGVudHJ5Lm5hbWUpLm5vcm1hbGl6ZWRDb250ZW50LnRvTG93ZXJDYXNlKCk7XG4gICAgICBjb25zdCBleGlzdGluZyA9IGJ5TmFtZS5nZXQoa2V5KTtcbiAgICAgIGlmICghZXhpc3RpbmcgfHwgZW50cnkubXRpbWVNcyA+IGV4aXN0aW5nLm10aW1lTXMpIHtcbiAgICAgICAgYnlOYW1lLnNldChrZXksIGVudHJ5KTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFsuLi5ieU5hbWUudmFsdWVzKCldO1xuICB9XG5cbiAgZ2V0UGF0aEJ5TmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IG5vcm1hbGl6ZWROYW1lID0gVW5pY29kZVZhbGlkYXRvci5ub3JtYWxpemUobmFtZSkubm9ybWFsaXplZENvbnRlbnQ7XG4gICAgcmV0dXJuIHRoaXMuaW5kZXguZ2V0UGF0aEJ5TmFtZShub3JtYWxpemVkTmFtZSk7XG4gIH1cblxuICBoYXNDb21wbGV0ZWRTY2FuKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmxhc3RTY2FuVGltZXN0YW1wID4gMDtcbiAgfVxuXG4gIGFzeW5jIG5vdGlmeVNhdmVkKHJlbGF0aXZlUGF0aDogc3RyaW5nLCBhYnNvbHV0ZVBhdGg6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgdGhpcy5iYWNrZW5kLnJlYWRGaWxlKGFic29sdXRlUGF0aCk7XG4gICAgICBjb25zdCBtZXRhID0gYXdhaXQgdGhpcy5iYWNrZW5kLnN0YXQoYWJzb2x1dGVQYXRoKTtcbiAgICAgIGNvbnN0IGV4dHJhY3RlZCA9IE1lbW9yeU1ldGFkYXRhRXh0cmFjdG9yLmV4dHJhY3RNZXRhZGF0YShjb250ZW50LCByZWxhdGl2ZVBhdGgpO1xuXG4gICAgICBjb25zdCBlbnRyeTogRWxlbWVudEluZGV4RW50cnkgPSB7XG4gICAgICAgIGZpbGVQYXRoOiByZWxhdGl2ZVBhdGgsXG4gICAgICAgIG5hbWU6IGV4dHJhY3RlZC5uYW1lID8/ICd1bm5hbWVkJyxcbiAgICAgICAgZGVzY3JpcHRpb246IGV4dHJhY3RlZC5kZXNjcmlwdGlvbiA/PyAnJyxcbiAgICAgICAgdmVyc2lvbjogZXh0cmFjdGVkLnZlcnNpb24gPz8gJzEuMC4wJyxcbiAgICAgICAgYXV0aG9yOiBleHRyYWN0ZWQuYXV0aG9yID8/ICcnLFxuICAgICAgICB0YWdzOiBleHRyYWN0ZWQudGFncyA/PyBbXSxcbiAgICAgICAgbXRpbWVNczogbWV0YS5tdGltZU1zLFxuICAgICAgICBzaXplQnl0ZXM6IG1ldGEuc2l6ZUJ5dGVzLFxuICAgICAgICBhdXRvTG9hZDogZXh0cmFjdGVkLmF1dG9Mb2FkLFxuICAgICAgICBwcmlvcml0eTogZXh0cmFjdGVkLnByaW9yaXR5LFxuICAgICAgICBtZW1vcnlUeXBlOiBleHRyYWN0ZWQubWVtb3J5VHlwZSxcbiAgICAgICAgdG90YWxFbnRyaWVzOiBleHRyYWN0ZWQudG90YWxFbnRyaWVzLFxuICAgICAgfTtcblxuICAgICAgdGhpcy5pbmRleC5zZXQoZW50cnkpO1xuICAgICAgdGhpcy5tYW5pZmVzdC5zZXQocmVsYXRpdmVQYXRoLCBtZXRhLm10aW1lTXMpO1xuICAgICAgdGhpcy5pbmRleEZpbGUuc2NoZWR1bGVXcml0ZSh0aGlzLmluZGV4LmdldEFsbCgpKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdNZW1vcnlTdG9yYWdlTGF5ZXIubm90aWZ5U2F2ZWQgZmFpbGVkLCBpbnZhbGlkYXRpbmcnLCB7XG4gICAgICAgIHJlbGF0aXZlUGF0aCxcbiAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5pbnZhbGlkYXRlKCk7XG4gICAgfVxuICB9XG5cbiAgbm90aWZ5RGVsZXRlZChyZWxhdGl2ZVBhdGg6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuaW5kZXgucmVtb3ZlKHJlbGF0aXZlUGF0aCk7XG4gICAgdGhpcy5tYW5pZmVzdC5yZW1vdmUocmVsYXRpdmVQYXRoKTtcbiAgICB0aGlzLmluZGV4RmlsZS5zY2hlZHVsZVdyaXRlKHRoaXMuaW5kZXguZ2V0QWxsKCkpO1xuICB9XG5cbiAgaW52YWxpZGF0ZSgpOiB2b2lkIHtcbiAgICB0aGlzLmxhc3RTY2FuVGltZXN0YW1wID0gMDtcbiAgfVxuXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIHRoaXMuaW5kZXhGaWxlLmRpc3Bvc2UoKTsgIC8vIENhbmNlbCBwZW5kaW5nIGRlYm91bmNlZCB3cml0ZSAocHJldmVudHMgRU5PVEVNUFRZIGluIHRlc3RzKVxuICAgIHRoaXMuaW5kZXguY2xlYXIoKTtcbiAgICB0aGlzLm1hbmlmZXN0LmNsZWFyKCk7XG4gICAgdGhpcy5sYXN0U2NhblRpbWVzdGFtcCA9IDA7XG4gIH1cblxuICAvLyAtLS0tIE1lbW9yeS1zcGVjaWZpYyBtZXRob2RzIC0tLS1cblxuICAvKipcbiAgICogUmV0dXJuIGluZGV4IGVudHJpZXMgd2hlcmUgYXV0b0xvYWQgPT09IHRydWUsIHNvcnRlZCBieSBwcmlvcml0eSBhc2NlbmRpbmcuXG4gICAqIFB1cmUgaW4tbWVtb3J5IOKAlCBkb2VzIE5PVCB0cmlnZ2VyIGEgc2Nhbi5cbiAgICovXG4gIGdldEF1dG9Mb2FkRW50cmllcygpOiBFbGVtZW50SW5kZXhFbnRyeVtdIHtcbiAgICByZXR1cm4gdGhpcy5pbmRleC5nZXRBbGwoKVxuICAgICAgLmZpbHRlcihlbnRyeSA9PiBlbnRyeS5hdXRvTG9hZCA9PT0gdHJ1ZSlcbiAgICAgIC5zb3J0KChhLCBiKSA9PiAoYS5wcmlvcml0eSA/PyA5OTkpIC0gKGIucHJpb3JpdHkgPz8gOTk5KSk7XG4gIH1cblxuICAvKipcbiAgICogRnVsbCBkaXNrIHNjYW4gb2YgYWxsIHN1YmRpcmVjdG9yaWVzICsgd3JpdGUgX2luZGV4Lmpzb24uXG4gICAqL1xuICBhc3luYyByZWJ1aWxkSW5kZXgoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbG9nZ2VyLmluZm8oJ01lbW9yeVN0b3JhZ2VMYXllcjogcmVidWlsZGluZyBpbmRleCBmcm9tIGRpc2suLi4nKTtcbiAgICB0aGlzLmluZGV4LmNsZWFyKCk7XG4gICAgdGhpcy5tYW5pZmVzdC5jbGVhcigpO1xuXG4gICAgYXdhaXQgdGhpcy5wZXJmb3JtU2NhbigpO1xuICAgIGF3YWl0IHRoaXMuaW5kZXhGaWxlLndyaXRlKHRoaXMuaW5kZXguZ2V0QWxsKCkpO1xuXG4gICAgbG9nZ2VyLmluZm8oYE1lbW9yeVN0b3JhZ2VMYXllcjogcmVidWlsZCBjb21wbGV0ZSDigJQgJHt0aGlzLmluZGV4LnNpemV9IGVudHJpZXMgaW5kZXhlZGApO1xuICB9XG5cbiAgLyoqXG4gICAqIEZsdXNoIHBlbmRpbmcgX2luZGV4Lmpzb24gd3JpdGUgYW5kIHJlbGVhc2UgcmVzb3VyY2VzLlxuICAgKi9cbiAgYXN5bmMgZGlzcG9zZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLmluZGV4RmlsZS5mbHVzaCgpO1xuICAgIHRoaXMuaW5kZXhGaWxlLmRpc3Bvc2UoKTtcbiAgfVxuXG4gIC8vIC0tLS0gUHJpdmF0ZSAtLS0tXG5cbiAgLyoqXG4gICAqIENvbGQgc3RhcnQ6IHRyeSB0byByZXN0b3JlIGZyb20gX2luZGV4Lmpzb24sIHRoZW4gcnVuIGluY3JlbWVudGFsIHNjYW4uXG4gICAqIEZhbGxzIGJhY2sgdG8gZnVsbCByZWJ1aWxkIGlmIGluZGV4IGlzIG1pc3NpbmcvY29ycnVwdC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgY29sZFN0YXJ0KCk6IFByb21pc2U8TWFuaWZlc3REaWZmUmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNhY2hlZCA9IGF3YWl0IHRoaXMuaW5kZXhGaWxlLnJlYWQoKTtcblxuICAgICAgaWYgKGNhY2hlZCkge1xuICAgICAgICAvLyBQb3B1bGF0ZSBpbmRleCBhbmQgbWFuaWZlc3QgZnJvbSBjYWNoZWQgZGF0YVxuICAgICAgICBmb3IgKGNvbnN0IFtyZWxQYXRoLCBlbnRyeV0gb2YgT2JqZWN0LmVudHJpZXMoY2FjaGVkLmVudHJpZXMpKSB7XG4gICAgICAgICAgdGhpcy5pbmRleC5zZXQoZW50cnkpO1xuICAgICAgICAgIHRoaXMubWFuaWZlc3Quc2V0KHJlbFBhdGgsIGVudHJ5Lm10aW1lTXMpO1xuICAgICAgICB9XG4gICAgICAgIGxvZ2dlci5pbmZvKGBNZW1vcnlTdG9yYWdlTGF5ZXI6IGNvbGQgc3RhcnQg4oCUIGxvYWRlZCAke09iamVjdC5rZXlzKGNhY2hlZC5lbnRyaWVzKS5sZW5ndGh9IGVudHJpZXMgZnJvbSBfaW5kZXguanNvbmApO1xuXG4gICAgICAgIC8vIFJ1biBpbmNyZW1lbnRhbCBzY2FuIHRvIGNhdGNoIGNoYW5nZXMgc2luY2UgX2luZGV4Lmpzb24gd2FzIHdyaXR0ZW5cbiAgICAgICAgcmV0dXJuIHRoaXMucGVyZm9ybVNjYW4oKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdNZW1vcnlTdG9yYWdlTGF5ZXI6IGNvbGQgc3RhcnQgX2luZGV4Lmpzb24gcmVhZCBmYWlsZWQsIHJlYnVpbGRpbmcnLCB7XG4gICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICAvLyBObyB2YWxpZCBfaW5kZXguanNvbiDigJQgZnVsbCByZWJ1aWxkXG4gICAgYXdhaXQgdGhpcy5yZWJ1aWxkSW5kZXgoKTtcbiAgICByZXR1cm4gRU1QVFlfRElGRjtcbiAgfVxuXG4gIC8qKlxuICAgKiBEaXNjb3ZlciBhbGwgc3ViZGlyZWN0b3JpZXMgdG8gc2Nhbi5cbiAgICogUmV0dXJucyBbJ3N5c3RlbScsICdhZGFwdGVycycsIC4uLmRhdGVGb2xkZXJzLCAnJ10gd2hlcmUgJycgPSByb290LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBkaXNjb3ZlclN1YmRpcmVjdG9yaWVzKCk6IFByb21pc2U8c3RyaW5nW10+IHtcbiAgICBjb25zdCBzdWJkaXJzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCB0aGlzLmZpbGVPcHMubGlzdERpcmVjdG9yeSh0aGlzLm1lbW9yaWVzRGlyKTtcblxuICAgICAgY29uc3QgZGF0ZUZvbGRlcnM6IHN0cmluZ1tdID0gW107XG4gICAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgICAgLy8gSW5jbHVkZSBrbm93biBmaXhlZCBkaXJlY3Rvcmllc1xuICAgICAgICBpZiAoZW50cnkgPT09ICdzeXN0ZW0nIHx8IGVudHJ5ID09PSAnYWRhcHRlcnMnKSB7XG4gICAgICAgICAgc3ViZGlycy5wdXNoKGVudHJ5KTtcbiAgICAgICAgfSBlbHNlIGlmIChEQVRFX0ZPTERFUl9QQVRURVJOLnRlc3QoZW50cnkpKSB7XG4gICAgICAgICAgZGF0ZUZvbGRlcnMucHVzaChlbnRyeSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gU2tpcCBiYWNrdXBzLywgX2luZGV4Lmpzb24sIGFuZCBvdGhlciBub24tbWVtb3J5IGRpcmVjdG9yaWVzXG4gICAgICB9XG4gICAgICAvLyBTb3J0IGRhdGUgZm9sZGVycyBjaHJvbm9sb2dpY2FsbHkgKG9sZGVzdCBmaXJzdCDihpIgbmV3ZXN0IGxhc3QpXG4gICAgICAvLyBzbyB0aGF0IG5hbWVUb1BhdGggaW4gTWV0YWRhdGFJbmRleCBlbmRzIHVwIHBvaW50aW5nIHRvIHRoZSBuZXdlc3QgY29weVxuICAgICAgZGF0ZUZvbGRlcnMuc29ydCgoYSwgYikgPT4gYS5sb2NhbGVDb21wYXJlKGIpKTtcbiAgICAgIHN1YmRpcnMucHVzaCguLi5kYXRlRm9sZGVycyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICgoZXJyb3IgYXMgYW55KS5jb2RlICE9PSAnRU5PRU5UJykge1xuICAgICAgICBsb2dnZXIuZGVidWcoJ01lbW9yeVN0b3JhZ2VMYXllcjogZmFpbGVkIHRvIGxpc3Qgc3ViZGlyZWN0b3JpZXMnLCB7XG4gICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQWx3YXlzIGluY2x1ZGUgcm9vdCAoZW1wdHkgc3RyaW5nID0gcm9vdCBkaXJlY3RvcnkpXG4gICAgc3ViZGlycy5wdXNoKCcnKTtcblxuICAgIHJldHVybiBzdWJkaXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFBlcmZvcm0gYSBmdWxsIGluY3JlbWVudGFsIHNjYW4gYWNyb3NzIGFsbCBzdWJkaXJlY3Rvcmllcy5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcGVyZm9ybVNjYW4oKTogUHJvbWlzZTxNYW5pZmVzdERpZmZSZXN1bHQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZXhpc3RzID0gYXdhaXQgdGhpcy5iYWNrZW5kLmRpcmVjdG9yeUV4aXN0cyh0aGlzLm1lbW9yaWVzRGlyKTtcbiAgICAgIGlmICghZXhpc3RzKSB7XG4gICAgICAgIGNvbnN0IHJlbW92ZWRQYXRocyA9IHRoaXMuaW5kZXguZ2V0UGF0aHMoKTtcbiAgICAgICAgdGhpcy5pbmRleC5jbGVhcigpO1xuICAgICAgICB0aGlzLm1hbmlmZXN0LmNsZWFyKCk7XG4gICAgICAgIHRoaXMubGFzdFNjYW5UaW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGFkZGVkOiBbXSxcbiAgICAgICAgICBtb2RpZmllZDogW10sXG4gICAgICAgICAgcmVtb3ZlZDogcmVtb3ZlZFBhdGhzLFxuICAgICAgICAgIHVuY2hhbmdlZDogW10sXG4gICAgICAgIH07XG4gICAgICB9XG5cbiAgICAgIC8vIDEuIERpc2NvdmVyIHN1YmRpcmVjdG9yaWVzXG4gICAgICBjb25zdCBzdWJkaXJzID0gYXdhaXQgdGhpcy5kaXNjb3ZlclN1YmRpcmVjdG9yaWVzKCk7XG5cbiAgICAgIC8vIDIuIEVudW1lcmF0ZSBhbGwgLnlhbWwgZmlsZXMgYWNyb3NzIHN1YmRpcmVjdG9yaWVzXG4gICAgICBjb25zdCBhbGxSZWxhdGl2ZVBhdGhzOiBzdHJpbmdbXSA9IFtdO1xuXG4gICAgICBmb3IgKGNvbnN0IHN1YmRpciBvZiBzdWJkaXJzKSB7XG4gICAgICAgIGNvbnN0IGFic0RpciA9IHN1YmRpciA/IHBhdGguam9pbih0aGlzLm1lbW9yaWVzRGlyLCBzdWJkaXIpIDogdGhpcy5tZW1vcmllc0RpcjtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgdGhpcy5iYWNrZW5kLmxpc3RGaWxlcyhhYnNEaXIsICcueWFtbCcpO1xuXG4gICAgICAgICAgZm9yIChjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICAvLyBBcHBseSBmaWxlIGZpbHRlciAoZS5nLiwgZXhjbHVkZSBiYWNrdXAgZmlsZXMpXG4gICAgICAgICAgICBpZiAodGhpcy5maWxlRmlsdGVyICYmICF0aGlzLmZpbGVGaWx0ZXIoZmlsZSkpIHtcbiAgICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFByZWZpeCB3aXRoIHN1YmRpciBmb3IgcmVsYXRpdmUgcGF0aFxuICAgICAgICAgICAgY29uc3QgcmVsUGF0aCA9IHN1YmRpciA/IGAke3N1YmRpcn0vJHtmaWxlfWAgOiBmaWxlO1xuICAgICAgICAgICAgYWxsUmVsYXRpdmVQYXRocy5wdXNoKHJlbFBhdGgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBEaXJlY3RvcnkgbWlnaHQgbm90IGV4aXN0IChlLmcuLCBubyBzeXN0ZW0vIGZvbGRlciB5ZXQpXG4gICAgICAgICAgaWYgKChlcnJvciBhcyBhbnkpLmNvZGUgIT09ICdFTk9FTlQnKSB7XG4gICAgICAgICAgICBsb2dnZXIuZGVidWcoYE1lbW9yeVN0b3JhZ2VMYXllcjogZmFpbGVkIHRvIGxpc3QgJHthYnNEaXJ9YCwge1xuICAgICAgICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIDMuIFN0YXQgYWxsIGZpbGVzXG4gICAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHRoaXMuYmFja2VuZC5zdGF0TWFueSh0aGlzLm1lbW9yaWVzRGlyLCBhbGxSZWxhdGl2ZVBhdGhzKTtcblxuICAgICAgLy8gNC4gRGlmZiBhZ2FpbnN0IG1hbmlmZXN0XG4gICAgICBjb25zdCBkaWZmID0gdGhpcy5tYW5pZmVzdC5kaWZmKHN0YXRzKTtcblxuICAgICAgY29uc3QgaGFzQ2hhbmdlcyA9IGRpZmYuYWRkZWQubGVuZ3RoID4gMCB8fCBkaWZmLm1vZGlmaWVkLmxlbmd0aCA+IDAgfHwgZGlmZi5yZW1vdmVkLmxlbmd0aCA+IDA7XG4gICAgICBpZiAoaGFzQ2hhbmdlcykge1xuICAgICAgICBsb2dnZXIuZGVidWcoYE1lbW9yeVN0b3JhZ2VMYXllci5zY2FuOiBESVNLIFNDQU4g4oCUICR7YWxsUmVsYXRpdmVQYXRocy5sZW5ndGh9IGZpbGVzLCAke2RpZmYuYWRkZWQubGVuZ3RofSBhZGRlZCwgJHtkaWZmLm1vZGlmaWVkLmxlbmd0aH0gbW9kaWZpZWQsICR7ZGlmZi5yZW1vdmVkLmxlbmd0aH0gcmVtb3ZlZGApO1xuICAgICAgfVxuXG4gICAgICAvLyA1LiBGb3IgYWRkZWQvbW9kaWZpZWQ6IHJlYWQgZmlsZSwgZXh0cmFjdCBtZXRhZGF0YSwgdXBkYXRlIGluZGV4XG4gICAgICBjb25zdCB0b0luZGV4ID0gWy4uLmRpZmYuYWRkZWQsIC4uLmRpZmYubW9kaWZpZWRdO1xuICAgICAgbGV0IGluZGV4VXBkYXRlZCA9IGZhbHNlO1xuXG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgdG9JbmRleC5tYXAoYXN5bmMgKHJlbFBhdGgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgYWJzUGF0aCA9IHBhdGguam9pbih0aGlzLm1lbW9yaWVzRGlyLCByZWxQYXRoKTtcbiAgICAgICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCB0aGlzLmJhY2tlbmQucmVhZEZpbGUoYWJzUGF0aCk7XG4gICAgICAgICAgICBjb25zdCBleHRyYWN0ZWQgPSBNZW1vcnlNZXRhZGF0YUV4dHJhY3Rvci5leHRyYWN0TWV0YWRhdGEoY29udGVudCwgcmVsUGF0aCk7XG4gICAgICAgICAgICBjb25zdCBtZXRhID0gc3RhdHMuZ2V0KHJlbFBhdGgpO1xuXG4gICAgICAgICAgICB0aGlzLmluZGV4LnNldCh7XG4gICAgICAgICAgICAgIGZpbGVQYXRoOiByZWxQYXRoLFxuICAgICAgICAgICAgICBuYW1lOiBleHRyYWN0ZWQubmFtZSA/PyAndW5uYW1lZCcsXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uOiBleHRyYWN0ZWQuZGVzY3JpcHRpb24gPz8gJycsXG4gICAgICAgICAgICAgIHZlcnNpb246IGV4dHJhY3RlZC52ZXJzaW9uID8/ICcxLjAuMCcsXG4gICAgICAgICAgICAgIGF1dGhvcjogZXh0cmFjdGVkLmF1dGhvciA/PyAnJyxcbiAgICAgICAgICAgICAgdGFnczogZXh0cmFjdGVkLnRhZ3MgPz8gW10sXG4gICAgICAgICAgICAgIG10aW1lTXM6IG1ldGE/Lm10aW1lTXMgPz8gMCxcbiAgICAgICAgICAgICAgc2l6ZUJ5dGVzOiBtZXRhPy5zaXplQnl0ZXMgPz8gMCxcbiAgICAgICAgICAgICAgYXV0b0xvYWQ6IGV4dHJhY3RlZC5hdXRvTG9hZCxcbiAgICAgICAgICAgICAgcHJpb3JpdHk6IGV4dHJhY3RlZC5wcmlvcml0eSxcbiAgICAgICAgICAgICAgbWVtb3J5VHlwZTogZXh0cmFjdGVkLm1lbW9yeVR5cGUsXG4gICAgICAgICAgICAgIHRvdGFsRW50cmllczogZXh0cmFjdGVkLnRvdGFsRW50cmllcyxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgaW5kZXhVcGRhdGVkID0gdHJ1ZTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgbG9nZ2VyLmRlYnVnKGBNZW1vcnlTdG9yYWdlTGF5ZXI6IGZhaWxlZCB0byBpbmRleCAke3JlbFBhdGh9YCwge1xuICAgICAgICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgKTtcblxuICAgICAgLy8gNi4gRm9yIHJlbW92ZWQ6IHJlbW92ZSBmcm9tIGluZGV4XG4gICAgICBmb3IgKGNvbnN0IHJlbFBhdGggb2YgZGlmZi5yZW1vdmVkKSB7XG4gICAgICAgIHRoaXMuaW5kZXgucmVtb3ZlKHJlbFBhdGgpO1xuICAgICAgICBpbmRleFVwZGF0ZWQgPSB0cnVlO1xuICAgICAgfVxuXG4gICAgICAvLyA3LiBVcGRhdGUgbWFuaWZlc3QgYW5kIHRpbWVzdGFtcFxuICAgICAgdGhpcy5tYW5pZmVzdC51cGRhdGUoc3RhdHMpO1xuICAgICAgdGhpcy5sYXN0U2NhblRpbWVzdGFtcCA9IERhdGUubm93KCk7XG5cbiAgICAgIC8vIDguIFNjaGVkdWxlIGRlYm91bmNlZCBfaW5kZXguanNvbiB3cml0ZSBpZiBpbmRleCBjaGFuZ2VkXG4gICAgICBpZiAoaW5kZXhVcGRhdGVkKSB7XG4gICAgICAgIHRoaXMuaW5kZXhGaWxlLnNjaGVkdWxlV3JpdGUodGhpcy5pbmRleC5nZXRBbGwoKSk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBkaWZmO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ01lbW9yeVN0b3JhZ2VMYXllci5wZXJmb3JtU2NhbiBmYWlsZWQnLCBlcnJvcik7XG4gICAgICB0aGlzLmxhc3RTY2FuVGltZXN0YW1wID0gRGF0ZS5ub3coKTsgLy8gUHJldmVudCByZXRyeSBzdG9ybXNcbiAgICAgIHJldHVybiBFTVBUWV9ESUZGO1xuICAgIH1cbiAgfVxufVxuIl19