UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

205 lines 27.3 kB
/** * ElementStorageLayer - Coordinates index, manifest, and backend for cache-aware listing. * * Owns the scan lifecycle: cooldown enforcement, concurrent call deduplication, * diff-based incremental index updates, and notifications from save/delete. */ import * as path from 'path'; import { logger } from '../utils/logger.js'; import { StorageManifest } from './StorageManifest.js'; import { MetadataIndex } from './MetadataIndex.js'; import { FrontmatterParser } from './FrontmatterParser.js'; import { FileStorageBackend } from './FileStorageBackend.js'; import { UnicodeValidator } from '../security/validators/unicodeValidator.js'; const EMPTY_DIFF = { added: [], modified: [], removed: [], unchanged: [] }; export class ElementStorageLayer { backend; manifest = new StorageManifest(); index = new MetadataIndex(); elementDir; fileExtension; scanCooldownMs; lastScanTimestamp = 0; scanInProgress = null; constructor(fileOperationsService, options) { this.elementDir = options.elementDir; this.fileExtension = options.fileExtension; this.scanCooldownMs = options.scanCooldownMs ?? 1000; if (options.storageBackend) { this.backend = options.storageBackend; } else { this.backend = new FileStorageBackend(fileOperationsService); } } /** * Scan the element directory for changes. * - No-op if within cooldown period (returns empty diff) * - Deduplicates concurrent calls (returns same promise) * - Updates index and manifest based on diff */ async scan() { const now = Date.now(); if (now - this.lastScanTimestamp < this.scanCooldownMs) { logger.debug(`ElementStorageLayer.scan: COOLDOWN ACTIVE for ${path.basename(this.elementDir)} — 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; } } /** * Trigger scan and return all index entries. */ async listSummaries() { await this.scan(); return this.index.getAll(); } /** * Trigger scan and return all indexed file paths. */ async getIndexedPaths() { await this.scan(); return this.index.getPaths(); } /** * O(1) name-to-path lookup from the index. Does not trigger a scan. */ getPathByName(name) { const normalizedName = UnicodeValidator.normalize(name).normalizedContent; return this.index.getPathByName(normalizedName); } /** * Returns true if at least one scan has completed (index is authoritative). */ hasCompletedScan() { return this.lastScanTimestamp > 0; } /** * Notify the storage layer that a file was saved. * Re-stats and re-parses the file to update index/manifest. */ async notifySaved(relativePath, absolutePath) { try { const meta = await this.backend.stat(absolutePath); const content = await this.backend.readFile(absolutePath); const fm = FrontmatterParser.extractMetadata(content); this.index.set({ filePath: relativePath, name: fm.name, description: fm.description, version: fm.version, author: fm.author, tags: fm.tags, mtimeMs: meta.mtimeMs, sizeBytes: meta.sizeBytes, }); this.manifest.set(relativePath, meta.mtimeMs); } catch (error) { logger.debug('ElementStorageLayer.notifySaved failed, invalidating', { relativePath, error: error instanceof Error ? error.message : String(error), }); this.invalidate(); } } /** * Notify the storage layer that a file was deleted. * Removes the entry from index and manifest. */ notifyDeleted(relativePath) { this.index.remove(relativePath); this.manifest.remove(relativePath); } /** * Force the next scan() to hit disk by resetting the cooldown timer. */ invalidate() { this.lastScanTimestamp = 0; } /** * Reset all state (index, manifest, cooldown). */ clear() { this.index.clear(); this.manifest.clear(); this.lastScanTimestamp = 0; } // ---- private ---- async performScan() { try { const exists = await this.backend.directoryExists(this.elementDir); if (!exists) { // Directory doesn't exist — clear everything and return const removedPaths = this.index.getPaths(); this.index.clear(); this.manifest.clear(); this.lastScanTimestamp = Date.now(); return { added: [], modified: [], removed: removedPaths, unchanged: [], }; } // 1. List + stat const files = await this.backend.listFiles(this.elementDir, this.fileExtension); const stats = await this.backend.statMany(this.elementDir, files); // 2. 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(`ElementStorageLayer.scan: DISK SCAN for ${path.basename(this.elementDir)} — ${files.length} files, ${diff.added.length} added, ${diff.modified.length} modified, ${diff.removed.length} removed`); } // 3. For added/modified: read frontmatter, update index const toIndex = [...diff.added, ...diff.modified]; await Promise.all(toIndex.map(async (relPath) => { try { const absPath = path.join(this.elementDir, relPath); const content = await this.backend.readFile(absPath); const fm = FrontmatterParser.extractMetadata(content); const meta = stats.get(relPath); this.index.set({ filePath: relPath, name: fm.name, description: fm.description, version: fm.version, author: fm.author, tags: fm.tags, mtimeMs: meta?.mtimeMs ?? 0, sizeBytes: meta?.sizeBytes ?? 0, }); } catch (error) { // Parse failure is non-fatal — skip this entry logger.debug(`ElementStorageLayer: failed to index ${relPath}`, { error: error instanceof Error ? error.message : String(error), }); } })); // 4. For removed: remove from index for (const relPath of diff.removed) { this.index.remove(relPath); } // 5. Update manifest and timestamp this.manifest.update(stats); this.lastScanTimestamp = Date.now(); return diff; } catch (error) { logger.error('ElementStorageLayer.performScan failed', error); this.lastScanTimestamp = Date.now(); // Prevent retry storms return EMPTY_DIFF; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRWxlbWVudFN0b3JhZ2VMYXllci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yYWdlL0VsZW1lbnRTdG9yYWdlTGF5ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFJNUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUMzRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUU3RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQWE5RSxNQUFNLFVBQVUsR0FBdUIsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFLENBQUM7QUFFL0YsTUFBTSxPQUFPLG1CQUFtQjtJQUNiLE9BQU8sQ0FBa0I7SUFDekIsUUFBUSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7SUFDakMsS0FBSyxHQUFHLElBQUksYUFBYSxFQUFFLENBQUM7SUFDNUIsVUFBVSxDQUFTO0lBQ25CLGFBQWEsQ0FBUztJQUN0QixjQUFjLENBQVM7SUFFaEMsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDO0lBQ3RCLGNBQWMsR0FBdUMsSUFBSSxDQUFDO0lBRWxFLFlBQ0UscUJBQTRDLEVBQzVDLE9BQW1DO1FBRW5DLElBQUksQ0FBQyxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQztRQUNyQyxJQUFJLENBQUMsYUFBYSxHQUFHLE9BQU8sQ0FBQyxhQUFhLENBQUM7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O2dCQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7b0JBQ2YsK0NBQStDO29CQUMvQyxNQUFNLENBQUMsS0FBSyxDQUFDLHdDQUF3QyxPQUFPLEVBQUUsRUFBRTt3QkFDOUQsS0FBSyxFQUFFLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7cUJBQzlELENBQUMsQ0FBQztnQkFDTCxDQUFDO1lBQ0gsQ0FBQyxDQUFDLENBQ0gsQ0FBQztZQUVGLG9DQUFvQztZQUNwQyxLQUFLLE1BQU0sT0FBTyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDN0IsQ0FBQztZQUVELG1DQUFtQztZQUNuQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUM1QixJQUFJLENBQUMsaUJBQWlCLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBRXBDLE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixNQUFNLENBQUMsS0FBSyxDQUFDLHdDQUF3QyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzlELElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQyx1QkFBdUI7WUFDNUQsT0FBTyxVQUFVLENBQUM7UUFDcEIsQ0FBQztJQUNILENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRWxlbWVudFN0b3JhZ2VMYXllciAtIENvb3JkaW5hdGVzIGluZGV4LCBtYW5pZmVzdCwgYW5kIGJhY2tlbmQgZm9yIGNhY2hlLWF3YXJlIGxpc3RpbmcuXG4gKlxuICogT3ducyB0aGUgc2NhbiBsaWZlY3ljbGU6IGNvb2xkb3duIGVuZm9yY2VtZW50LCBjb25jdXJyZW50IGNhbGwgZGVkdXBsaWNhdGlvbixcbiAqIGRpZmYtYmFzZWQgaW5jcmVtZW50YWwgaW5kZXggdXBkYXRlcywgYW5kIG5vdGlmaWNhdGlvbnMgZnJvbSBzYXZlL2RlbGV0ZS5cbiAqL1xuXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB0eXBlIHsgSVN0b3JhZ2VCYWNrZW5kIH0gZnJvbSAnLi9JU3RvcmFnZUJhY2tlbmQuanMnO1xuaW1wb3J0IHR5cGUgeyBJU3RvcmFnZUxheWVyIH0gZnJvbSAnLi9JU3RvcmFnZUxheWVyLmpzJztcbmltcG9ydCB0eXBlIHsgRWxlbWVudEluZGV4RW50cnksIE1hbmlmZXN0RGlmZlJlc3VsdCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgU3RvcmFnZU1hbmlmZXN0IH0gZnJvbSAnLi9TdG9yYWdlTWFuaWZlc3QuanMnO1xuaW1wb3J0IHsgTWV0YWRhdGFJbmRleCB9IGZyb20gJy4vTWV0YWRhdGFJbmRleC5qcyc7XG5pbXBvcnQgeyBGcm9udG1hdHRlclBhcnNlciB9IGZyb20gJy4vRnJvbnRtYXR0ZXJQYXJzZXIuanMnO1xuaW1wb3J0IHsgRmlsZVN0b3JhZ2VCYWNrZW5kIH0gZnJvbSAnLi9GaWxlU3RvcmFnZUJhY2tlbmQuanMnO1xuaW1wb3J0IHR5cGUgeyBGaWxlT3BlcmF0aW9uc1NlcnZpY2UgfSBmcm9tICcuLi9zZXJ2aWNlcy9GaWxlT3BlcmF0aW9uc1NlcnZpY2UuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgRWxlbWVudFN0b3JhZ2VMYXllck9wdGlvbnMge1xuICAvKiogQWJzb2x1dGUgcGF0aCB0byB0aGUgZWxlbWVudCBkaXJlY3RvcnkgKi9cbiAgZWxlbWVudERpcjogc3RyaW5nO1xuICAvKiogRmlsZSBleHRlbnNpb24gdG8gZmlsdGVyIChlLmcuICcubWQnKSAqL1xuICBmaWxlRXh0ZW5zaW9uOiBzdHJpbmc7XG4gIC8qKiBNaW5pbXVtIG1pbGxpc2Vjb25kcyBiZXR3ZWVuIGZ1bGwgc2NhbnMgKGRlZmF1bHQ6IDEwMDApICovXG4gIHNjYW5Db29sZG93bk1zPzogbnVtYmVyO1xuICAvKiogT3ZlcnJpZGUgdGhlIHN0b3JhZ2UgYmFja2VuZCAodXNlZnVsIGZvciB0ZXN0aW5nKSAqL1xuICBzdG9yYWdlQmFja2VuZD86IElTdG9yYWdlQmFja2VuZDtcbn1cblxuY29uc3QgRU1QVFlfRElGRjogTWFuaWZlc3REaWZmUmVzdWx0ID0geyBhZGRlZDogW10sIG1vZGlmaWVkOiBbXSwgcmVtb3ZlZDogW10sIHVuY2hhbmdlZDogW10gfTtcblxuZXhwb3J0IGNsYXNzIEVsZW1lbnRTdG9yYWdlTGF5ZXIgaW1wbGVtZW50cyBJU3RvcmFnZUxheWVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBiYWNrZW5kOiBJU3RvcmFnZUJhY2tlbmQ7XG4gIHByaXZhdGUgcmVhZG9ubHkgbWFuaWZlc3QgPSBuZXcgU3RvcmFnZU1hbmlmZXN0KCk7XG4gIHByaXZhdGUgcmVhZG9ubHkgaW5kZXggPSBuZXcgTWV0YWRhdGFJbmRleCgpO1xuICBwcml2YXRlIHJlYWRvbmx5IGVsZW1lbnREaXI6IHN0cmluZztcbiAgcHJpdmF0ZSByZWFkb25seSBmaWxlRXh0ZW5zaW9uOiBzdHJpbmc7XG4gIHByaXZhdGUgcmVhZG9ubHkgc2NhbkNvb2xkb3duTXM6IG51bWJlcjtcblxuICBwcml2YXRlIGxhc3RTY2FuVGltZXN0YW1wID0gMDtcbiAgcHJpdmF0ZSBzY2FuSW5Qcm9ncmVzczogUHJvbWlzZTxNYW5pZmVzdERpZmZSZXN1bHQ+IHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgZmlsZU9wZXJhdGlvbnNTZXJ2aWNlOiBGaWxlT3BlcmF0aW9uc1NlcnZpY2UsXG4gICAgb3B0aW9uczogRWxlbWVudFN0b3JhZ2VMYXllck9wdGlvbnNcbiAgKSB7XG4gICAgdGhpcy5lbGVtZW50RGlyID0gb3B0aW9ucy5lbGVtZW50RGlyO1xuICAgIHRoaXMuZmlsZUV4dGVuc2lvbiA9IG9wdGlvbnMuZmlsZUV4dGVuc2lvbjtcbiAgICB0aGlzLnNjYW5Db29sZG93bk1zID0gb3B0aW9ucy5zY2FuQ29vbGRvd25NcyA/PyAxMDAwO1xuXG4gICAgaWYgKG9wdGlvbnMuc3RvcmFnZUJhY2tlbmQpIHtcbiAgICAgIHRoaXMuYmFja2VuZCA9IG9wdGlvbnMuc3RvcmFnZUJhY2tlbmQ7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuYmFja2VuZCA9IG5ldyBGaWxlU3RvcmFnZUJhY2tlbmQoZmlsZU9wZXJhdGlvbnNTZXJ2aWNlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2NhbiB0aGUgZWxlbWVudCBkaXJlY3RvcnkgZm9yIGNoYW5nZXMuXG4gICAqIC0gTm8tb3AgaWYgd2l0aGluIGNvb2xkb3duIHBlcmlvZCAocmV0dXJucyBlbXB0eSBkaWZmKVxuICAgKiAtIERlZHVwbGljYXRlcyBjb25jdXJyZW50IGNhbGxzIChyZXR1cm5zIHNhbWUgcHJvbWlzZSlcbiAgICogLSBVcGRhdGVzIGluZGV4IGFuZCBtYW5pZmVzdCBiYXNlZCBvbiBkaWZmXG4gICAqL1xuICBhc3luYyBzY2FuKCk6IFByb21pc2U8TWFuaWZlc3REaWZmUmVzdWx0PiB7XG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgICBpZiAobm93IC0gdGhpcy5sYXN0U2NhblRpbWVzdGFtcCA8IHRoaXMuc2NhbkNvb2xkb3duTXMpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgRWxlbWVudFN0b3JhZ2VMYXllci5zY2FuOiBDT09MRE9XTiBBQ1RJVkUgZm9yICR7cGF0aC5iYXNlbmFtZSh0aGlzLmVsZW1lbnREaXIpfSDigJQgc2tpcHBpbmcgZGlzayBJL08gKCR7dGhpcy5zY2FuQ29vbGRvd25NcyAtIChub3cgLSB0aGlzLmxhc3RTY2FuVGltZXN0YW1wKX1tcyByZW1haW5pbmcpYCk7XG4gICAgICByZXR1cm4gRU1QVFlfRElGRjtcbiAgICB9XG5cbiAgICAvLyBEZWR1cGxpY2F0ZSBjb25jdXJyZW50IHNjYW5zXG4gICAgaWYgKHRoaXMuc2NhbkluUHJvZ3Jlc3MpIHtcbiAgICAgIHJldHVybiB0aGlzLnNjYW5JblByb2dyZXNzO1xuICAgIH1cblxuICAgIHRoaXMuc2NhbkluUHJvZ3Jlc3MgPSB0aGlzLnBlcmZvcm1TY2FuKCk7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBhd2FpdCB0aGlzLnNjYW5JblByb2dyZXNzO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLnNjYW5JblByb2dyZXNzID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVHJpZ2dlciBzY2FuIGFuZCByZXR1cm4gYWxsIGluZGV4IGVudHJpZXMuXG4gICAqL1xuICBhc3luYyBsaXN0U3VtbWFyaWVzKCk6IFByb21pc2U8RWxlbWVudEluZGV4RW50cnlbXT4ge1xuICAgIGF3YWl0IHRoaXMuc2NhbigpO1xuICAgIHJldHVybiB0aGlzLmluZGV4LmdldEFsbCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyaWdnZXIgc2NhbiBhbmQgcmV0dXJuIGFsbCBpbmRleGVkIGZpbGUgcGF0aHMuXG4gICAqL1xuICBhc3luYyBnZXRJbmRleGVkUGF0aHMoKTogUHJvbWlzZTxzdHJpbmdbXT4ge1xuICAgIGF3YWl0IHRoaXMuc2NhbigpO1xuICAgIHJldHVybiB0aGlzLmluZGV4LmdldFBhdGhzKCk7XG4gIH1cblxuICAvKipcbiAgICogTygxKSBuYW1lLXRvLXBhdGggbG9va3VwIGZyb20gdGhlIGluZGV4LiBEb2VzIG5vdCB0cmlnZ2VyIGEgc2Nhbi5cbiAgICovXG4gIGdldFBhdGhCeU5hbWUobmFtZTogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBub3JtYWxpemVkTmFtZSA9IFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKG5hbWUpLm5vcm1hbGl6ZWRDb250ZW50O1xuICAgIHJldHVybiB0aGlzLmluZGV4LmdldFBhdGhCeU5hbWUobm9ybWFsaXplZE5hbWUpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdHJ1ZSBpZiBhdCBsZWFzdCBvbmUgc2NhbiBoYXMgY29tcGxldGVkIChpbmRleCBpcyBhdXRob3JpdGF0aXZlKS5cbiAgICovXG4gIGhhc0NvbXBsZXRlZFNjYW4oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMubGFzdFNjYW5UaW1lc3RhbXAgPiAwO1xuICB9XG5cbiAgLyoqXG4gICAqIE5vdGlmeSB0aGUgc3RvcmFnZSBsYXllciB0aGF0IGEgZmlsZSB3YXMgc2F2ZWQuXG4gICAqIFJlLXN0YXRzIGFuZCByZS1wYXJzZXMgdGhlIGZpbGUgdG8gdXBkYXRlIGluZGV4L21hbmlmZXN0LlxuICAgKi9cbiAgYXN5bmMgbm90aWZ5U2F2ZWQocmVsYXRpdmVQYXRoOiBzdHJpbmcsIGFic29sdXRlUGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IG1ldGEgPSBhd2FpdCB0aGlzLmJhY2tlbmQuc3RhdChhYnNvbHV0ZVBhdGgpO1xuICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IHRoaXMuYmFja2VuZC5yZWFkRmlsZShhYnNvbHV0ZVBhdGgpO1xuICAgICAgY29uc3QgZm0gPSBGcm9udG1hdHRlclBhcnNlci5leHRyYWN0TWV0YWRhdGEoY29udGVudCk7XG5cbiAgICAgIHRoaXMuaW5kZXguc2V0KHtcbiAgICAgICAgZmlsZVBhdGg6IHJlbGF0aXZlUGF0aCxcbiAgICAgICAgbmFtZTogZm0ubmFtZSxcbiAgICAgICAgZGVzY3JpcHRpb246IGZtLmRlc2NyaXB0aW9uLFxuICAgICAgICB2ZXJzaW9uOiBmbS52ZXJzaW9uLFxuICAgICAgICBhdXRob3I6IGZtLmF1dGhvcixcbiAgICAgICAgdGFnczogZm0udGFncyxcbiAgICAgICAgbXRpbWVNczogbWV0YS5tdGltZU1zLFxuICAgICAgICBzaXplQnl0ZXM6IG1ldGEuc2l6ZUJ5dGVzLFxuICAgICAgfSk7XG4gICAgICB0aGlzLm1hbmlmZXN0LnNldChyZWxhdGl2ZVBhdGgsIG1ldGEubXRpbWVNcyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRWxlbWVudFN0b3JhZ2VMYXllci5ub3RpZnlTYXZlZCBmYWlsZWQsIGludmFsaWRhdGluZycsIHtcbiAgICAgICAgcmVsYXRpdmVQYXRoLFxuICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpLFxuICAgICAgfSk7XG4gICAgICB0aGlzLmludmFsaWRhdGUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTm90aWZ5IHRoZSBzdG9yYWdlIGxheWVyIHRoYXQgYSBmaWxlIHdhcyBkZWxldGVkLlxuICAgKiBSZW1vdmVzIHRoZSBlbnRyeSBmcm9tIGluZGV4IGFuZCBtYW5pZmVzdC5cbiAgICovXG4gIG5vdGlmeURlbGV0ZWQocmVsYXRpdmVQYXRoOiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmluZGV4LnJlbW92ZShyZWxhdGl2ZVBhdGgpO1xuICAgIHRoaXMubWFuaWZlc3QucmVtb3ZlKHJlbGF0aXZlUGF0aCk7XG4gIH1cblxuICAvKipcbiAgICogRm9yY2UgdGhlIG5leHQgc2NhbigpIHRvIGhpdCBkaXNrIGJ5IHJlc2V0dGluZyB0aGUgY29vbGRvd24gdGltZXIuXG4gICAqL1xuICBpbnZhbGlkYXRlKCk6IHZvaWQge1xuICAgIHRoaXMubGFzdFNjYW5UaW1lc3RhbXAgPSAwO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlc2V0IGFsbCBzdGF0ZSAoaW5kZXgsIG1hbmlmZXN0LCBjb29sZG93bikuXG4gICAqL1xuICBjbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmluZGV4LmNsZWFyKCk7XG4gICAgdGhpcy5tYW5pZmVzdC5jbGVhcigpO1xuICAgIHRoaXMubGFzdFNjYW5UaW1lc3RhbXAgPSAwO1xuICB9XG5cbiAgLy8gLS0tLSBwcml2YXRlIC0tLS1cblxuICBwcml2YXRlIGFzeW5jIHBlcmZvcm1TY2FuKCk6IFByb21pc2U8TWFuaWZlc3REaWZmUmVzdWx0PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGV4aXN0cyA9IGF3YWl0IHRoaXMuYmFja2VuZC5kaXJlY3RvcnlFeGlzdHModGhpcy5lbGVtZW50RGlyKTtcbiAgICAgIGlmICghZXhpc3RzKSB7XG4gICAgICAgIC8vIERpcmVjdG9yeSBkb2Vzbid0IGV4aXN0IOKAlCBjbGVhciBldmVyeXRoaW5nIGFuZCByZXR1cm5cbiAgICAgICAgY29uc3QgcmVtb3ZlZFBhdGhzID0gdGhpcy5pbmRleC5nZXRQYXRocygpO1xuICAgICAgICB0aGlzLmluZGV4LmNsZWFyKCk7XG4gICAgICAgIHRoaXMubWFuaWZlc3QuY2xlYXIoKTtcbiAgICAgICAgdGhpcy5sYXN0U2NhblRpbWVzdGFtcCA9IERhdGUubm93KCk7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgYWRkZWQ6IFtdLFxuICAgICAgICAgIG1vZGlmaWVkOiBbXSxcbiAgICAgICAgICByZW1vdmVkOiByZW1vdmVkUGF0aHMsXG4gICAgICAgICAgdW5jaGFuZ2VkOiBbXSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgLy8gMS4gTGlzdCArIHN0YXRcbiAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgdGhpcy5iYWNrZW5kLmxpc3RGaWxlcyh0aGlzLmVsZW1lbnREaXIsIHRoaXMuZmlsZUV4dGVuc2lvbik7XG4gICAgICBjb25zdCBzdGF0cyA9IGF3YWl0IHRoaXMuYmFja2VuZC5zdGF0TWFueSh0aGlzLmVsZW1lbnREaXIsIGZpbGVzKTtcblxuICAgICAgLy8gMi4gRGlmZiBhZ2FpbnN0IG1hbmlmZXN0XG4gICAgICBjb25zdCBkaWZmID0gdGhpcy5tYW5pZmVzdC5kaWZmKHN0YXRzKTtcblxuICAgICAgY29uc3QgaGFzQ2hhbmdlcyA9IGRpZmYuYWRkZWQubGVuZ3RoID4gMCB8fCBkaWZmLm1vZGlmaWVkLmxlbmd0aCA+IDAgfHwgZGlmZi5yZW1vdmVkLmxlbmd0aCA+IDA7XG4gICAgICBpZiAoaGFzQ2hhbmdlcykge1xuICAgICAgICBsb2dnZXIuZGVidWcoYEVsZW1lbnRTdG9yYWdlTGF5ZXIuc2NhbjogRElTSyBTQ0FOIGZvciAke3BhdGguYmFzZW5hbWUodGhpcy5lbGVtZW50RGlyKX0g4oCUICR7ZmlsZXMubGVuZ3RofSBmaWxlcywgJHtkaWZmLmFkZGVkLmxlbmd0aH0gYWRkZWQsICR7ZGlmZi5tb2RpZmllZC5sZW5ndGh9IG1vZGlmaWVkLCAke2RpZmYucmVtb3ZlZC5sZW5ndGh9IHJlbW92ZWRgKTtcbiAgICAgIH1cblxuICAgICAgLy8gMy4gRm9yIGFkZGVkL21vZGlmaWVkOiByZWFkIGZyb250bWF0dGVyLCB1cGRhdGUgaW5kZXhcbiAgICAgIGNvbnN0IHRvSW5kZXggPSBbLi4uZGlmZi5hZGRlZCwgLi4uZGlmZi5tb2RpZmllZF07XG4gICAgICBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgdG9JbmRleC5tYXAoYXN5bmMgKHJlbFBhdGgpID0+IHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgYWJzUGF0aCA9IHBhdGguam9pbih0aGlzLmVsZW1lbnREaXIsIHJlbFBhdGgpO1xuICAgICAgICAgICAgY29uc3QgY29udGVudCA9IGF3YWl0IHRoaXMuYmFja2VuZC5yZWFkRmlsZShhYnNQYXRoKTtcbiAgICAgICAgICAgIGNvbnN0IGZtID0gRnJvbnRtYXR0ZXJQYXJzZXIuZXh0cmFjdE1ldGFkYXRhKGNvbnRlbnQpO1xuICAgICAgICAgICAgY29uc3QgbWV0YSA9IHN0YXRzLmdldChyZWxQYXRoKTtcblxuICAgICAgICAgICAgdGhpcy5pbmRleC5zZXQoe1xuICAgICAgICAgICAgICBmaWxlUGF0aDogcmVsUGF0aCxcbiAgICAgICAgICAgICAgbmFtZTogZm0ubmFtZSxcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb246IGZtLmRlc2NyaXB0aW9uLFxuICAgICAgICAgICAgICB2ZXJzaW9uOiBmbS52ZXJzaW9uLFxuICAgICAgICAgICAgICBhdXRob3I6IGZtLmF1dGhvcixcbiAgICAgICAgICAgICAgdGFnczogZm0udGFncyxcbiAgICAgICAgICAgICAgbXRpbWVNczogbWV0YT8ubXRpbWVNcyA/PyAwLFxuICAgICAgICAgICAgICBzaXplQnl0ZXM6IG1ldGE/LnNpemVCeXRlcyA/PyAwLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIC8vIFBhcnNlIGZhaWx1cmUgaXMgbm9uLWZhdGFsIOKAlCBza2lwIHRoaXMgZW50cnlcbiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZyhgRWxlbWVudFN0b3JhZ2VMYXllcjogZmFpbGVkIHRvIGluZGV4ICR7cmVsUGF0aH1gLCB7XG4gICAgICAgICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICApO1xuXG4gICAgICAvLyA0LiBGb3IgcmVtb3ZlZDogcmVtb3ZlIGZyb20gaW5kZXhcbiAgICAgIGZvciAoY29uc3QgcmVsUGF0aCBvZiBkaWZmLnJlbW92ZWQpIHtcbiAgICAgICAgdGhpcy5pbmRleC5yZW1vdmUocmVsUGF0aCk7XG4gICAgICB9XG5cbiAgICAgIC8vIDUuIFVwZGF0ZSBtYW5pZmVzdCBhbmQgdGltZXN0YW1wXG4gICAgICB0aGlzLm1hbmlmZXN0LnVwZGF0ZShzdGF0cyk7XG4gICAgICB0aGlzLmxhc3RTY2FuVGltZXN0YW1wID0gRGF0ZS5ub3coKTtcblxuICAgICAgcmV0dXJuIGRpZmY7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRWxlbWVudFN0b3JhZ2VMYXllci5wZXJmb3JtU2NhbiBmYWlsZWQnLCBlcnJvcik7XG4gICAgICB0aGlzLmxhc3RTY2FuVGltZXN0YW1wID0gRGF0ZS5ub3coKTsgLy8gUHJldmVudCByZXRyeSBzdG9ybXNcbiAgICAgIHJldHVybiBFTVBUWV9ESUZGO1xuICAgIH1cbiAgfVxufVxuIl19