UNPKG

llmverify

Version:

AI Output Verification Toolkit — Local-first LLM safety, hallucination detection, PII redaction, prompt injection defense, and runtime monitoring. Zero telemetry. OWASP LLM Top 10 aligned.

386 lines 37.9 kB
"use strict"; /** * Structured Logging System * * Industry-grade logging with rotation, request tracking, and audit trails * * @module logging/logger */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Logger = exports.LogLevel = void 0; exports.getLogger = getLogger; exports.setLogger = setLogger; exports.resetLogger = resetLogger; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const os = __importStar(require("os")); const uuid_1 = require("uuid"); /** * Log levels */ var LogLevel; (function (LogLevel) { LogLevel["DEBUG"] = "debug"; LogLevel["INFO"] = "info"; LogLevel["WARN"] = "warn"; LogLevel["ERROR"] = "error"; })(LogLevel || (exports.LogLevel = LogLevel = {})); /** * Default configuration */ const DEFAULT_CONFIG = { enabled: true, level: LogLevel.INFO, logDir: path.join(os.homedir(), '.llmverify', 'logs'), maxFileSize: 10 * 1024 * 1024, // 10MB maxFiles: 10, includeMetadata: true, sanitizePII: true }; /** * Logger class */ class Logger { constructor(config) { this.currentRequestId = null; this.requestStartTime = null; this.config = { ...DEFAULT_CONFIG, ...config }; this.ensureLogDirectory(); } /** * Ensure log directory exists */ ensureLogDirectory() { if (this.config.enabled && this.config.logDir) { try { fs.mkdirSync(this.config.logDir, { recursive: true }); } catch (error) { console.error('Failed to create log directory:', error); this.config.enabled = false; } } } /** * Generate new request ID */ startRequest() { this.currentRequestId = (0, uuid_1.v4)(); this.requestStartTime = Date.now(); return this.currentRequestId; } /** * End request and return duration */ endRequest() { if (this.requestStartTime) { const duration = Date.now() - this.requestStartTime; this.requestStartTime = null; return duration; } return null; } /** * Get current request ID */ getRequestId() { if (!this.currentRequestId) { this.currentRequestId = (0, uuid_1.v4)(); } return this.currentRequestId; } /** * Get log file path for today */ getLogFilePath() { const date = new Date().toISOString().split('T')[0]; return path.join(this.config.logDir, `llmverify-${date}.jsonl`); } /** * Sanitize data to remove PII */ sanitizeData(data) { if (!this.config.sanitizePII) return data; if (typeof data === 'string') { // Remove email addresses data = data.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g, '[EMAIL]'); // Remove phone numbers data = data.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, '[PHONE]'); // Remove SSN data = data.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '[SSN]'); // Remove API keys (common patterns) data = data.replace(/\b[A-Za-z0-9]{32,}\b/g, '[API_KEY]'); } else if (typeof data === 'object' && data !== null) { const sanitized = Array.isArray(data) ? [] : {}; for (const key in data) { // Skip sensitive keys if (['password', 'apiKey', 'token', 'secret', 'authorization'].includes(key.toLowerCase())) { sanitized[key] = '[REDACTED]'; } else { sanitized[key] = this.sanitizeData(data[key]); } } return sanitized; } return data; } /** * Write log entry */ writeLog(entry) { if (!this.config.enabled) return; try { const logFile = this.getLogFilePath(); const line = JSON.stringify(entry) + '\n'; fs.appendFileSync(logFile, line, 'utf-8'); // Check file size and rotate if needed this.rotateLogsIfNeeded(logFile); } catch (error) { // Fail silently to not disrupt main operation if (process.env.NODE_ENV === 'development') { console.error('Failed to write log:', error); } } } /** * Rotate logs if file exceeds max size */ rotateLogsIfNeeded(logFile) { try { const stats = fs.statSync(logFile); if (stats.size > this.config.maxFileSize) { const timestamp = Date.now(); const rotatedFile = logFile.replace('.jsonl', `.${timestamp}.jsonl`); fs.renameSync(logFile, rotatedFile); // Clean up old files this.cleanupOldLogs(); } } catch (error) { // Ignore rotation errors } } /** * Clean up old log files */ cleanupOldLogs() { if (!this.config.logDir) return; try { const files = fs.readdirSync(this.config.logDir) .filter(f => f.startsWith('llmverify-') && f.endsWith('.jsonl')) .map(f => ({ name: f, path: path.join(this.config.logDir, f), time: fs.statSync(path.join(this.config.logDir, f)).mtime.getTime() })) .sort((a, b) => b.time - a.time); // Keep only maxFiles if (files.length > this.config.maxFiles) { files.slice(this.config.maxFiles).forEach(file => { try { fs.unlinkSync(file.path); } catch (error) { // Ignore deletion errors } }); } } catch (error) { // Ignore cleanup errors } } /** * Create log entry */ createEntry(level, message, data, error) { const entry = { timestamp: new Date().toISOString(), level, requestId: this.getRequestId(), message: this.config.sanitizePII ? this.sanitizeData(message) : message }; if (data) { entry.data = this.sanitizeData(data); } if (this.requestStartTime) { entry.duration = Date.now() - this.requestStartTime; } if (error) { entry.error = { message: error.message, code: error.code, stack: process.env.NODE_ENV === 'development' ? error.stack : undefined }; } if (this.config.includeMetadata) { entry.metadata = { version: require('../../package.json').version, pid: process.pid, hostname: os.hostname() }; } return entry; } /** * Check if should log at level */ shouldLog(level) { const levels = [LogLevel.DEBUG, LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR]; const configLevelIndex = levels.indexOf(this.config.level); const messageLevelIndex = levels.indexOf(level); return messageLevelIndex >= configLevelIndex; } /** * Log debug message */ debug(message, data) { if (this.shouldLog(LogLevel.DEBUG)) { this.writeLog(this.createEntry(LogLevel.DEBUG, message, data)); } } /** * Log info message */ info(message, data) { if (this.shouldLog(LogLevel.INFO)) { this.writeLog(this.createEntry(LogLevel.INFO, message, data)); } } /** * Log warning message */ warn(message, data) { if (this.shouldLog(LogLevel.WARN)) { this.writeLog(this.createEntry(LogLevel.WARN, message, data)); } } /** * Log error message */ error(message, error, data) { if (this.shouldLog(LogLevel.ERROR)) { this.writeLog(this.createEntry(LogLevel.ERROR, message, data, error)); } } /** * Read logs for a specific date */ readLogs(date) { if (!this.config.logDir) return []; const dateStr = date || new Date().toISOString().split('T')[0]; const logFile = path.join(this.config.logDir, `llmverify-${dateStr}.jsonl`); if (!fs.existsSync(logFile)) return []; try { const content = fs.readFileSync(logFile, 'utf-8'); return content .split('\n') .filter(line => line.trim()) .map(line => JSON.parse(line)); } catch (error) { console.error('Failed to read logs:', error); return []; } } /** * Get log statistics */ getStats(date) { const logs = this.readLogs(date); const stats = { totalEntries: logs.length, byLevel: { [LogLevel.DEBUG]: 0, [LogLevel.INFO]: 0, [LogLevel.WARN]: 0, [LogLevel.ERROR]: 0 }, errorCount: 0, avgDuration: 0 }; let totalDuration = 0; let durationCount = 0; logs.forEach(log => { stats.byLevel[log.level]++; if (log.error) stats.errorCount++; if (log.duration) { totalDuration += log.duration; durationCount++; } }); if (durationCount > 0) { stats.avgDuration = totalDuration / durationCount; } return stats; } } exports.Logger = Logger; /** * Global logger instance */ let globalLogger = null; /** * Get global logger */ function getLogger(config) { if (!globalLogger) { globalLogger = new Logger(config); } return globalLogger; } /** * Set global logger */ function setLogger(logger) { globalLogger = logger; } /** * Reset global logger */ function resetLogger() { globalLogger = null; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xvZ2dpbmcvbG9nZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTJZSCw4QkFLQztBQUtELDhCQUVDO0FBS0Qsa0NBRUM7QUE1WkQsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3Qix1Q0FBeUI7QUFDekIsK0JBQW9DO0FBR3BDOztHQUVHO0FBQ0gsSUFBWSxRQUtYO0FBTEQsV0FBWSxRQUFRO0lBQ2xCLDJCQUFlLENBQUE7SUFDZix5QkFBYSxDQUFBO0lBQ2IseUJBQWEsQ0FBQTtJQUNiLDJCQUFlLENBQUE7QUFDakIsQ0FBQyxFQUxXLFFBQVEsd0JBQVIsUUFBUSxRQUtuQjtBQXFDRDs7R0FFRztBQUNILE1BQU0sY0FBYyxHQUFpQjtJQUNuQyxPQUFPLEVBQUUsSUFBSTtJQUNiLEtBQUssRUFBRSxRQUFRLENBQUMsSUFBSTtJQUNwQixNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQztJQUNyRCxXQUFXLEVBQUUsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLEVBQUUsT0FBTztJQUN0QyxRQUFRLEVBQUUsRUFBRTtJQUNaLGVBQWUsRUFBRSxJQUFJO0lBQ3JCLFdBQVcsRUFBRSxJQUFJO0NBQ2xCLENBQUM7QUFFRjs7R0FFRztBQUNILE1BQWEsTUFBTTtJQUtqQixZQUFZLE1BQThCO1FBSGxDLHFCQUFnQixHQUFrQixJQUFJLENBQUM7UUFDdkMscUJBQWdCLEdBQWtCLElBQUksQ0FBQztRQUc3QyxJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsR0FBRyxjQUFjLEVBQUUsR0FBRyxNQUFNLEVBQUUsQ0FBQztRQUMvQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUM1QixDQUFDO0lBRUQ7O09BRUc7SUFDSyxrQkFBa0I7UUFDeEIsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQzlDLElBQUksQ0FBQztnQkFDSCxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDeEQsQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsT0FBTyxDQUFDLEtBQUssQ0FBQyxpQ0FBaUMsRUFBRSxLQUFLLENBQUMsQ0FBQztnQkFDeEQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLEdBQUcsS0FBSyxDQUFDO1lBQzlCLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksWUFBWTtRQUNqQixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBQSxTQUFNLEdBQUUsQ0FBQztRQUNqQyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ25DLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDO0lBQy9CLENBQUM7SUFFRDs7T0FFRztJQUNJLFVBQVU7UUFDZixJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzFCLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7WUFDcEQsSUFBSSxDQUFDLGdCQUFnQixHQUFHLElBQUksQ0FBQztZQUM3QixPQUFPLFFBQVEsQ0FBQztRQUNsQixDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUM7SUFDZCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxZQUFZO1FBQ2pCLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztZQUMzQixJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBQSxTQUFNLEdBQUUsQ0FBQztRQUNuQyxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUM7SUFDL0IsQ0FBQztJQUVEOztPQUVHO0lBQ0ssY0FBYztRQUNwQixNQUFNLElBQUksR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwRCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFPLEVBQUUsYUFBYSxJQUFJLFFBQVEsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFRDs7T0FFRztJQUNLLFlBQVksQ0FBQyxJQUFTO1FBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQztRQUUxQyxJQUFJLE9BQU8sSUFBSSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQzdCLHlCQUF5QjtZQUN6QixJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxpREFBaUQsRUFBRSxTQUFTLENBQUMsQ0FBQztZQUNsRix1QkFBdUI7WUFDdkIsSUFBSSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZ0NBQWdDLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDakUsYUFBYTtZQUNiLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ3ZELG9DQUFvQztZQUNwQyxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyx1QkFBdUIsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUM1RCxDQUFDO2FBQU0sSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksSUFBSSxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ3JELE1BQU0sU0FBUyxHQUFRLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQ3JELEtBQUssTUFBTSxHQUFHLElBQUksSUFBSSxFQUFFLENBQUM7Z0JBQ3ZCLHNCQUFzQjtnQkFDdEIsSUFBSSxDQUFDLFVBQVUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxlQUFlLENBQUMsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxDQUFDLEVBQUUsQ0FBQztvQkFDM0YsU0FBUyxDQUFDLEdBQUcsQ0FBQyxHQUFHLFlBQVksQ0FBQztnQkFDaEMsQ0FBQztxQkFBTSxDQUFDO29CQUNOLFNBQVMsQ0FBQyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNoRCxDQUFDO1lBQ0gsQ0FBQztZQUNELE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQztJQUNkLENBQUM7SUFFRDs7T0FFRztJQUNLLFFBQVEsQ0FBQyxLQUFlO1FBQzlCLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU87WUFBRSxPQUFPO1FBRWpDLElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUN0QyxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQztZQUUxQyxFQUFFLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFFMUMsdUNBQXVDO1lBQ3ZDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNuQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLDhDQUE4QztZQUM5QyxJQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxLQUFLLGFBQWEsRUFBRSxDQUFDO2dCQUMzQyxPQUFPLENBQUMsS0FBSyxDQUFDLHNCQUFzQixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQy9DLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssa0JBQWtCLENBQUMsT0FBZTtRQUN4QyxJQUFJLENBQUM7WUFDSCxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBRW5DLElBQUksS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVksRUFBRSxDQUFDO2dCQUMxQyxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7Z0JBQzdCLE1BQU0sV0FBVyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLElBQUksU0FBUyxRQUFRLENBQUMsQ0FBQztnQkFDckUsRUFBRSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUM7Z0JBRXBDLHFCQUFxQjtnQkFDckIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQ3hCLENBQUM7UUFDSCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLHlCQUF5QjtRQUMzQixDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ssY0FBYztRQUNwQixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNO1lBQUUsT0FBTztRQUVoQyxJQUFJLENBQUM7WUFDSCxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDO2lCQUM3QyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7aUJBQy9ELEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7Z0JBQ1QsSUFBSSxFQUFFLENBQUM7Z0JBQ1AsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFPLEVBQUUsQ0FBQyxDQUFDO2dCQUN2QyxJQUFJLEVBQUUsRUFBRSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsTUFBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRTthQUNyRSxDQUFDLENBQUM7aUJBQ0YsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFbkMscUJBQXFCO1lBQ3JCLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLFFBQVMsRUFBRSxDQUFDO2dCQUN6QyxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO29CQUNoRCxJQUFJLENBQUM7d0JBQ0gsRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7b0JBQzNCLENBQUM7b0JBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQzt3QkFDZix5QkFBeUI7b0JBQzNCLENBQUM7Z0JBQ0gsQ0FBQyxDQUFDLENBQUM7WUFDTCxDQUFDO1FBQ0gsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZix3QkFBd0I7UUFDMUIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLFdBQVcsQ0FBQyxLQUFlLEVBQUUsT0FBZSxFQUFFLElBQVUsRUFBRSxLQUFhO1FBQzdFLE1BQU0sS0FBSyxHQUFhO1lBQ3RCLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtZQUNuQyxLQUFLO1lBQ0wsU0FBUyxFQUFFLElBQUksQ0FBQyxZQUFZLEVBQUU7WUFDOUIsT0FBTyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPO1NBQ3hFLENBQUM7UUFFRixJQUFJLElBQUksRUFBRSxDQUFDO1lBQ1QsS0FBSyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZDLENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzFCLEtBQUssQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztRQUN0RCxDQUFDO1FBRUQsSUFBSSxLQUFLLEVBQUUsQ0FBQztZQUNWLEtBQUssQ0FBQyxLQUFLLEdBQUc7Z0JBQ1osT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPO2dCQUN0QixJQUFJLEVBQUcsS0FBYSxDQUFDLElBQUk7Z0JBQ3pCLEtBQUssRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsS0FBSyxhQUFhLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVM7YUFDeEUsQ0FBQztRQUNKLENBQUM7UUFFRCxJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsZUFBZSxFQUFFLENBQUM7WUFDaEMsS0FBSyxDQUFDLFFBQVEsR0FBRztnQkFDZixPQUFPLEVBQUUsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBQUMsT0FBTztnQkFDOUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxHQUFHO2dCQUNoQixRQUFRLEVBQUUsRUFBRSxDQUFDLFFBQVEsRUFBRTthQUN4QixDQUFDO1FBQ0osQ0FBQztRQUVELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVEOztPQUVHO0lBQ0ssU0FBUyxDQUFDLEtBQWU7UUFDL0IsTUFBTSxNQUFNLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDOUUsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0QsTUFBTSxpQkFBaUIsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRWhELE9BQU8saUJBQWlCLElBQUksZ0JBQWdCLENBQUM7SUFDL0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLE9BQWUsRUFBRSxJQUFVO1FBQ3RDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUNuQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNqRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksSUFBSSxDQUFDLE9BQWUsRUFBRSxJQUFVO1FBQ3JDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNoRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksSUFBSSxDQUFDLE9BQWUsRUFBRSxJQUFVO1FBQ3JDLElBQUksSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUNsQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNoRSxDQUFDO0lBQ0gsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLE9BQWUsRUFBRSxLQUFhLEVBQUUsSUFBVTtRQUNyRCxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDbkMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxLQUFLLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRLENBQUMsSUFBYTtRQUMzQixJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFFbkMsTUFBTSxPQUFPLEdBQUcsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQy9ELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsYUFBYSxPQUFPLFFBQVEsQ0FBQyxDQUFDO1FBRTVFLElBQUksQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQztZQUFFLE9BQU8sRUFBRSxDQUFDO1FBRXZDLElBQUksQ0FBQztZQUNILE1BQU0sT0FBTyxHQUFHLEVBQUUsQ0FBQyxZQUFZLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ2xELE9BQU8sT0FBTztpQkFDWCxLQUFLLENBQUMsSUFBSSxDQUFDO2lCQUNYLE1BQU0sQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztpQkFDM0IsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQWEsQ0FBQyxDQUFDO1FBQy9DLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUM3QyxPQUFPLEVBQUUsQ0FBQztRQUNaLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRLENBQUMsSUFBYTtRQU0zQixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBRWpDLE1BQU0sS0FBSyxHQUFHO1lBQ1osWUFBWSxFQUFFLElBQUksQ0FBQyxNQUFNO1lBQ3pCLE9BQU8sRUFBRTtnQkFDUCxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUNuQixDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2dCQUNsQixDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2dCQUNsQixDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2FBQ3BCO1lBQ0QsVUFBVSxFQUFFLENBQUM7WUFDYixXQUFXLEVBQUUsQ0FBQztTQUNmLENBQUM7UUFFRixJQUFJLGFBQWEsR0FBRyxDQUFDLENBQUM7UUFDdEIsSUFBSSxhQUFhLEdBQUcsQ0FBQyxDQUFDO1FBRXRCLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDakIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMzQixJQUFJLEdBQUcsQ0FBQyxLQUFLO2dCQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNsQyxJQUFJLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztnQkFDakIsYUFBYSxJQUFJLEdBQUcsQ0FBQyxRQUFRLENBQUM7Z0JBQzlCLGFBQWEsRUFBRSxDQUFDO1lBQ2xCLENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztRQUVILElBQUksYUFBYSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RCLEtBQUssQ0FBQyxXQUFXLEdBQUcsYUFBYSxHQUFHLGFBQWEsQ0FBQztRQUNwRCxDQUFDO1FBRUQsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0Y7QUE1VEQsd0JBNFRDO0FBRUQ7O0dBRUc7QUFDSCxJQUFJLFlBQVksR0FBa0IsSUFBSSxDQUFDO0FBRXZDOztHQUVHO0FBQ0gsU0FBZ0IsU0FBUyxDQUFDLE1BQThCO0lBQ3RELElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUNsQixZQUFZLEdBQUcsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUNELE9BQU8sWUFBWSxDQUFDO0FBQ3RCLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxNQUFjO0lBQ3RDLFlBQVksR0FBRyxNQUFNLENBQUM7QUFDeEIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsV0FBVztJQUN6QixZQUFZLEdBQUcsSUFBSSxDQUFDO0FBQ3RCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFN0cnVjdHVyZWQgTG9nZ2luZyBTeXN0ZW1cbiAqIFxuICogSW5kdXN0cnktZ3JhZGUgbG9nZ2luZyB3aXRoIHJvdGF0aW9uLCByZXF1ZXN0IHRyYWNraW5nLCBhbmQgYXVkaXQgdHJhaWxzXG4gKiBcbiAqIEBtb2R1bGUgbG9nZ2luZy9sb2dnZXJcbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0IHsgdjQgYXMgdXVpZHY0IH0gZnJvbSAndXVpZCc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi9lcnJvcnMvY29kZXMnO1xuXG4vKipcbiAqIExvZyBsZXZlbHNcbiAqL1xuZXhwb3J0IGVudW0gTG9nTGV2ZWwge1xuICBERUJVRyA9ICdkZWJ1ZycsXG4gIElORk8gPSAnaW5mbycsXG4gIFdBUk4gPSAnd2FybicsXG4gIEVSUk9SID0gJ2Vycm9yJ1xufVxuXG4vKipcbiAqIExvZyBlbnRyeSBzdHJ1Y3R1cmVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dFbnRyeSB7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICBsZXZlbDogTG9nTGV2ZWw7XG4gIHJlcXVlc3RJZDogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIGRhdGE/OiBhbnk7XG4gIGR1cmF0aW9uPzogbnVtYmVyO1xuICBlcnJvcj86IHtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgY29kZT86IEVycm9yQ29kZTtcbiAgICBzdGFjaz86IHN0cmluZztcbiAgfTtcbiAgbWV0YWRhdGE/OiB7XG4gICAgdmVyc2lvbjogc3RyaW5nO1xuICAgIHBpZDogbnVtYmVyO1xuICAgIGhvc3RuYW1lOiBzdHJpbmc7XG4gIH07XG59XG5cbi8qKlxuICogTG9nZ2VyIGNvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dnZXJDb25maWcge1xuICBlbmFibGVkOiBib29sZWFuO1xuICBsZXZlbDogTG9nTGV2ZWw7XG4gIGxvZ0Rpcj86IHN0cmluZztcbiAgbWF4RmlsZVNpemU/OiBudW1iZXI7IC8vIGJ5dGVzXG4gIG1heEZpbGVzPzogbnVtYmVyO1xuICBpbmNsdWRlTWV0YWRhdGE/OiBib29sZWFuO1xuICBzYW5pdGl6ZVBJST86IGJvb2xlYW47XG59XG5cbi8qKlxuICogRGVmYXVsdCBjb25maWd1cmF0aW9uXG4gKi9cbmNvbnN0IERFRkFVTFRfQ09ORklHOiBMb2dnZXJDb25maWcgPSB7XG4gIGVuYWJsZWQ6IHRydWUsXG4gIGxldmVsOiBMb2dMZXZlbC5JTkZPLFxuICBsb2dEaXI6IHBhdGguam9pbihvcy5ob21lZGlyKCksICcubGxtdmVyaWZ5JywgJ2xvZ3MnKSxcbiAgbWF4RmlsZVNpemU6IDEwICogMTAyNCAqIDEwMjQsIC8vIDEwTUJcbiAgbWF4RmlsZXM6IDEwLFxuICBpbmNsdWRlTWV0YWRhdGE6IHRydWUsXG4gIHNhbml0aXplUElJOiB0cnVlXG59O1xuXG4vKipcbiAqIExvZ2dlciBjbGFzc1xuICovXG5leHBvcnQgY2xhc3MgTG9nZ2VyIHtcbiAgcHJpdmF0ZSBjb25maWc6IExvZ2dlckNvbmZpZztcbiAgcHJpdmF0ZSBjdXJyZW50UmVxdWVzdElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSByZXF1ZXN0U3RhcnRUaW1lOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgXG4gIGNvbnN0cnVjdG9yKGNvbmZpZz86IFBhcnRpYWw8TG9nZ2VyQ29uZmlnPikge1xuICAgIHRoaXMuY29uZmlnID0geyAuLi5ERUZBVUxUX0NPTkZJRywgLi4uY29uZmlnIH07XG4gICAgdGhpcy5lbnN1cmVMb2dEaXJlY3RvcnkoKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEVuc3VyZSBsb2cgZGlyZWN0b3J5IGV4aXN0c1xuICAgKi9cbiAgcHJpdmF0ZSBlbnN1cmVMb2dEaXJlY3RvcnkoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmVuYWJsZWQgJiYgdGhpcy5jb25maWcubG9nRGlyKSB7XG4gICAgICB0cnkge1xuICAgICAgICBmcy5ta2RpclN5bmModGhpcy5jb25maWcubG9nRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBjcmVhdGUgbG9nIGRpcmVjdG9yeTonLCBlcnJvcik7XG4gICAgICAgIHRoaXMuY29uZmlnLmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBuZXcgcmVxdWVzdCBJRFxuICAgKi9cbiAgcHVibGljIHN0YXJ0UmVxdWVzdCgpOiBzdHJpbmcge1xuICAgIHRoaXMuY3VycmVudFJlcXVlc3RJZCA9IHV1aWR2NCgpO1xuICAgIHRoaXMucmVxdWVzdFN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJlcXVlc3RJZDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEVuZCByZXF1ZXN0IGFuZCByZXR1cm4gZHVyYXRpb25cbiAgICovXG4gIHB1YmxpYyBlbmRSZXF1ZXN0KCk6IG51bWJlciB8IG51bGwge1xuICAgIGlmICh0aGlzLnJlcXVlc3RTdGFydFRpbWUpIHtcbiAgICAgIGNvbnN0IGR1cmF0aW9uID0gRGF0ZS5ub3coKSAtIHRoaXMucmVxdWVzdFN0YXJ0VGltZTtcbiAgICAgIHRoaXMucmVxdWVzdFN0YXJ0VGltZSA9IG51bGw7XG4gICAgICByZXR1cm4gZHVyYXRpb247XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgcmVxdWVzdCBJRFxuICAgKi9cbiAgcHVibGljIGdldFJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5jdXJyZW50UmVxdWVzdElkKSB7XG4gICAgICB0aGlzLmN1cnJlbnRSZXF1ZXN0SWQgPSB1dWlkdjQoKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJlcXVlc3RJZDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBsb2cgZmlsZSBwYXRoIGZvciB0b2RheVxuICAgKi9cbiAgcHJpdmF0ZSBnZXRMb2dGaWxlUGF0aCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXTtcbiAgICByZXR1cm4gcGF0aC5qb2luKHRoaXMuY29uZmlnLmxvZ0RpciEsIGBsbG12ZXJpZnktJHtkYXRlfS5qc29ubGApO1xuICB9XG4gIFxuICAvKipcbiAgICogU2FuaXRpemUgZGF0YSB0byByZW1vdmUgUElJXG4gICAqL1xuICBwcml2YXRlIHNhbml0aXplRGF0YShkYXRhOiBhbnkpOiBhbnkge1xuICAgIGlmICghdGhpcy5jb25maWcuc2FuaXRpemVQSUkpIHJldHVybiBkYXRhO1xuICAgIFxuICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFJlbW92ZSBlbWFpbCBhZGRyZXNzZXNcbiAgICAgIGRhdGEgPSBkYXRhLnJlcGxhY2UoL1thLXpBLVowLTkuXyUrLV0rQFthLXpBLVowLTkuLV0rXFwuW2EtekEtWl17Mix9L2csICdbRU1BSUxdJyk7XG4gICAgICAvLyBSZW1vdmUgcGhvbmUgbnVtYmVyc1xuICAgICAgZGF0YSA9IGRhdGEucmVwbGFjZSgvXFxiXFxkezN9Wy0uXT9cXGR7M31bLS5dP1xcZHs0fVxcYi9nLCAnW1BIT05FXScpO1xuICAgICAgLy8gUmVtb3ZlIFNTTlxuICAgICAgZGF0YSA9IGRhdGEucmVwbGFjZSgvXFxiXFxkezN9LVxcZHsyfS1cXGR7NH1cXGIvZywgJ1tTU05dJyk7XG4gICAgICAvLyBSZW1vdmUgQVBJIGtleXMgKGNvbW1vbiBwYXR0ZXJucylcbiAgICAgIGRhdGEgPSBkYXRhLnJlcGxhY2UoL1xcYltBLVphLXowLTldezMyLH1cXGIvZywgJ1tBUElfS0VZXScpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnICYmIGRhdGEgIT09IG51bGwpIHtcbiAgICAgIGNvbnN0IHNhbml0aXplZDogYW55ID0gQXJyYXkuaXNBcnJheShkYXRhKSA/IFtdIDoge307XG4gICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhKSB7XG4gICAgICAgIC8vIFNraXAgc2Vuc2l0aXZlIGtleXNcbiAgICAgICAgaWYgKFsncGFzc3dvcmQnLCAnYXBpS2V5JywgJ3Rva2VuJywgJ3NlY3JldCcsICdhdXRob3JpemF0aW9uJ10uaW5jbHVkZXMoa2V5LnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICAgICAgc2FuaXRpemVkW2tleV0gPSAnW1JFREFDVEVEXSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2FuaXRpemVkW2tleV0gPSB0aGlzLnNhbml0aXplRGF0YShkYXRhW2tleV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gc2FuaXRpemVkO1xuICAgIH1cbiAgICBcbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFdyaXRlIGxvZyBlbnRyeVxuICAgKi9cbiAgcHJpdmF0ZSB3cml0ZUxvZyhlbnRyeTogTG9nRW50cnkpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmVuYWJsZWQpIHJldHVybjtcbiAgICBcbiAgICB0cnkge1xuICAgICAgY29uc3QgbG9nRmlsZSA9IHRoaXMuZ2V0TG9nRmlsZVBhdGgoKTtcbiAgICAgIGNvbnN0IGxpbmUgPSBKU09OLnN0cmluZ2lmeShlbnRyeSkgKyAnXFxuJztcbiAgICAgIFxuICAgICAgZnMuYXBwZW5kRmlsZVN5bmMobG9nRmlsZSwgbGluZSwgJ3V0Zi04Jyk7XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGZpbGUgc2l6ZSBhbmQgcm90YXRlIGlmIG5lZWRlZFxuICAgICAgdGhpcy5yb3RhdGVMb2dzSWZOZWVkZWQobG9nRmlsZSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEZhaWwgc2lsZW50bHkgdG8gbm90IGRpc3J1cHQgbWFpbiBvcGVyYXRpb25cbiAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50Jykge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gd3JpdGUgbG9nOicsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSb3RhdGUgbG9ncyBpZiBmaWxlIGV4Y2VlZHMgbWF4IHNpemVcbiAgICovXG4gIHByaXZhdGUgcm90YXRlTG9nc0lmTmVlZGVkKGxvZ0ZpbGU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdGF0cyA9IGZzLnN0YXRTeW5jKGxvZ0ZpbGUpO1xuICAgICAgXG4gICAgICBpZiAoc3RhdHMuc2l6ZSA+IHRoaXMuY29uZmlnLm1heEZpbGVTaXplISkge1xuICAgICAgICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICAgICAgICBjb25zdCByb3RhdGVkRmlsZSA9IGxvZ0ZpbGUucmVwbGFjZSgnLmpzb25sJywgYC4ke3RpbWVzdGFtcH0uanNvbmxgKTtcbiAgICAgICAgZnMucmVuYW1lU3luYyhsb2dGaWxlLCByb3RhdGVkRmlsZSk7XG4gICAgICAgIFxuICAgICAgICAvLyBDbGVhbiB1cCBvbGQgZmlsZXNcbiAgICAgICAgdGhpcy5jbGVhbnVwT2xkTG9ncygpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBJZ25vcmUgcm90YXRpb24gZXJyb3JzXG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYW4gdXAgb2xkIGxvZyBmaWxlc1xuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbnVwT2xkTG9ncygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmxvZ0RpcikgcmV0dXJuO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBmaWxlcyA9IGZzLnJlYWRkaXJTeW5jKHRoaXMuY29uZmlnLmxvZ0RpcilcbiAgICAgICAgLmZpbHRlcihmID0+IGYuc3RhcnRzV2l0aCgnbGxtdmVyaWZ5LScpICYmIGYuZW5kc1dpdGgoJy5qc29ubCcpKVxuICAgICAgICAubWFwKGYgPT4gKHtcbiAgICAgICAgICBuYW1lOiBmLFxuICAgICAgICAgIHBhdGg6IHBhdGguam9pbih0aGlzLmNvbmZpZy5sb2dEaXIhLCBmKSxcbiAgICAgICAgICB0aW1lOiBmcy5zdGF0U3luYyhwYXRoLmpvaW4odGhpcy5jb25maWcubG9nRGlyISwgZikpLm10aW1lLmdldFRpbWUoKVxuICAgICAgICB9KSlcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGIudGltZSAtIGEudGltZSk7XG4gICAgICBcbiAgICAgIC8vIEtlZXAgb25seSBtYXhGaWxlc1xuICAgICAgaWYgKGZpbGVzLmxlbmd0aCA+IHRoaXMuY29uZmlnLm1heEZpbGVzISkge1xuICAgICAgICBmaWxlcy5zbGljZSh0aGlzLmNvbmZpZy5tYXhGaWxlcyEpLmZvckVhY2goZmlsZSA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGZzLnVubGlua1N5bmMoZmlsZS5wYXRoKTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgLy8gSWdub3JlIGRlbGV0aW9uIGVycm9yc1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIElnbm9yZSBjbGVhbnVwIGVycm9yc1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENyZWF0ZSBsb2cgZW50cnlcbiAgICovXG4gIHByaXZhdGUgY3JlYXRlRW50cnkobGV2ZWw6IExvZ0xldmVsLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBhbnksIGVycm9yPzogRXJyb3IpOiBMb2dFbnRyeSB7XG4gICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBsZXZlbCxcbiAgICAgIHJlcXVlc3RJZDogdGhpcy5nZXRSZXF1ZXN0SWQoKSxcbiAgICAgIG1lc3NhZ2U6IHRoaXMuY29uZmlnLnNhbml0aXplUElJID8gdGhpcy5zYW5pdGl6ZURhdGEobWVzc2FnZSkgOiBtZXNzYWdlXG4gICAgfTtcbiAgICBcbiAgICBpZiAoZGF0YSkge1xuICAgICAgZW50cnkuZGF0YSA9IHRoaXMuc2FuaXRpemVEYXRhKGRhdGEpO1xuICAgIH1cbiAgICBcbiAgICBpZiAodGhpcy5yZXF1ZXN0U3RhcnRUaW1lKSB7XG4gICAgICBlbnRyeS5kdXJhdGlvbiA9IERhdGUubm93KCkgLSB0aGlzLnJlcXVlc3RTdGFydFRpbWU7XG4gICAgfVxuICAgIFxuICAgIGlmIChlcnJvcikge1xuICAgICAgZW50cnkuZXJyb3IgPSB7XG4gICAgICAgIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UsXG4gICAgICAgIGNvZGU6IChlcnJvciBhcyBhbnkpLmNvZGUsXG4gICAgICAgIHN0YWNrOiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50JyA/IGVycm9yLnN0YWNrIDogdW5kZWZpbmVkXG4gICAgICB9O1xuICAgIH1cbiAgICBcbiAgICBpZiAodGhpcy5jb25maWcuaW5jbHVkZU1ldGFkYXRhKSB7XG4gICAgICBlbnRyeS5tZXRhZGF0YSA9IHtcbiAgICAgICAgdmVyc2lvbjogcmVxdWlyZSgnLi4vLi4vcGFja2FnZS5qc29uJykudmVyc2lvbixcbiAgICAgICAgcGlkOiBwcm9jZXNzLnBpZCxcbiAgICAgICAgaG9zdG5hbWU6IG9zLmhvc3RuYW1lKClcbiAgICAgIH07XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBlbnRyeTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHNob3VsZCBsb2cgYXQgbGV2ZWxcbiAgICovXG4gIHByaXZhdGUgc2hvdWxkTG9nKGxldmVsOiBMb2dMZXZlbCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGxldmVscyA9IFtMb2dMZXZlbC5ERUJVRywgTG9nTGV2ZWwuSU5GTywgTG9nTGV2ZWwuV0FSTiwgTG9nTGV2ZWwuRVJST1JdO1xuICAgIGNvbnN0IGNvbmZpZ0xldmVsSW5kZXggPSBsZXZlbHMuaW5kZXhPZih0aGlzLmNvbmZpZy5sZXZlbCk7XG4gICAgY29uc3QgbWVzc2FnZUxldmVsSW5kZXggPSBsZXZlbHMuaW5kZXhPZihsZXZlbCk7XG4gICAgXG4gICAgcmV0dXJuIG1lc3NhZ2VMZXZlbEluZGV4ID49IGNvbmZpZ0xldmVsSW5kZXg7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2cgZGVidWcgbWVzc2FnZVxuICAgKi9cbiAgcHVibGljIGRlYnVnKG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IGFueSk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNob3VsZExvZyhMb2dMZXZlbC5ERUJVRykpIHtcbiAgICAgIHRoaXMud3JpdGVMb2codGhpcy5jcmVhdGVFbnRyeShMb2dMZXZlbC5ERUJVRywgbWVzc2FnZSwgZGF0YSkpO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIExvZyBpbmZvIG1lc3NhZ2VcbiAgICovXG4gIHB1YmxpYyBpbmZvKG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IGFueSk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNob3VsZExvZyhMb2dMZXZlbC5JTkZPKSkge1xuICAgICAgdGhpcy53cml0ZUxvZyh0aGlzLmNyZWF0ZUVudHJ5KExvZ0xldmVsLklORk8sIG1lc3NhZ2UsIGRhdGEpKTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2cgd2FybmluZyBtZXNzYWdlXG4gICAqL1xuICBwdWJsaWMgd2FybihtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBhbnkpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zaG91bGRMb2coTG9nTGV2ZWwuV0FSTikpIHtcbiAgICAgIHRoaXMud3JpdGVMb2codGhpcy5jcmVhdGVFbnRyeShMb2dMZXZlbC5XQVJOLCBtZXNzYWdlLCBkYXRhKSk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogTG9nIGVycm9yIG1lc3NhZ2VcbiAgICovXG4gIHB1YmxpYyBlcnJvcihtZXNzYWdlOiBzdHJpbmcsIGVycm9yPzogRXJyb3IsIGRhdGE/OiBhbnkpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zaG91bGRMb2coTG9nTGV2ZWwuRVJST1IpKSB7XG4gICAgICB0aGlzLndyaXRlTG9nKHRoaXMuY3JlYXRlRW50cnkoTG9nTGV2ZWwuRVJST1IsIG1lc3NhZ2UsIGRhdGEsIGVycm9yKSk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogUmVhZCBsb2dzIGZvciBhIHNwZWNpZmljIGRhdGVcbiAgICovXG4gIHB1YmxpYyByZWFkTG9ncyhkYXRlPzogc3RyaW5nKTogTG9nRW50cnlbXSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5sb2dEaXIpIHJldHVybiBbXTtcbiAgICBcbiAgICBjb25zdCBkYXRlU3RyID0gZGF0ZSB8fCBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXTtcbiAgICBjb25zdCBsb2dGaWxlID0gcGF0aC5qb2luKHRoaXMuY29uZmlnLmxvZ0RpciwgYGxsbXZlcmlmeS0ke2RhdGVTdHJ9Lmpzb25sYCk7XG4gICAgXG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKGxvZ0ZpbGUpKSByZXR1cm4gW107XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMobG9nRmlsZSwgJ3V0Zi04Jyk7XG4gICAgICByZXR1cm4gY29udGVudFxuICAgICAgICAuc3BsaXQoJ1xcbicpXG4gICAgICAgIC5maWx0ZXIobGluZSA9PiBsaW5lLnRyaW0oKSlcbiAgICAgICAgLm1hcChsaW5lID0+IEpTT04ucGFyc2UobGluZSkgYXMgTG9nRW50cnkpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gcmVhZCBsb2dzOicsIGVycm9yKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgbG9nIHN0YXRpc3RpY3NcbiAgICovXG4gIHB1YmxpYyBnZXRTdGF0cyhkYXRlPzogc3RyaW5nKToge1xuICAgIHRvdGFsRW50cmllczogbnVtYmVyO1xuICAgIGJ5TGV2ZWw6IFJlY29yZDxMb2dMZXZlbCwgbnVtYmVyPjtcbiAgICBlcnJvckNvdW50OiBudW1iZXI7XG4gICAgYXZnRHVyYXRpb246IG51bWJlcjtcbiAgfSB7XG4gICAgY29uc3QgbG9ncyA9IHRoaXMucmVhZExvZ3MoZGF0ZSk7XG4gICAgXG4gICAgY29uc3Qgc3RhdHMgPSB7XG4gICAgICB0b3RhbEVudHJpZXM6IGxvZ3MubGVuZ3RoLFxuICAgICAgYnlMZXZlbDoge1xuICAgICAgICBbTG9nTGV2ZWwuREVCVUddOiAwLFxuICAgICAgICBbTG9nTGV2ZWwuSU5GT106IDAsXG4gICAgICAgIFtMb2dMZXZlbC5XQVJOXTogMCxcbiAgICAgICAgW0xvZ0xldmVsLkVSUk9SXTogMFxuICAgICAgfSxcbiAgICAgIGVycm9yQ291bnQ6IDAsXG4gICAgICBhdmdEdXJhdGlvbjogMFxuICAgIH07XG4gICAgXG4gICAgbGV0IHRvdGFsRHVyYXRpb24gPSAwO1xuICAgIGxldCBkdXJhdGlvbkNvdW50ID0gMDtcbiAgICBcbiAgICBsb2dzLmZvckVhY2gobG9nID0+IHtcbiAgICAgIHN0YXRzLmJ5TGV2ZWxbbG9nLmxldmVsXSsrO1xuICAgICAgaWYgKGxvZy5lcnJvcikgc3RhdHMuZXJyb3JDb3VudCsrO1xuICAgICAgaWYgKGxvZy5kdXJhdGlvbikge1xuICAgICAgICB0b3RhbER1cmF0aW9uICs9IGxvZy5kdXJhdGlvbjtcbiAgICAgICAgZHVyYXRpb25Db3VudCsrO1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIGlmIChkdXJhdGlvbkNvdW50ID4gMCkge1xuICAgICAgc3RhdHMuYXZnRHVyYXRpb24gPSB0b3RhbER1cmF0aW9uIC8gZHVyYXRpb25Db3VudDtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHN0YXRzO1xuICB9XG59XG5cbi8qKlxuICogR2xvYmFsIGxvZ2dlciBpbnN0YW5jZVxuICovXG5sZXQgZ2xvYmFsTG9nZ2VyOiBMb2dnZXIgfCBudWxsID0gbnVsbDtcblxuLyoqXG4gKiBHZXQgZ2xvYmFsIGxvZ2dlclxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TG9nZ2VyKGNvbmZpZz86IFBhcnRpYWw8TG9nZ2VyQ29uZmlnPik6IExvZ2dlciB7XG4gIGlmICghZ2xvYmFsTG9nZ2VyKSB7XG4gICAgZ2xvYmFsTG9nZ2VyID0gbmV3IExvZ2dlcihjb25maWcpO1xuICB9XG4gIHJldHVybiBnbG9iYWxMb2dnZXI7XG59XG5cbi8qKlxuICogU2V0IGdsb2JhbCBsb2dnZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ2dlcihsb2dnZXI6IExvZ2dlcik6IHZvaWQge1xuICBnbG9iYWxMb2dnZXIgPSBsb2dnZXI7XG59XG5cbi8qKlxuICogUmVzZXQgZ2xvYmFsIGxvZ2dlclxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRMb2dnZXIoKTogdm9pZCB7XG4gIGdsb2JhbExvZ2dlciA9IG51bGw7XG59XG4iXX0=