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.

311 lines 43.9 kB
import * as path from 'node:path'; import { watch, accessSync, mkdirSync, readdirSync, statSync } from 'node:fs'; import { logger } from '../utils/logger.js'; class PollingWatcher { interval; constructor(interval) { this.interval = interval; } close() { clearInterval(this.interval); } // Match the fs.FSWatcher shape well enough for internal use. on(_event, _listener) { return this; } } /** * Type guard to check if an error is a Node.js system error with an error code */ function isNodeError(error) { return error instanceof Error && 'code' in error; } /** * Thin wrapper over fs.watch that fan-outs change events to interested managers. * * This service is managed as a singleton by the DI container. * Inject it via constructor injection rather than using a static getInstance() method. */ export class FileWatchService { watchers = new Map(); debounceTimers = new Map(); /** * Watch a directory for changes. If the directory doesn't exist, it will be created. * * @param dirPath - Path to directory to watch * @param handler - Callback function invoked when files change * @returns Cleanup function to stop watching * * @remarks * This method ensures the directory exists before setting up the watcher. * fs.watch throws ENOENT if the directory doesn't exist, so we create it * with recursive:true to handle test scenarios and edge cases gracefully. * * File watching is optional functionality. If fs.watch() fails due to permission * restrictions or platform limitations, a no-op cleanup function is returned and * the application continues without file watching for that directory. */ watchDirectory(dirPath, handler) { const absoluteDir = path.resolve(dirPath); const existing = this.watchers.get(absoluteDir); if (existing) { existing.handlers.add(handler); return () => this.detachHandler(absoluteDir, handler); } // Ensure directory exists before watching // fs.watch throws ENOENT if directory doesn't exist, so we create it first try { accessSync(absoluteDir); } catch (_error) { // Directory doesn't exist - create it try { mkdirSync(absoluteDir, { recursive: true }); logger.debug('Created directory for watching', { directory: absoluteDir }); } catch (mkdirError) { // Failed to create directory - log and return no-op cleanup logger.warn('Failed to create directory for watching - watcher will not be set up', { directory: absoluteDir, error: mkdirError instanceof Error ? mkdirError.message : String(mkdirError) }); // Return a no-op cleanup function since watcher was not created return () => { }; } } const handlers = new Set([handler]); const startPollingWatcher = (reason) => { const snapshot = () => { const next = new Map(); for (const entry of readdirSync(absoluteDir, { withFileTypes: true })) { if (!entry.isFile()) { continue; } try { const stats = statSync(path.join(absoluteDir, entry.name)); next.set(entry.name, stats.mtimeMs); } catch { // Ignore races (file removed between readdir and stat, etc.) } } return next; }; let previous; try { previous = snapshot(); } catch (pollSetupError) { logger.warn('Failed to initialize polling directory watcher', { directory: absoluteDir, phase: reason?.phase, originalError: reason?.error instanceof Error ? reason.error.message : reason?.error, error: pollSetupError instanceof Error ? pollSetupError.message : String(pollSetupError), }); return undefined; } // Adaptive polling interval: scale with directory size to prevent // scan-overlaps on large portfolios (Issue #1687). // Base: 2s, +2ms per file, capped at 10s. const baseIntervalMs = 2000; const perFileMs = 2; const maxIntervalMs = 10_000; const adaptiveInterval = Math.min(baseIntervalMs + previous.size * perFileMs, maxIntervalMs); // Guard: skip poll if a previous scan is still being processed let scanInProgress = false; // Debounce: batch changed files and notify handlers once per cycle const interval = setInterval(() => { if (scanInProgress) return; scanInProgress = true; try { let next; try { next = snapshot(); } catch { return; } // Collect all changed files before notifying handlers const changedFiles = []; for (const [filename, mtimeMs] of next) { const priorMtime = previous.get(filename); if (priorMtime === undefined || priorMtime !== mtimeMs) { changedFiles.push(filename); } } // Deletions for (const filename of previous.keys()) { if (!next.has(filename)) { changedFiles.push(filename); } } // Notify handlers once per changed file (deduplicated) if (changedFiles.length > 0) { const uniqueChanges = [...new Set(changedFiles)]; for (const filename of uniqueChanges) { for (const h of handlers) { try { h(filename); } catch (error) { logger.warn('FileWatchService polling handler failed', { directory: absoluteDir, error: error instanceof Error ? error.message : error }); } } } } previous = next; } finally { scanInProgress = false; } }, adaptiveInterval); logger.warn('Falling back to polling directory watcher', { directory: absoluteDir, phase: reason?.phase, originalError: reason?.error instanceof Error ? reason.error.message : reason?.error, code: reason?.error && isNodeError(reason.error) ? reason.error.code : undefined }); return new PollingWatcher(interval); }; // Try to set up file watcher - may fail due to permission restrictions or platform limitations let watcher; try { // Debounce fs.watch events — coalesce rapid-fire notifications into // a single handler call per file within a 500ms window (Issue #1687). const pendingChanges = new Set(); watcher = watch(absoluteDir, { recursive: false }, (_eventType, filename) => { if (!filename) { return; } pendingChanges.add(filename.toString()); const existingTimer = this.debounceTimers.get(absoluteDir); if (existingTimer) clearTimeout(existingTimer); this.debounceTimers.set(absoluteDir, setTimeout(() => { this.debounceTimers.delete(absoluteDir); const files = [...pendingChanges]; pendingChanges.clear(); for (const relative of files) { for (const h of handlers) { try { h(relative); } catch (error) { logger.warn('FileWatchService handler failed', { directory: absoluteDir, error: error instanceof Error ? error.message : error }); } } } }, 500)); }); } catch (watchError) { // File watching failed - fall back to polling (or no-op if polling also fails). // Common on platforms with watch limits (EMFILE/ENOSPC) or restricted permissions. logger.warn('Failed to set up file watcher', { directory: absoluteDir, error: watchError instanceof Error ? watchError.message : String(watchError), code: isNodeError(watchError) ? watchError.code : undefined }); const pollingWatcher = startPollingWatcher({ error: watchError, phase: 'setup' }); if (!pollingWatcher) { // Return a no-op cleanup function since watcher was not created return () => { }; } this.watchers.set(absoluteDir, { watcher: pollingWatcher, handlers }); return () => this.detachHandler(absoluteDir, handler); } // Handle error events from the watcher (required for Windows) // On Windows, fs.watch emits error events for permission issues (EPERM) // Without this handler, Node.js throws "Unhandled 'error' event" let earlyWatchError; watcher.on('error', (error) => { // If the watcher errors immediately (before we store it), record and handle after setup. if (!this.watchers.has(absoluteDir)) { earlyWatchError = error; watcher.close(); return; } logger.warn('File watcher error - closing watcher', { directory: absoluteDir, error: error instanceof Error ? error.message : String(error), code: isNodeError(error) ? error.code : undefined }); // Close the watcher as recommended in Node.js documentation. // If the watcher is failing (e.g., EMFILE/ENOSPC), fall back to polling so the app can still react. const current = this.watchers.get(absoluteDir); const isCurrentWatcher = current?.watcher === watcher; watcher.close(); if (isCurrentWatcher) { const pollingWatcher = startPollingWatcher({ error, phase: 'runtime' }); if (pollingWatcher) { this.watchers.set(absoluteDir, { watcher: pollingWatcher, handlers }); } else { this.watchers.delete(absoluteDir); } } }); this.watchers.set(absoluteDir, { watcher, handlers }); logger.debug('Started directory watcher', { directory: absoluteDir }); if (earlyWatchError) { logger.warn('File watcher error during initialization - switching to polling', { directory: absoluteDir, error: earlyWatchError instanceof Error ? earlyWatchError.message : String(earlyWatchError), code: isNodeError(earlyWatchError) ? earlyWatchError.code : undefined }); const pollingWatcher = startPollingWatcher({ error: earlyWatchError, phase: 'runtime' }); if (pollingWatcher) { this.watchers.set(absoluteDir, { watcher: pollingWatcher, handlers }); } else { this.watchers.delete(absoluteDir); } } return () => this.detachHandler(absoluteDir, handler); } detachHandler(dir, handler) { const entry = this.watchers.get(dir); if (!entry) { return; } entry.handlers.delete(handler); if (entry.handlers.size === 0) { entry.watcher.close(); this.watchers.delete(dir); logger.debug('Stopped directory watcher', { directory: dir }); } } /** * Dispose of all file watchers. Called during graceful shutdown. * This method is invoked by the DI container's dispose() method. */ dispose() { // Clear all pending debounce timers to prevent post-dispose handler calls for (const [, timer] of this.debounceTimers) { clearTimeout(timer); } this.debounceTimers.clear(); for (const [dir, entry] of this.watchers) { try { entry.watcher.close(); logger.debug('Closed directory watcher during dispose', { directory: dir }); } catch (error) { logger.warn('Error closing watcher during dispose', { directory: dir, error: error instanceof Error ? error.message : String(error) }); } } this.watchers.clear(); logger.debug('FileWatchService disposed', { watchersClosed: this.watchers.size === 0 }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmlsZVdhdGNoU2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlcy9GaWxlV2F0Y2hTZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxLQUFLLEVBQWEsVUFBVSxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsUUFBUSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pGLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQVM1QyxNQUFNLGNBQWM7SUFDRCxRQUFRLENBQWlCO0lBRTFDLFlBQVksUUFBd0I7UUFDbEMsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7SUFDM0IsQ0FBQztJQUVELEtBQUs7UUFDSCxhQUFhLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRCw2REFBNkQ7SUFDN0QsRUFBRSxDQUFDLE1BQWMsRUFBRSxTQUF1QztRQUN4RCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7Q0FDRjtBQUVEOztHQUVHO0FBQ0gsU0FBUyxXQUFXLENBQUMsS0FBYztJQUNqQyxPQUFPLEtBQUssWUFBWSxLQUFLLElBQUksTUFBTSxJQUFJLEtBQUssQ0FBQztBQUNuRCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWdCO0lBQ1YsUUFBUSxHQUFHLElBQUksR0FBRyxFQUFzQixDQUFDO0lBQ3pDLGNBQWMsR0FBRyxJQUFJLEdBQUcsRUFBeUMsQ0FBQztJQUVuRjs7Ozs7Ozs7Ozs7Ozs7O09BZUc7SUFDSCxjQUFjLENBQUMsT0FBZSxFQUFFLE9BQTBCO1FBQ3hELE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUMsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMs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QyxRQUFRLEVBQUUsQ0FBQztZQUN6QyxJQUFJLENBQUM7Z0JBQ0gsS0FBSyxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDdEIsTUFBTSxDQUFDLEtBQUssQ0FBQyx5Q0FBeUMsRUFBRSxFQUFFLFNBQVMsRUFBRSxHQUFHLEVBQUUsQ0FBQyxDQUFDO1lBQzlFLENBQUM7WUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO2dCQUNmLE1BQU0sQ0FBQyxJQUFJLENBQUMsc0NBQXNDLEVBQUU7b0JBQ2xELFNBQVMsRUFBRSxHQUFHO29CQUNkLEtBQUssRUFBRSxLQUFLLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO2lCQUM5RCxDQUFDLENBQUM7WUFDTCxDQUFDO1FBQ0gsQ0FBQztRQUNELElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDdEIsTUFBTSxDQUFDLEtBQUssQ0FBQywyQkFBMkIsRUFBRSxFQUFFLGNBQWMsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksS0FBSyxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBQzFGLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIHBhdGggZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCB7IHdhdGNoLCBGU1dhdGNoZXIsIGFjY2Vzc1N5bmMsIG1rZGlyU3luYywgcmVhZGRpclN5bmMsIHN0YXRTeW5jIH0gZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG5leHBvcnQgdHlwZSBGaWxlQ2hhbmdlSGFuZGxlciA9IChyZWxhdGl2ZVBhdGg6IHN0cmluZykgPT4gdm9pZDtcblxuaW50ZXJmYWNlIFdhdGNoRW50cnkge1xuICB3YXRjaGVyOiBGU1dhdGNoZXIgfCBQb2xsaW5nV2F0Y2hlcjtcbiAgaGFuZGxlcnM6IFNldDxGaWxlQ2hhbmdlSGFuZGxlcj47XG59XG5cbmNsYXNzIFBvbGxpbmdXYXRjaGVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBpbnRlcnZhbDogTm9kZUpTLlRpbWVvdXQ7XG5cbiAgY29uc3RydWN0b3IoaW50ZXJ2YWw6IE5vZGVKUy5UaW1lb3V0KSB7XG4gICAgdGhpcy5pbnRlcnZhbCA9IGludGVydmFsO1xuICB9XG5cbiAgY2xvc2UoKTogdm9pZCB7XG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLmludGVydmFsKTtcbiAgfVxuXG4gIC8vIE1hdGNoIHRoZSBmcy5GU1dhdGNoZXIgc2hhcGUgd2VsbCBlbm91Z2ggZm9yIGludGVybmFsIHVzZS5cbiAgb24oX2V2ZW50OiBzdHJpbmcsIF9saXN0ZW5lcjogKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gdm9pZCk6IHRoaXMge1xuICAgIHJldHVybiB0aGlzO1xuICB9XG59XG5cbi8qKlxuICogVHlwZSBndWFyZCB0byBjaGVjayBpZiBhbiBlcnJvciBpcyBhIE5vZGUuanMgc3lzdGVtIGVycm9yIHdpdGggYW4gZXJyb3IgY29kZVxuICovXG5mdW5jdGlvbiBpc05vZGVFcnJvcihlcnJvcjogdW5rbm93bik6IGVycm9yIGlzIE5vZGVKUy5FcnJub0V4Y2VwdGlvbiB7XG4gIHJldHVybiBlcnJvciBpbnN0YW5jZW9mIEVycm9yICYmICdjb2RlJyBpbiBlcnJvcjtcbn1cblxuLyoqXG4gKiBUaGluIHdyYXBwZXIgb3ZlciBmcy53YXRjaCB0aGF0IGZhbi1vdXRzIGNoYW5nZSBldmVudHMgdG8gaW50ZXJlc3RlZCBtYW5hZ2Vycy5cbiAqXG4gKiBUaGlzIHNlcnZpY2UgaXMgbWFuYWdlZCBhcyBhIHNpbmdsZXRvbiBieSB0aGUgREkgY29udGFpbmVyLlxuICogSW5qZWN0IGl0IHZpYSBjb25zdHJ1Y3RvciBpbmplY3Rpb24gcmF0aGVyIHRoYW4gdXNpbmcgYSBzdGF0aWMgZ2V0SW5zdGFuY2UoKSBtZXRob2QuXG4gKi9cbmV4cG9ydCBjbGFzcyBGaWxlV2F0Y2hTZXJ2aWNlIHtcbiAgcHJpdmF0ZSByZWFkb25seSB3YXRjaGVycyA9IG5ldyBNYXA8c3RyaW5nLCBXYXRjaEVudHJ5PigpO1xuICBwcml2YXRlIHJlYWRvbmx5IGRlYm91bmNlVGltZXJzID0gbmV3IE1hcDxzdHJpbmcsIFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+PigpO1xuXG4gIC8qKlxuICAgKiBXYXRjaCBhIGRpcmVjdG9yeSBmb3IgY2hhbmdlcy4gSWYgdGhlIGRpcmVjdG9yeSBkb2Vzbid0IGV4aXN0LCBpdCB3aWxsIGJlIGNyZWF0ZWQuXG4gICAqXG4gICAqIEBwYXJhbSBkaXJQYXRoIC0gUGF0aCB0byBkaXJlY3RvcnkgdG8gd2F0Y2hcbiAgICogQHBhcmFtIGhhbmRsZXIgLSBDYWxsYmFjayBmdW5jdGlvbiBpbnZva2VkIHdoZW4gZmlsZXMgY2hhbmdlXG4gICAqIEByZXR1cm5zIENsZWFudXAgZnVuY3Rpb24gdG8gc3RvcCB3YXRjaGluZ1xuICAgKlxuICAgKiBAcmVtYXJrc1xuICAgKiBUaGlzIG1ldGhvZCBlbnN1cmVzIHRoZSBkaXJlY3RvcnkgZXhpc3RzIGJlZm9yZSBzZXR0aW5nIHVwIHRoZSB3YXRjaGVyLlxuICAgKiBmcy53YXRjaCB0aHJvd3MgRU5PRU5UIGlmIHRoZSBkaXJlY3RvcnkgZG9lc24ndCBleGlzdCwgc28gd2UgY3JlYXRlIGl0XG4gICAqIHdpdGggcmVjdXJzaXZlOnRydWUgdG8gaGFuZGxlIHRlc3Qgc2NlbmFyaW9zIGFuZCBlZGdlIGNhc2VzIGdyYWNlZnVsbHkuXG4gICAqXG4gICAqIEZpbGUgd2F0Y2hpbmcgaXMgb3B0aW9uYWwgZnVuY3Rpb25hbGl0eS4gSWYgZnMud2F0Y2goKSBmYWlscyBkdWUgdG8gcGVybWlzc2lvblxuICAgKiByZXN0cmljdGlvbnMgb3IgcGxhdGZvcm0gbGltaXRhdGlvbnMsIGEgbm8tb3AgY2xlYW51cCBmdW5jdGlvbiBpcyByZXR1cm5lZCBhbmRcbiAgICogdGhlIGFwcGxpY2F0aW9uIGNvbnRpbnVlcyB3aXRob3V0IGZpbGUgd2F0Y2hpbmcgZm9yIHRoYXQgZGlyZWN0b3J5LlxuICAgKi9cbiAgd2F0Y2hEaXJlY3RvcnkoZGlyUGF0aDogc3RyaW5nLCBoYW5kbGVyOiBGaWxlQ2hhbmdlSGFuZGxlcik6ICgpID0+IHZvaWQge1xuICAgIGNvbnN0IGFic29sdXRlRGlyID0gcGF0aC5yZXNvbHZlKGRpclBhdGgpO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gdGhpcy53YXRjaGVycy5nZXQoYWJzb2x1dGVEaXIpO1xuXG4gICAgaWYgKGV4aXN0aW5nKSB7XG4gICAgICBleGlzdGluZy5oYW5kbGVycy5hZGQoaGFuZGxlcik7XG4gICAgICByZXR1cm4gKCkgPT4gdGhpcy5kZXRhY2hIYW5kbGVyKGFic29sdXRlRGlyLCBoYW5kbGVyKTtcbiAgICB9XG5cbiAgICAvLyBFbnN1cmUgZGlyZWN0b3J5IGV4aXN0cyBiZWZvcmUgd2F0Y2hpbmdcbiAgICAvLyBmcy53YXRjaCB0aHJvd3MgRU5PRU5UIGlmIGRpcmVjdG9yeSBkb2Vzbid0IGV4aXN0LCBzbyB3ZSBjcmVhdGUgaXQgZmlyc3RcbiAgICB0cnkge1xuICAgICAgYWNjZXNzU3luYyhhYnNvbHV0ZURpcik7XG4gICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICAvLyBEaXJlY3RvcnkgZG9lc24ndCBleGlzdCAtIGNyZWF0ZSBpdFxuICAgICAgdHJ5IHtcbiAgICAgICAgbWtkaXJTeW5jKGFic29sdXRlRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKCdDcmVhdGVkIGRpcmVjdG9yeSBmb3Igd2F0Y2hpbmcnLCB7IGRpcmVjdG9yeTogYWJzb2x1dGVEaXIgfSk7XG4gICAgICB9IGNhdGNoIChta2RpckVycm9yKSB7XG4gICAgICAgIC8vIEZhaWxlZCB0byBjcmVhdGUgZGlyZWN0b3J5IC0gbG9nIGFuZCByZXR1cm4gbm8tb3AgY2xlYW51cFxuICAgICAgICBsb2dnZXIud2FybignRmFpbGVkIHRvIGNyZWF0ZSBkaXJlY3RvcnkgZm9yIHdhdGNoaW5nIC0gd2F0Y2hlciB3aWxsIG5vdCBiZSBzZXQgdXAnLCB7XG4gICAgICAgICAgZGlyZWN0b3J5OiBhYnNvbHV0ZURpcixcbiAgICAgICAgICBlcnJvcjogbWtkaXJFcnJvciBpbnN0YW5jZW9mIEVycm9yID8gbWtkaXJFcnJvci5tZXNzYWdlIDogU3RyaW5nKG1rZGlyRXJyb3IpXG4gICAgICAgIH0pO1xuICAgICAgICAvLyBSZXR1cm4gYSBuby1vcCBjbGVhbnVwIGZ1bmN0aW9uIHNpbmNlIHdhdGNoZXIgd2FzIG5vdCBjcmVhdGVkXG4gICAgICAgIHJldHVybiAoKSA9PiB7fTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBoYW5kbGVycyA9IG5ldyBTZXQ8RmlsZUNoYW5nZUhhbmRsZXI+KFtoYW5kbGVyXSk7XG5cbiAgICBjb25zdCBzdGFydFBvbGxpbmdXYXRjaGVyID0gKHJlYXNvbjogeyBlcnJvcjogdW5rbm93bjsgcGhhc2U6ICdzZXR1cCcgfCAncnVudGltZScgfSB8IHVuZGVmaW5lZCk6IFBvbGxpbmdXYXRjaGVyIHwgdW5kZWZpbmVkID0+IHtcbiAgICAgIGNvbnN0IHNuYXBzaG90ID0gKCk6IE1hcDxzdHJpbmcsIG51bWJlcj4gPT4ge1xuICAgICAgICBjb25zdCBuZXh0ID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTtcbiAgICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiByZWFkZGlyU3luYyhhYnNvbHV0ZURpciwgeyB3aXRoRmlsZVR5cGVzOiB0cnVlIH0pKSB7XG4gICAgICAgICAgaWYgKCFlbnRyeS5pc0ZpbGUoKSkge1xuICAgICAgICAgICAgY29udGludWU7XG4gICAgICAgICAgfVxuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBzdGF0cyA9IHN0YXRTeW5jKHBhdGguam9pbihhYnNvbHV0ZURpciwgZW50cnkubmFtZSkpO1xuICAgICAgICAgICAgbmV4dC5zZXQoZW50cnkubmFtZSwgc3RhdHMubXRpbWVNcyk7XG4gICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAvLyBJZ25vcmUgcmFjZXMgKGZpbGUgcmVtb3ZlZCBiZXR3ZWVuIHJlYWRkaXIgYW5kIHN0YXQsIGV0Yy4pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXh0O1xuICAgICAgfTtcblxuICAgICAgbGV0IHByZXZpb3VzOiBNYXA8c3RyaW5nLCBudW1iZXI+O1xuICAgICAgdHJ5IHtcbiAgICAgICAgcHJldmlvdXMgPSBzbmFwc2hvdCgpO1xuICAgICAgfSBjYXRjaCAocG9sbFNldHVwRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ0ZhaWxlZCB0byBpbml0aWFsaXplIHBvbGxpbmcgZGlyZWN0b3J5IHdhdGNoZXInLCB7XG4gICAgICAgICAgZGlyZWN0b3J5OiBhYnNvbHV0ZURpcixcbiAgICAgICAgICBwaGFzZTogcmVhc29uPy5waGFzZSxcbiAgICAgICAgICBvcmlnaW5hbEVycm9yOiByZWFzb24/LmVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyByZWFzb24uZXJyb3IubWVzc2FnZSA6IHJlYXNvbj8uZXJyb3IsXG4gICAgICAgICAgZXJyb3I6IHBvbGxTZXR1cEVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBwb2xsU2V0dXBFcnJvci5tZXNzYWdlIDogU3RyaW5nKHBvbGxTZXR1cEVycm9yKSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIC8vIEFkYXB0aXZlIHBvbGxpbmcgaW50ZXJ2YWw6IHNjYWxlIHdpdGggZGlyZWN0b3J5IHNpemUgdG8gcHJldmVudFxuICAgICAgLy8gc2Nhbi1vdmVybGFwcyBvbiBsYXJnZSBwb3J0Zm9saW9zIChJc3N1ZSAjMTY4NykuXG4gICAgICAvLyBCYXNlOiAycywgKzJtcyBwZXIgZmlsZSwgY2FwcGVkIGF0IDEwcy5cbiAgICAgIGNvbnN0IGJhc2VJbnRlcnZhbE1zID0gMjAwMDtcbiAgICAgIGNvbnN0IHBlckZpbGVNcyA9IDI7XG4gICAgICBjb25zdCBtYXhJbnRlcnZhbE1zID0gMTBfMDAwO1xuICAgICAgY29uc3QgYWRhcHRpdmVJbnRlcnZhbCA9IE1hdGgubWluKFxuICAgICAgICBiYXNlSW50ZXJ2YWxNcyArIHByZXZpb3VzLnNpemUgKiBwZXJGaWxlTXMsXG4gICAgICAgIG1heEludGVydmFsTXMsXG4gICAgICApO1xuXG4gICAgICAvLyBHdWFyZDogc2tpcCBwb2xsIGlmIGEgcHJldmlvdXMgc2NhbiBpcyBzdGlsbCBiZWluZyBwcm9jZXNzZWRcbiAgICAgIGxldCBzY2FuSW5Qcm9ncmVzcyA9IGZhbHNlO1xuXG4gICAgICAvLyBEZWJvdW5jZTogYmF0Y2ggY2hhbmdlZCBmaWxlcyBhbmQgbm90aWZ5IGhhbmRsZXJzIG9uY2UgcGVyIGN5Y2xlXG4gICAgICBjb25zdCBpbnRlcnZhbCA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgaWYgKHNjYW5JblByb2dyZXNzKSByZXR1cm47XG4gICAgICAgIHNjYW5JblByb2dyZXNzID0gdHJ1ZTtcblxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxldCBuZXh0OiBNYXA8c3RyaW5nLCBudW1iZXI+O1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBuZXh0ID0gc25hcHNob3QoKTtcbiAgICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDb2xsZWN0IGFsbCBjaGFuZ2VkIGZpbGVzIGJlZm9yZSBub3RpZnlpbmcgaGFuZGxlcnNcbiAgICAgICAgICBjb25zdCBjaGFuZ2VkRmlsZXM6IHN0cmluZ1tdID0gW107XG5cbiAgICAgICAgICBmb3IgKGNvbnN0IFtmaWxlbmFtZSwgbXRpbWVNc10gb2YgbmV4dCkge1xuICAgICAgICAgICAgY29uc3QgcHJpb3JNdGltZSA9IHByZXZpb3VzLmdldChmaWxlbmFtZSk7XG4gICAgICAgICAgICBpZiAocHJpb3JNdGltZSA9PT0gdW5kZWZpbmVkIHx8IHByaW9yTXRpbWUgIT09IG10aW1lTXMpIHtcbiAgICAgICAgICAgICAgY2hhbmdlZEZpbGVzLnB1c2goZmlsZW5hbWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIERlbGV0aW9uc1xuICAgICAgICAgIGZvciAoY29uc3QgZmlsZW5hbWUgb2YgcHJldmlvdXMua2V5cygpKSB7XG4gICAgICAgICAgICBpZiAoIW5leHQuaGFzKGZpbGVuYW1lKSkge1xuICAgICAgICAgICAgICBjaGFuZ2VkRmlsZXMucHVzaChmaWxlbmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTm90aWZ5IGhhbmRsZXJzIG9uY2UgcGVyIGNoYW5nZWQgZmlsZSAoZGVkdXBsaWNhdGVkKVxuICAgICAgICAgIGlmIChjaGFuZ2VkRmlsZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgY29uc3QgdW5pcXVlQ2hhbmdlcyA9IFsuLi5uZXcgU2V0KGNoYW5nZWRGaWxlcyldO1xuICAgICAgICAgICAgZm9yIChjb25zdCBmaWxlbmFtZSBvZiB1bmlxdWVDaGFuZ2VzKSB7XG4gICAgICAgICAgICAgIGZvciAoY29uc3QgaCBvZiBoYW5kbGVycykge1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICBoKGZpbGVuYW1lKTtcbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ0ZpbGVXYXRjaFNlcnZpY2UgcG9sbGluZyBoYW5kbGVyIGZhaWxlZCcsIHtcbiAgICAgICAgICAgICAgICAgICAgZGlyZWN0b3J5OiBhYnNvbHV0ZURpcixcbiAgICAgICAgICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogZXJyb3JcbiAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHByZXZpb3VzID0gbmV4dDtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICBzY2FuSW5Qcm9ncmVzcyA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9LCBhZGFwdGl2ZUludGVydmFsKTtcblxuICAgICAgbG9nZ2VyLndhcm4oJ0ZhbGxpbmcgYmFjayB0byBwb2xsaW5nIGRpcmVjdG9yeSB3YXRjaGVyJywge1xuICAgICAgICBkaXJlY3Rvcnk6IGFic29sdXRlRGlyLFxuICAgICAgICBwaGFzZTogcmVhc29uPy5waGFzZSxcbiAgICAgICAgb3JpZ2luYWxFcnJvcjogcmVhc29uPy5lcnJvciBpbnN0YW5jZW9mIEVycm9yID8gcmVhc29uLmVycm9yLm1lc3NhZ2UgOiByZWFzb24/LmVycm9yLFxuICAgICAgICBjb2RlOiByZWFzb24/LmVycm9yICYmIGlzTm9kZUVycm9yKHJlYXNvbi5lcnJvcikgPyByZWFzb24uZXJyb3IuY29kZSA6IHVuZGVmaW5lZFxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiBuZXcgUG9sbGluZ1dhdGNoZXIoaW50ZXJ2YWwpO1xuICAgIH07XG5cbiAgICAvLyBUcnkgdG8gc2V0IHVwIGZpbGUgd2F0Y2hlciAtIG1heSBmYWlsIGR1ZSB0byBwZXJtaXNzaW9uIHJlc3RyaWN0aW9ucyBvciBwbGF0Zm9ybSBsaW1pdGF0aW9uc1xuICAgIGxldCB3YXRjaGVyOiBGU1dhdGNoZXIgfCBQb2xsaW5nV2F0Y2hlciB8IHVuZGVmaW5lZDtcbiAgICB0cnkge1xuICAgICAgLy8gRGVib3VuY2UgZnMud2F0Y2ggZXZlbnRzIOKAlCBjb2FsZXNjZSByYXBpZC1maXJlIG5vdGlmaWNhdGlvbnMgaW50b1xuICAgICAgLy8gYSBzaW5nbGUgaGFuZGxlciBjYWxsIHBlciBmaWxlIHdpdGhpbiBhIDUwMG1zIHdpbmRvdyAoSXNzdWUgIzE2ODcpLlxuICAgICAgY29uc3QgcGVuZGluZ0NoYW5nZXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcblxuICAgICAgd2F0Y2hlciA9IHdhdGNoKGFic29sdXRlRGlyLCB7IHJlY3Vyc2l2ZTogZmFsc2UgfSwgKF9ldmVudFR5cGUsIGZpbGVuYW1lKSA9PiB7XG4gICAgICAgIGlmICghZmlsZW5hbWUpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgcGVuZGluZ0NoYW5nZXMuYWRkKGZpbGVuYW1lLnRvU3RyaW5nKCkpO1xuXG4gICAgICAgIGNvbnN0IGV4aXN0aW5nVGltZXIgPSB0aGlzLmRlYm91bmNlVGltZXJzLmdldChhYnNvbHV0ZURpcik7XG4gICAgICAgIGlmIChleGlzdGluZ1RpbWVyKSBjbGVhclRpbWVvdXQoZXhpc3RpbmdUaW1lcik7XG4gICAgICAgIHRoaXMuZGVib3VuY2VUaW1lcnMuc2V0KGFic29sdXRlRGlyLCBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICB0aGlzLmRlYm91bmNlVGltZXJzLmRlbGV0ZShhYnNvbHV0ZURpcik7XG4gICAgICAgICAgY29uc3QgZmlsZXMgPSBbLi4ucGVuZGluZ0NoYW5nZXNdO1xuICAgICAgICAgIHBlbmRpbmdDaGFuZ2VzLmNsZWFyKCk7XG4gICAgICAgICAgZm9yIChjb25zdCByZWxhdGl2ZSBvZiBmaWxlcykge1xuICAgICAgICAgICAgZm9yIChjb25zdCBoIG9mIGhhbmRsZXJzKSB7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgaChyZWxhdGl2ZSk7XG4gICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ0ZpbGVXYXRjaFNlcnZpY2UgaGFuZGxlciBmYWlsZWQnLCB7XG4gICAgICAgICAgICAgICAgICBkaXJlY3Rvcnk6IGFic29sdXRlRGlyLFxuICAgICAgICAgICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogZXJyb3JcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSwgNTAwKSk7XG4gICAgICB9KTtcbiAgICB9IGNhdGNoICh3YXRjaEVycm9yKSB7XG4gICAgICAvLyBGaWxlIHdhdGNoaW5nIGZhaWxlZCAtIGZhbGwgYmFjayB0byBwb2xsaW5nIChvciBuby1vcCBpZiBwb2xsaW5nIGFsc28gZmFpbHMpLlxuICAgICAgLy8gQ29tbW9uIG9uIHBsYXRmb3JtcyB3aXRoIHdhdGNoIGxpbWl0cyAoRU1GSUxFL0VOT1NQQykgb3IgcmVzdHJpY3RlZCBwZXJtaXNzaW9ucy5cbiAgICAgIGxvZ2dlci53YXJuKCdGYWlsZWQgdG8gc2V0IHVwIGZpbGUgd2F0Y2hlcicsIHtcbiAgICAgICAgZGlyZWN0b3J5OiBhYnNvbHV0ZURpcixcbiAgICAgICAgZXJyb3I6IHdhdGNoRXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IHdhdGNoRXJyb3IubWVzc2FnZSA6IFN0cmluZyh3YXRjaEVycm9yKSxcbiAgICAgICAgY29kZTogaXNOb2RlRXJyb3Iod2F0Y2hFcnJvcikgPyB3YXRjaEVycm9yLmNvZGUgOiB1bmRlZmluZWRcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBwb2xsaW5nV2F0Y2hlciA9IHN0YXJ0UG9sbGluZ1dhdGNoZXIoeyBlcnJvcjogd2F0Y2hFcnJvciwgcGhhc2U6ICdzZXR1cCcgfSk7XG4gICAgICBpZiAoIXBvbGxpbmdXYXRjaGVyKSB7XG4gICAgICAgIC8vIFJldHVybiBhIG5vLW9wIGNsZWFudXAgZnVuY3Rpb24gc2luY2Ugd2F0Y2hlciB3YXMgbm90IGNyZWF0ZWRcbiAgICAgICAgcmV0dXJuICgpID0+IHt9O1xuICAgICAgfVxuXG4gICAgICB0aGlzLndhdGNoZXJzLnNldChhYnNvbHV0ZURpciwgeyB3YXRjaGVyOiBwb2xsaW5nV2F0Y2hlciwgaGFuZGxlcnMgfSk7XG4gICAgICByZXR1cm4gKCkgPT4gdGhpcy5kZXRhY2hIYW5kbGVyKGFic29sdXRlRGlyLCBoYW5kbGVyKTtcbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgZXJyb3IgZXZlbnRzIGZyb20gdGhlIHdhdGNoZXIgKHJlcXVpcmVkIGZvciBXaW5kb3dzKVxuICAgIC8vIE9uIFdpbmRvd3MsIGZzLndhdGNoIGVtaXRzIGVycm9yIGV2ZW50cyBmb3IgcGVybWlzc2lvbiBpc3N1ZXMgKEVQRVJNKVxuICAgIC8vIFdpdGhvdXQgdGhpcyBoYW5kbGVyLCBOb2RlLmpzIHRocm93cyBcIlVuaGFuZGxlZCAnZXJyb3InIGV2ZW50XCJcbiAgICBsZXQgZWFybHlXYXRjaEVycm9yOiB1bmtub3duIHwgdW5kZWZpbmVkO1xuICAgIHdhdGNoZXIub24oJ2Vycm9yJywgKGVycm9yKSA9PiB7XG4gICAgICAvLyBJZiB0aGUgd2F0Y2hlciBlcnJvcnMgaW1tZWRpYXRlbHkgKGJlZm9yZSB3ZSBzdG9yZSBpdCksIHJlY29yZCBhbmQgaGFuZGxlIGFmdGVyIHNldHVwLlxuICAgICAgaWYgKCF0aGlzLndhdGNoZXJzLmhhcyhhYnNvbHV0ZURpcikpIHtcbiAgICAgICAgZWFybHlXYXRjaEVycm9yID0gZXJyb3I7XG4gICAgICAgIHdhdGNoZXIuY2xvc2UoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBsb2dnZXIud2FybignRmlsZSB3YXRjaGVyIGVycm9yIC0gY2xvc2luZyB3YXRjaGVyJywge1xuICAgICAgICBkaXJlY3Rvcnk6IGFic29sdXRlRGlyLFxuICAgICAgICBlcnJvcjogZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpLFxuICAgICAgICBjb2RlOiBpc05vZGVFcnJvcihlcnJvcikgPyBlcnJvci5jb2RlIDogdW5kZWZpbmVkXG4gICAgICB9KTtcblxuICAgICAgLy8gQ2xvc2UgdGhlIHdhdGNoZXIgYXMgcmVjb21tZW5kZWQgaW4gTm9kZS5qcyBkb2N1bWVudGF0aW9uLlxuICAgICAgLy8gSWYgdGhlIHdhdGNoZXIgaXMgZmFpbGluZyAoZS5nLiwgRU1GSUxFL0VOT1NQQyksIGZhbGwgYmFjayB0byBwb2xsaW5nIHNvIHRoZSBhcHAgY2FuIHN0aWxsIHJlYWN0LlxuICAgICAgY29uc3QgY3VycmVudCA9IHRoaXMud2F0Y2hlcnMuZ2V0KGFic29sdXRlRGlyKTtcbiAgICAgIGNvbnN0IGlzQ3VycmVudFdhdGNoZXIgPSBjdXJyZW50Py53YXRjaGVyID09PSB3YXRjaGVyO1xuXG4gICAgICB3YXRjaGVyLmNsb3NlKCk7XG5cbiAgICAgIGlmIChpc0N1cnJlbnRXYXRjaGVyKSB7XG4gICAgICAgIGNvbnN0IHBvbGxpbmdXYXRjaGVyID0gc3RhcnRQb2xsaW5nV2F0Y2hlcih7IGVycm9yLCBwaGFzZTogJ3J1bnRpbWUnIH0pO1xuICAgICAgICBpZiAocG9sbGluZ1dhdGNoZXIpIHtcbiAgICAgICAgICB0aGlzLndhdGNoZXJzLnNldChhYnNvbHV0ZURpciwgeyB3YXRjaGVyOiBwb2xsaW5nV2F0Y2hlciwgaGFuZGxlcnMgfSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhpcy53YXRjaGVycy5kZWxldGUoYWJzb2x1dGVEaXIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICB0aGlzLndhdGNoZXJzLnNldChhYnNvbHV0ZURpciwgeyB3YXRjaGVyLCBoYW5kbGVycyB9KTtcbiAgICBsb2dnZXIuZGVidWcoJ1N0YXJ0ZWQgZGlyZWN0b3J5IHdhdGNoZXInLCB7IGRpcmVjdG9yeTogYWJzb2x1dGVEaXIgfSk7XG5cbiAgICBpZiAoZWFybHlXYXRjaEVycm9yKSB7XG4gICAgICBsb2dnZXIud2FybignRmlsZSB3YXRjaGVyIGVycm9yIGR1cmluZyBpbml0aWFsaXphdGlvbiAtIHN3aXRjaGluZyB0byBwb2xsaW5nJywge1xuICAgICAgICBkaXJlY3Rvcnk6IGFic29sdXRlRGlyLFxuICAgICAgICBlcnJvcjogZWFybHlXYXRjaEVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlYXJseVdhdGNoRXJyb3IubWVzc2FnZSA6IFN0cmluZyhlYXJseVdhdGNoRXJyb3IpLFxuICAgICAgICBjb2RlOiBpc05vZGVFcnJvcihlYXJseVdhdGNoRXJyb3IpID8gZWFybHlXYXRjaEVycm9yLmNvZGUgOiB1bmRlZmluZWRcbiAgICAgIH0pO1xuICAgICAgY29uc3QgcG9sbGluZ1dhdGNoZXIgPSBzdGFydFBvbGxpbmdXYXRjaGVyKHsgZXJyb3I6IGVhcmx5V2F0Y2hFcnJvciwgcGhhc2U6ICdydW50aW1lJyB9KTtcbiAgICAgIGlmIChwb2xsaW5nV2F0Y2hlcikge1xuICAgICAgICB0aGlzLndhdGNoZXJzLnNldChhYnNvbHV0ZURpciwgeyB3YXRjaGVyOiBwb2xsaW5nV2F0Y2hlciwgaGFuZGxlcnMgfSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLndhdGNoZXJzLmRlbGV0ZShhYnNvbHV0ZURpcik7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuICgpID0+IHRoaXMuZGV0YWNoSGFuZGxlcihhYnNvbHV0ZURpciwgaGFuZGxlcik7XG4gIH1cblxuICBwcml2YXRlIGRldGFjaEhhbmRsZXIoZGlyOiBzdHJpbmcsIGhhbmRsZXI6IEZpbGVDaGFuZ2VIYW5kbGVyKTogdm9pZCB7XG4gICAgY29uc3QgZW50cnkgPSB0aGlzLndhdGNoZXJzLmdldChkaXIpO1xuICAgIGlmICghZW50cnkpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgZW50cnkuaGFuZGxlcnMuZGVsZXRlKGhhbmRsZXIpO1xuICAgIGlmIChlbnRyeS5oYW5kbGVycy5zaXplID09PSAwKSB7XG4gICAgICBlbnRyeS53YXRjaGVyLmNsb3NlKCk7XG4gICAgICB0aGlzLndhdGNoZXJzLmRlbGV0ZShkaXIpO1xuICAgICAgbG9nZ2VyLmRlYnVnKCdTdG9wcGVkIGRpcmVjdG9yeSB3YXRjaGVyJywgeyBkaXJlY3Rvcnk6IGRpciB9KTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSBvZiBhbGwgZmlsZSB3YXRjaGVycy4gQ2FsbGVkIGR1cmluZyBncmFjZWZ1bCBzaHV0ZG93bi5cbiAgICogVGhpcyBtZXRob2QgaXMgaW52b2tlZCBieSB0aGUgREkgY29udGFpbmVyJ3MgZGlzcG9zZSgpIG1ldGhvZC5cbiAgICovXG4gIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgLy8gQ2xlYXIgYWxsIHBlbmRpbmcgZGVib3VuY2UgdGltZXJzIHRvIHByZXZlbnQgcG9zdC1kaXNwb3NlIGhhbmRsZXIgY2FsbHNcbiAgICBmb3IgKGNvbnN0IFssIHRpbWVyXSBvZiB0aGlzLmRlYm91bmNlVGltZXJzKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGltZXIpO1xuICAgIH1cbiAgICB0aGlzLmRlYm91bmNlVGltZXJzLmNsZWFyKCk7XG5cbiAgICBmb3IgKGNvbnN0IFtkaXIsIGVudHJ5XSBvZiB0aGlzLndhdGNoZXJzKSB7XG4gICAgICB0cnkge1xuICAgICAgICBlbnRyeS53YXRjaGVyLmNsb3NlKCk7XG4gICAgICAgIGxvZ2dlci5kZWJ1ZygnQ2xvc2VkIGRpcmVjdG9yeSB3YXRjaGVyIGR1cmluZyBkaXNwb3NlJywgeyBkaXJlY3Rvcnk6IGRpciB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdFcnJvciBjbG9zaW5nIHdhdGNoZXIgZHVyaW5nIGRpc3Bvc2UnLCB7XG4gICAgICAgICAgZGlyZWN0b3J5OiBkaXIsXG4gICAgICAgICAgZXJyb3I6IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy53YXRjaGVycy5jbGVhcigpO1xuICAgIGxvZ2dlci5kZWJ1ZygnRmlsZVdhdGNoU2VydmljZSBkaXNwb3NlZCcsIHsgd2F0Y2hlcnNDbG9zZWQ6IHRoaXMud2F0Y2hlcnMuc2l6ZSA9PT0gMCB9KTtcbiAgfVxufVxuIl19