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.

273 lines 29.2 kB
"use strict"; /** * Audit Logger * * Local-only audit logging for verification results. * Supports file output and optional GitHub export. * No external API calls - all processing is local. * * @module audit * @author llmverify * @license MIT */ 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.AuditLogger = void 0; exports.getAuditLogger = getAuditLogger; exports.auditLog = auditLog; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const DEFAULT_CONFIG = { enabled: false, outputPath: './llmverify-audit.jsonl', maxEntries: 10000, rotateDaily: true, includeContentHash: true }; /** * Simple hash function for content (no crypto dependency) */ function simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(16).padStart(8, '0'); } /** * Generate unique ID */ function generateId() { const timestamp = Date.now().toString(36); const random = Math.random().toString(36).substring(2, 8); return `${timestamp}-${random}`; } /** * Audit Logger class */ class AuditLogger { constructor(config = {}) { this.entries = []; this.currentDate = ''; this.config = { ...DEFAULT_CONFIG, ...config }; this.currentDate = new Date().toISOString().split('T')[0]; } /** * Log a verification action */ log(entry) { if (!this.config.enabled) { return { ...entry, id: '', timestamp: '' }; } const fullEntry = { ...entry, id: generateId(), timestamp: new Date().toISOString() }; this.entries.push(fullEntry); // Write to file if configured if (this.config.outputPath) { this.writeToFile(fullEntry); } // Rotate if needed if (this.entries.length > (this.config.maxEntries || 10000)) { this.entries = this.entries.slice(-1000); } return fullEntry; } /** * Create audit entry from verification result */ createEntry(action, content, result, preset) { return { action, input: { contentLength: content.length, contentHash: this.config.includeContentHash ? simpleHash(content) : '', preset }, output: { riskLevel: result.risk?.level || 'unknown', riskScore: result.risk?.overall || 0, action: result.risk?.action || 'unknown', findingsCount: result.findings?.length || 0 }, performance: { latencyMs: result.meta?.latency_ms || 0, enginesUsed: result.meta?.enginesUsed || [] } }; } /** * Write entry to file (JSONL format) */ writeToFile(entry) { if (!this.config.outputPath) return; try { // Check for daily rotation const today = new Date().toISOString().split('T')[0]; let filePath = this.config.outputPath; if (this.config.rotateDaily && today !== this.currentDate) { this.currentDate = today; const ext = path.extname(filePath); const base = filePath.slice(0, -ext.length); filePath = `${base}-${today}${ext}`; } // Append to file const line = JSON.stringify(entry) + '\n'; fs.appendFileSync(filePath, line, 'utf-8'); } catch (error) { // Silently fail - audit should not break main functionality console.error('[llmverify audit] Failed to write:', error); } } /** * Get recent entries */ getRecent(count = 100) { return this.entries.slice(-count); } /** * Get entries by risk level */ getByRiskLevel(level) { return this.entries.filter(e => e.output.riskLevel === level); } /** * Get summary statistics */ getSummary() { const byRiskLevel = {}; const byAction = {}; let totalLatency = 0; let blockedCount = 0; for (const entry of this.entries) { byRiskLevel[entry.output.riskLevel] = (byRiskLevel[entry.output.riskLevel] || 0) + 1; byAction[entry.action] = (byAction[entry.action] || 0) + 1; totalLatency += entry.performance.latencyMs; if (entry.output.action === 'block') { blockedCount++; } } return { totalEntries: this.entries.length, byRiskLevel, byAction, avgLatencyMs: this.entries.length > 0 ? totalLatency / this.entries.length : 0, blockedCount }; } /** * Export to JSON file */ exportToFile(filePath) { const data = { exportedAt: new Date().toISOString(), summary: this.getSummary(), entries: this.entries }; fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); } /** * Export for GitHub (creates markdown report) */ exportForGitHub(filePath) { const summary = this.getSummary(); const recent = this.getRecent(10); let markdown = `# llmverify Audit Report\n\n`; markdown += `Generated: ${new Date().toISOString()}\n\n`; markdown += `## Summary\n\n`; markdown += `| Metric | Value |\n`; markdown += `|--------|-------|\n`; markdown += `| Total Verifications | ${summary.totalEntries} |\n`; markdown += `| Blocked | ${summary.blockedCount} |\n`; markdown += `| Avg Latency | ${summary.avgLatencyMs.toFixed(2)}ms |\n\n`; markdown += `## Risk Distribution\n\n`; markdown += `| Level | Count |\n`; markdown += `|-------|-------|\n`; for (const [level, count] of Object.entries(summary.byRiskLevel)) { markdown += `| ${level} | ${count} |\n`; } markdown += `\n`; markdown += `## Recent Entries\n\n`; markdown += `| Time | Action | Risk | Latency |\n`; markdown += `|------|--------|------|--------|\n`; for (const entry of recent) { const time = entry.timestamp.split('T')[1].split('.')[0]; markdown += `| ${time} | ${entry.action} | ${entry.output.riskLevel} | ${entry.performance.latencyMs}ms |\n`; } fs.writeFileSync(filePath, markdown, 'utf-8'); } /** * Clear all entries */ clear() { this.entries = []; } /** * Enable/disable logging */ setEnabled(enabled) { this.config.enabled = enabled; } } exports.AuditLogger = AuditLogger; // Singleton instance let defaultLogger = null; /** * Get or create default audit logger */ function getAuditLogger(config) { if (!defaultLogger || config) { defaultLogger = new AuditLogger(config); } return defaultLogger; } /** * Quick log function */ function auditLog(action, content, result, preset) { const logger = getAuditLogger(); if (!logger) return null; const entry = logger.createEntry(action, content, result, preset); return logger.log(entry); } exports.default = AuditLogger; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXVkaXQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7O0dBVUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTBSSCx3Q0FLQztBQUtELDRCQWVDO0FBalRELHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFnQzdCLE1BQU0sY0FBYyxHQUFnQjtJQUNsQyxPQUFPLEVBQUUsS0FBSztJQUNkLFVBQVUsRUFBRSx5QkFBeUI7SUFDckMsVUFBVSxFQUFFLEtBQUs7SUFDakIsV0FBVyxFQUFFLElBQUk7SUFDakIsa0JBQWtCLEVBQUUsSUFBSTtDQUN6QixDQUFDO0FBRUY7O0dBRUc7QUFDSCxTQUFTLFVBQVUsQ0FBQyxHQUFXO0lBQzdCLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNiLEtBQUssSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDcEMsTUFBTSxJQUFJLEdBQUcsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUMvQixJQUFJLEdBQUcsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUM7UUFDbkMsSUFBSSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7SUFDckIsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUN0RCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxTQUFTLFVBQVU7SUFDakIsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUMxQyxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDMUQsT0FBTyxHQUFHLFNBQVMsSUFBSSxNQUFNLEVBQUUsQ0FBQztBQUNsQyxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFhLFdBQVc7SUFLdEIsWUFBWSxTQUErQixFQUFFO1FBSHJDLFlBQU8sR0FBaUIsRUFBRSxDQUFDO1FBQzNCLGdCQUFXLEdBQVcsRUFBRSxDQUFDO1FBRy9CLElBQUksQ0FBQyxNQUFNLEdBQUcsRUFBRSxHQUFHLGNBQWMsRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDO1FBQy9DLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsR0FBRyxDQUFDLEtBQTJDO1FBQzdDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ3pCLE9BQU8sRUFBRSxHQUFHLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUUsQ0FBQztRQUM3QyxDQUFDO1FBRUQsTUFBTSxTQUFTLEdBQWU7WUFDNUIsR0FBRyxLQUFLO1lBQ1IsRUFBRSxFQUFFLFVBQVUsRUFBRTtZQUNoQixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUU7U0FDcEMsQ0FBQztRQUVGLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBRTdCLDhCQUE4QjtRQUM5QixJQUFJLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDM0IsSUFBSSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUM5QixDQUFDO1FBRUQsbUJBQW1CO1FBQ25CLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVUsSUFBSSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzVELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQyxDQUFDO1FBRUQsT0FBTyxTQUFTLENBQUM7SUFDbkIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsV0FBVyxDQUNULE1BQTRCLEVBQzVCLE9BQWUsRUFDZixNQUlDLEVBQ0QsTUFBZTtRQUVmLE9BQU87WUFDTCxNQUFNO1lBQ04sS0FBSyxFQUFFO2dCQUNMLGFBQWEsRUFBRSxPQUFPLENBQUMsTUFBTTtnQkFDN0IsV0FBVyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsa0JBQWtCLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRTtnQkFDdEUsTUFBTTthQUNQO1lBQ0QsTUFBTSxFQUFFO2dCQUNOLFNBQVMsRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLEtBQUssSUFBSSxTQUFTO2dCQUMxQyxTQUFTLEVBQUUsTUFBTSxDQUFDLElBQUksRUFBRSxPQUFPLElBQUksQ0FBQztnQkFDcEMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUUsTUFBTSxJQUFJLFNBQVM7Z0JBQ3hDLGFBQWEsRUFBRSxNQUFNLENBQUMsUUFBUSxFQUFFLE1BQU0sSUFBSSxDQUFDO2FBQzVDO1lBQ0QsV0FBVyxFQUFFO2dCQUNYLFNBQVMsRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFLFVBQVUsSUFBSSxDQUFDO2dCQUN2QyxXQUFXLEVBQUUsTUFBTSxDQUFDLElBQUksRUFBRSxXQUFXLElBQUksRUFBRTthQUM1QztTQUNGLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSyxXQUFXLENBQUMsS0FBaUI7UUFDbkMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVTtZQUFFLE9BQU87UUFFcEMsSUFBSSxDQUFDO1lBQ0gsMkJBQTJCO1lBQzNCLE1BQU0sS0FBSyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3JELElBQUksUUFBUSxHQUFHLElBQUksQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDO1lBRXRDLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLElBQUksS0FBSyxLQUFLLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDMUQsSUFBSSxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUM7Z0JBQ3pCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ25DLE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO2dCQUM1QyxRQUFRLEdBQUcsR0FBRyxJQUFJLElBQUksS0FBSyxHQUFHLEdBQUcsRUFBRSxDQUFDO1lBQ3RDLENBQUM7WUFFRCxpQkFBaUI7WUFDakIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUM7WUFDMUMsRUFBRSxDQUFDLGNBQWMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQzdDLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsNERBQTREO1lBQzVELE9BQU8sQ0FBQyxLQUFLLENBQUMsb0NBQW9DLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDN0QsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNILFNBQVMsQ0FBQyxRQUFnQixHQUFHO1FBQzNCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNwQyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxjQUFjLENBQUMsS0FBYTtRQUMxQixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEtBQUssS0FBSyxDQUFDLENBQUM7SUFDaEUsQ0FBQztJQUVEOztPQUVHO0lBQ0gsVUFBVTtRQU9SLE1BQU0sV0FBVyxHQUEyQixFQUFFLENBQUM7UUFDL0MsTUFBTSxRQUFRLEdBQTJCLEVBQUUsQ0FBQztRQUM1QyxJQUFJLFlBQVksR0FBRyxDQUFDLENBQUM7UUFDckIsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDO1FBRXJCLEtBQUssTUFBTSxLQUFLLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2pDLFdBQVcsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3JGLFFBQVEsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUMzRCxZQUFZLElBQUksS0FBSyxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUM7WUFDNUMsSUFBSSxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxPQUFPLEVBQUUsQ0FBQztnQkFDcEMsWUFBWSxFQUFFLENBQUM7WUFDakIsQ0FBQztRQUNILENBQUM7UUFFRCxPQUFPO1lBQ0wsWUFBWSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTTtZQUNqQyxXQUFXO1lBQ1gsUUFBUTtZQUNSLFlBQVksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUM5RSxZQUFZO1NBQ2IsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILFlBQVksQ0FBQyxRQUFnQjtRQUMzQixNQUFNLElBQUksR0FBRztZQUNYLFVBQVUsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtZQUNwQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFVBQVUsRUFBRTtZQUMxQixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU87U0FDdEIsQ0FBQztRQUNGLEVBQUUsQ0FBQyxhQUFhLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNyRSxDQUFDO0lBRUQ7O09BRUc7SUFDSCxlQUFlLENBQUMsUUFBZ0I7UUFDOUIsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFbEMsSUFBSSxRQUFRLEdBQUcsOEJBQThCLENBQUM7UUFDOUMsUUFBUSxJQUFJLGNBQWMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsTUFBTSxDQUFDO1FBQ3pELFFBQVEsSUFBSSxnQkFBZ0IsQ0FBQztRQUM3QixRQUFRLElBQUksc0JBQXNCLENBQUM7UUFDbkMsUUFBUSxJQUFJLHNCQUFzQixDQUFDO1FBQ25DLFFBQVEsSUFBSSwyQkFBMkIsT0FBTyxDQUFDLFlBQVksTUFBTSxDQUFDO1FBQ2xFLFFBQVEsSUFBSSxlQUFlLE9BQU8sQ0FBQyxZQUFZLE1BQU0sQ0FBQztRQUN0RCxRQUFRLElBQUksbUJBQW1CLE9BQU8sQ0FBQyxZQUFZLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUM7UUFFekUsUUFBUSxJQUFJLDBCQUEwQixDQUFDO1FBQ3ZDLFFBQVEsSUFBSSxxQkFBcUIsQ0FBQztRQUNsQyxRQUFRLElBQUkscUJBQXFCLENBQUM7UUFDbEMsS0FBSyxNQUFNLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUM7WUFDakUsUUFBUSxJQUFJLEtBQUssS0FBSyxNQUFNLEtBQUssTUFBTSxDQUFDO1FBQzFDLENBQUM7UUFDRCxRQUFRLElBQUksSUFBSSxDQUFDO1FBRWpCLFFBQVEsSUFBSSx1QkFBdUIsQ0FBQztRQUNwQyxRQUFRLElBQUksc0NBQXNDLENBQUM7UUFDbkQsUUFBUSxJQUFJLHFDQUFxQyxDQUFDO1FBQ2xELEtBQUssTUFBTSxLQUFLLElBQUksTUFBTSxFQUFFLENBQUM7WUFDM0IsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ3pELFFBQVEsSUFBSSxLQUFLLElBQUksTUFBTSxLQUFLLENBQUMsTUFBTSxNQUFNLEtBQUssQ0FBQyxNQUFNLENBQUMsU0FBUyxNQUFNLEtBQUssQ0FBQyxXQUFXLENBQUMsU0FBUyxRQUFRLENBQUM7UUFDL0csQ0FBQztRQUVELEVBQUUsQ0FBQyxhQUFhLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNoRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLO1FBQ0gsSUFBSSxDQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7SUFDcEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsVUFBVSxDQUFDLE9BQWdCO1FBQ3pCLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUNoQyxDQUFDO0NBQ0Y7QUE5TUQsa0NBOE1DO0FBRUQscUJBQXFCO0FBQ3JCLElBQUksYUFBYSxHQUF1QixJQUFJLENBQUM7QUFFN0M7O0dBRUc7QUFDSCxTQUFnQixjQUFjLENBQUMsTUFBNkI7SUFDMUQsSUFBSSxDQUFDLGFBQWEsSUFBSSxNQUFNLEVBQUUsQ0FBQztRQUM3QixhQUFhLEdBQUcsSUFBSSxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDMUMsQ0FBQztJQUNELE9BQU8sYUFBYSxDQUFDO0FBQ3ZCLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLFFBQVEsQ0FDdEIsTUFBNEIsRUFDNUIsT0FBZSxFQUNmLE1BSUMsRUFDRCxNQUFlO0lBRWYsTUFBTSxNQUFNLEdBQUcsY0FBYyxFQUFFLENBQUM7SUFDaEMsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLElBQUksQ0FBQztJQUV6QixNQUFNLEtBQUssR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2xFLE9BQU8sTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUMzQixDQUFDO0FBRUQsa0JBQWUsV0FBVyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBBdWRpdCBMb2dnZXJcbiAqIFxuICogTG9jYWwtb25seSBhdWRpdCBsb2dnaW5nIGZvciB2ZXJpZmljYXRpb24gcmVzdWx0cy5cbiAqIFN1cHBvcnRzIGZpbGUgb3V0cHV0IGFuZCBvcHRpb25hbCBHaXRIdWIgZXhwb3J0LlxuICogTm8gZXh0ZXJuYWwgQVBJIGNhbGxzIC0gYWxsIHByb2Nlc3NpbmcgaXMgbG9jYWwuXG4gKiBcbiAqIEBtb2R1bGUgYXVkaXRcbiAqIEBhdXRob3IgbGxtdmVyaWZ5XG4gKiBAbGljZW5zZSBNSVRcbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEF1ZGl0RW50cnkge1xuICBpZDogc3RyaW5nO1xuICB0aW1lc3RhbXA6IHN0cmluZztcbiAgYWN0aW9uOiAndmVyaWZ5JyB8ICdjbGFzc2lmeScgfCAnY2hlY2tfcGlpJyB8ICdjaGVja19pbmplY3Rpb24nIHwgJ3J1bic7XG4gIGlucHV0OiB7XG4gICAgY29udGVudExlbmd0aDogbnVtYmVyO1xuICAgIGNvbnRlbnRIYXNoOiBzdHJpbmc7XG4gICAgcHJlc2V0Pzogc3RyaW5nO1xuICB9O1xuICBvdXRwdXQ6IHtcbiAgICByaXNrTGV2ZWw6IHN0cmluZztcbiAgICByaXNrU2NvcmU6IG51bWJlcjtcbiAgICBhY3Rpb246IHN0cmluZztcbiAgICBmaW5kaW5nc0NvdW50OiBudW1iZXI7XG4gIH07XG4gIHBlcmZvcm1hbmNlOiB7XG4gICAgbGF0ZW5jeU1zOiBudW1iZXI7XG4gICAgZW5naW5lc1VzZWQ6IHN0cmluZ1tdO1xuICB9O1xuICBtZXRhZGF0YT86IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEF1ZGl0Q29uZmlnIHtcbiAgZW5hYmxlZDogYm9vbGVhbjtcbiAgb3V0cHV0UGF0aD86IHN0cmluZztcbiAgbWF4RW50cmllcz86IG51bWJlcjtcbiAgcm90YXRlRGFpbHk/OiBib29sZWFuO1xuICBpbmNsdWRlQ29udGVudEhhc2g/OiBib29sZWFuO1xufVxuXG5jb25zdCBERUZBVUxUX0NPTkZJRzogQXVkaXRDb25maWcgPSB7XG4gIGVuYWJsZWQ6IGZhbHNlLFxuICBvdXRwdXRQYXRoOiAnLi9sbG12ZXJpZnktYXVkaXQuanNvbmwnLFxuICBtYXhFbnRyaWVzOiAxMDAwMCxcbiAgcm90YXRlRGFpbHk6IHRydWUsXG4gIGluY2x1ZGVDb250ZW50SGFzaDogdHJ1ZVxufTtcblxuLyoqXG4gKiBTaW1wbGUgaGFzaCBmdW5jdGlvbiBmb3IgY29udGVudCAobm8gY3J5cHRvIGRlcGVuZGVuY3kpXG4gKi9cbmZ1bmN0aW9uIHNpbXBsZUhhc2goc3RyOiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgaGFzaCA9IDA7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgY2hhciA9IHN0ci5jaGFyQ29kZUF0KGkpO1xuICAgIGhhc2ggPSAoKGhhc2ggPDwgNSkgLSBoYXNoKSArIGNoYXI7XG4gICAgaGFzaCA9IGhhc2ggJiBoYXNoO1xuICB9XG4gIHJldHVybiBNYXRoLmFicyhoYXNoKS50b1N0cmluZygxNikucGFkU3RhcnQoOCwgJzAnKTtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZSB1bmlxdWUgSURcbiAqL1xuZnVuY3Rpb24gZ2VuZXJhdGVJZCgpOiBzdHJpbmcge1xuICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpLnRvU3RyaW5nKDM2KTtcbiAgY29uc3QgcmFuZG9tID0gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyaW5nKDIsIDgpO1xuICByZXR1cm4gYCR7dGltZXN0YW1wfS0ke3JhbmRvbX1gO1xufVxuXG4vKipcbiAqIEF1ZGl0IExvZ2dlciBjbGFzc1xuICovXG5leHBvcnQgY2xhc3MgQXVkaXRMb2dnZXIge1xuICBwcml2YXRlIGNvbmZpZzogQXVkaXRDb25maWc7XG4gIHByaXZhdGUgZW50cmllczogQXVkaXRFbnRyeVtdID0gW107XG4gIHByaXZhdGUgY3VycmVudERhdGU6IHN0cmluZyA9ICcnO1xuXG4gIGNvbnN0cnVjdG9yKGNvbmZpZzogUGFydGlhbDxBdWRpdENvbmZpZz4gPSB7fSkge1xuICAgIHRoaXMuY29uZmlnID0geyAuLi5ERUZBVUxUX0NPTkZJRywgLi4uY29uZmlnIH07XG4gICAgdGhpcy5jdXJyZW50RGF0ZSA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKS5zcGxpdCgnVCcpWzBdO1xuICB9XG5cbiAgLyoqXG4gICAqIExvZyBhIHZlcmlmaWNhdGlvbiBhY3Rpb25cbiAgICovXG4gIGxvZyhlbnRyeTogT21pdDxBdWRpdEVudHJ5LCAnaWQnIHwgJ3RpbWVzdGFtcCc+KTogQXVkaXRFbnRyeSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5lbmFibGVkKSB7XG4gICAgICByZXR1cm4geyAuLi5lbnRyeSwgaWQ6ICcnLCB0aW1lc3RhbXA6ICcnIH07XG4gICAgfVxuXG4gICAgY29uc3QgZnVsbEVudHJ5OiBBdWRpdEVudHJ5ID0ge1xuICAgICAgLi4uZW50cnksXG4gICAgICBpZDogZ2VuZXJhdGVJZCgpLFxuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKClcbiAgICB9O1xuXG4gICAgdGhpcy5lbnRyaWVzLnB1c2goZnVsbEVudHJ5KTtcblxuICAgIC8vIFdyaXRlIHRvIGZpbGUgaWYgY29uZmlndXJlZFxuICAgIGlmICh0aGlzLmNvbmZpZy5vdXRwdXRQYXRoKSB7XG4gICAgICB0aGlzLndyaXRlVG9GaWxlKGZ1bGxFbnRyeSk7XG4gICAgfVxuXG4gICAgLy8gUm90YXRlIGlmIG5lZWRlZFxuICAgIGlmICh0aGlzLmVudHJpZXMubGVuZ3RoID4gKHRoaXMuY29uZmlnLm1heEVudHJpZXMgfHwgMTAwMDApKSB7XG4gICAgICB0aGlzLmVudHJpZXMgPSB0aGlzLmVudHJpZXMuc2xpY2UoLTEwMDApO1xuICAgIH1cblxuICAgIHJldHVybiBmdWxsRW50cnk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGF1ZGl0IGVudHJ5IGZyb20gdmVyaWZpY2F0aW9uIHJlc3VsdFxuICAgKi9cbiAgY3JlYXRlRW50cnkoXG4gICAgYWN0aW9uOiBBdWRpdEVudHJ5WydhY3Rpb24nXSxcbiAgICBjb250ZW50OiBzdHJpbmcsXG4gICAgcmVzdWx0OiB7XG4gICAgICByaXNrPzogeyBsZXZlbDogc3RyaW5nOyBvdmVyYWxsOiBudW1iZXI7IGFjdGlvbjogc3RyaW5nIH07XG4gICAgICBmaW5kaW5ncz86IHVua25vd25bXTtcbiAgICAgIG1ldGE/OiB7IGxhdGVuY3lfbXM/OiBudW1iZXI7IGVuZ2luZXNVc2VkPzogc3RyaW5nW10gfTtcbiAgICB9LFxuICAgIHByZXNldD86IHN0cmluZ1xuICApOiBPbWl0PEF1ZGl0RW50cnksICdpZCcgfCAndGltZXN0YW1wJz4ge1xuICAgIHJldHVybiB7XG4gICAgICBhY3Rpb24sXG4gICAgICBpbnB1dDoge1xuICAgICAgICBjb250ZW50TGVuZ3RoOiBjb250ZW50Lmxlbmd0aCxcbiAgICAgICAgY29udGVudEhhc2g6IHRoaXMuY29uZmlnLmluY2x1ZGVDb250ZW50SGFzaCA/IHNpbXBsZUhhc2goY29udGVudCkgOiAnJyxcbiAgICAgICAgcHJlc2V0XG4gICAgICB9LFxuICAgICAgb3V0cHV0OiB7XG4gICAgICAgIHJpc2tMZXZlbDogcmVzdWx0LnJpc2s/LmxldmVsIHx8ICd1bmtub3duJyxcbiAgICAgICAgcmlza1Njb3JlOiByZXN1bHQucmlzaz8ub3ZlcmFsbCB8fCAwLFxuICAgICAgICBhY3Rpb246IHJlc3VsdC5yaXNrPy5hY3Rpb24gfHwgJ3Vua25vd24nLFxuICAgICAgICBmaW5kaW5nc0NvdW50OiByZXN1bHQuZmluZGluZ3M/Lmxlbmd0aCB8fCAwXG4gICAgICB9LFxuICAgICAgcGVyZm9ybWFuY2U6IHtcbiAgICAgICAgbGF0ZW5jeU1zOiByZXN1bHQubWV0YT8ubGF0ZW5jeV9tcyB8fCAwLFxuICAgICAgICBlbmdpbmVzVXNlZDogcmVzdWx0Lm1ldGE/LmVuZ2luZXNVc2VkIHx8IFtdXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXcml0ZSBlbnRyeSB0byBmaWxlIChKU09OTCBmb3JtYXQpXG4gICAqL1xuICBwcml2YXRlIHdyaXRlVG9GaWxlKGVudHJ5OiBBdWRpdEVudHJ5KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5vdXRwdXRQYXRoKSByZXR1cm47XG5cbiAgICB0cnkge1xuICAgICAgLy8gQ2hlY2sgZm9yIGRhaWx5IHJvdGF0aW9uXG4gICAgICBjb25zdCB0b2RheSA9IG5ldyBEYXRlKCkudG9JU09TdHJpbmcoKS5zcGxpdCgnVCcpWzBdO1xuICAgICAgbGV0IGZpbGVQYXRoID0gdGhpcy5jb25maWcub3V0cHV0UGF0aDtcblxuICAgICAgaWYgKHRoaXMuY29uZmlnLnJvdGF0ZURhaWx5ICYmIHRvZGF5ICE9PSB0aGlzLmN1cnJlbnREYXRlKSB7XG4gICAgICAgIHRoaXMuY3VycmVudERhdGUgPSB0b2RheTtcbiAgICAgICAgY29uc3QgZXh0ID0gcGF0aC5leHRuYW1lKGZpbGVQYXRoKTtcbiAgICAgICAgY29uc3QgYmFzZSA9IGZpbGVQYXRoLnNsaWNlKDAsIC1leHQubGVuZ3RoKTtcbiAgICAgICAgZmlsZVBhdGggPSBgJHtiYXNlfS0ke3RvZGF5fSR7ZXh0fWA7XG4gICAgICB9XG5cbiAgICAgIC8vIEFwcGVuZCB0byBmaWxlXG4gICAgICBjb25zdCBsaW5lID0gSlNPTi5zdHJpbmdpZnkoZW50cnkpICsgJ1xcbic7XG4gICAgICBmcy5hcHBlbmRGaWxlU3luYyhmaWxlUGF0aCwgbGluZSwgJ3V0Zi04Jyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIFNpbGVudGx5IGZhaWwgLSBhdWRpdCBzaG91bGQgbm90IGJyZWFrIG1haW4gZnVuY3Rpb25hbGl0eVxuICAgICAgY29uc29sZS5lcnJvcignW2xsbXZlcmlmeSBhdWRpdF0gRmFpbGVkIHRvIHdyaXRlOicsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IHJlY2VudCBlbnRyaWVzXG4gICAqL1xuICBnZXRSZWNlbnQoY291bnQ6IG51bWJlciA9IDEwMCk6IEF1ZGl0RW50cnlbXSB7XG4gICAgcmV0dXJuIHRoaXMuZW50cmllcy5zbGljZSgtY291bnQpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBlbnRyaWVzIGJ5IHJpc2sgbGV2ZWxcbiAgICovXG4gIGdldEJ5Umlza0xldmVsKGxldmVsOiBzdHJpbmcpOiBBdWRpdEVudHJ5W10ge1xuICAgIHJldHVybiB0aGlzLmVudHJpZXMuZmlsdGVyKGUgPT4gZS5vdXRwdXQucmlza0xldmVsID09PSBsZXZlbCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHN1bW1hcnkgc3RhdGlzdGljc1xuICAgKi9cbiAgZ2V0U3VtbWFyeSgpOiB7XG4gICAgdG90YWxFbnRyaWVzOiBudW1iZXI7XG4gICAgYnlSaXNrTGV2ZWw6IFJlY29yZDxzdHJpbmcsIG51bWJlcj47XG4gICAgYnlBY3Rpb246IFJlY29yZDxzdHJpbmcsIG51bWJlcj47XG4gICAgYXZnTGF0ZW5jeU1zOiBudW1iZXI7XG4gICAgYmxvY2tlZENvdW50OiBudW1iZXI7XG4gIH0ge1xuICAgIGNvbnN0IGJ5Umlza0xldmVsOiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge307XG4gICAgY29uc3QgYnlBY3Rpb246IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gPSB7fTtcbiAgICBsZXQgdG90YWxMYXRlbmN5ID0gMDtcbiAgICBsZXQgYmxvY2tlZENvdW50ID0gMDtcblxuICAgIGZvciAoY29uc3QgZW50cnkgb2YgdGhpcy5lbnRyaWVzKSB7XG4gICAgICBieVJpc2tMZXZlbFtlbnRyeS5vdXRwdXQucmlza0xldmVsXSA9IChieVJpc2tMZXZlbFtlbnRyeS5vdXRwdXQucmlza0xldmVsXSB8fCAwKSArIDE7XG4gICAgICBieUFjdGlvbltlbnRyeS5hY3Rpb25dID0gKGJ5QWN0aW9uW2VudHJ5LmFjdGlvbl0gfHwgMCkgKyAxO1xuICAgICAgdG90YWxMYXRlbmN5ICs9IGVudHJ5LnBlcmZvcm1hbmNlLmxhdGVuY3lNcztcbiAgICAgIGlmIChlbnRyeS5vdXRwdXQuYWN0aW9uID09PSAnYmxvY2snKSB7XG4gICAgICAgIGJsb2NrZWRDb3VudCsrO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICB0b3RhbEVudHJpZXM6IHRoaXMuZW50cmllcy5sZW5ndGgsXG4gICAgICBieVJpc2tMZXZlbCxcbiAgICAgIGJ5QWN0aW9uLFxuICAgICAgYXZnTGF0ZW5jeU1zOiB0aGlzLmVudHJpZXMubGVuZ3RoID4gMCA/IHRvdGFsTGF0ZW5jeSAvIHRoaXMuZW50cmllcy5sZW5ndGggOiAwLFxuICAgICAgYmxvY2tlZENvdW50XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFeHBvcnQgdG8gSlNPTiBmaWxlXG4gICAqL1xuICBleHBvcnRUb0ZpbGUoZmlsZVBhdGg6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGRhdGEgPSB7XG4gICAgICBleHBvcnRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBzdW1tYXJ5OiB0aGlzLmdldFN1bW1hcnkoKSxcbiAgICAgIGVudHJpZXM6IHRoaXMuZW50cmllc1xuICAgIH07XG4gICAgZnMud3JpdGVGaWxlU3luYyhmaWxlUGF0aCwgSlNPTi5zdHJpbmdpZnkoZGF0YSwgbnVsbCwgMiksICd1dGYtOCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4cG9ydCBmb3IgR2l0SHViIChjcmVhdGVzIG1hcmtkb3duIHJlcG9ydClcbiAgICovXG4gIGV4cG9ydEZvckdpdEh1YihmaWxlUGF0aDogc3RyaW5nKTogdm9pZCB7XG4gICAgY29uc3Qgc3VtbWFyeSA9IHRoaXMuZ2V0U3VtbWFyeSgpO1xuICAgIGNvbnN0IHJlY2VudCA9IHRoaXMuZ2V0UmVjZW50KDEwKTtcblxuICAgIGxldCBtYXJrZG93biA9IGAjIGxsbXZlcmlmeSBBdWRpdCBSZXBvcnRcXG5cXG5gO1xuICAgIG1hcmtkb3duICs9IGBHZW5lcmF0ZWQ6ICR7bmV3IERhdGUoKS50b0lTT1N0cmluZygpfVxcblxcbmA7XG4gICAgbWFya2Rvd24gKz0gYCMjIFN1bW1hcnlcXG5cXG5gO1xuICAgIG1hcmtkb3duICs9IGB8IE1ldHJpYyB8IFZhbHVlIHxcXG5gO1xuICAgIG1hcmtkb3duICs9IGB8LS0tLS0tLS18LS0tLS0tLXxcXG5gO1xuICAgIG1hcmtkb3duICs9IGB8IFRvdGFsIFZlcmlmaWNhdGlvbnMgfCAke3N1bW1hcnkudG90YWxFbnRyaWVzfSB8XFxuYDtcbiAgICBtYXJrZG93biArPSBgfCBCbG9ja2VkIHwgJHtzdW1tYXJ5LmJsb2NrZWRDb3VudH0gfFxcbmA7XG4gICAgbWFya2Rvd24gKz0gYHwgQXZnIExhdGVuY3kgfCAke3N1bW1hcnkuYXZnTGF0ZW5jeU1zLnRvRml4ZWQoMil9bXMgfFxcblxcbmA7XG5cbiAgICBtYXJrZG93biArPSBgIyMgUmlzayBEaXN0cmlidXRpb25cXG5cXG5gO1xuICAgIG1hcmtkb3duICs9IGB8IExldmVsIHwgQ291bnQgfFxcbmA7XG4gICAgbWFya2Rvd24gKz0gYHwtLS0tLS0tfC0tLS0tLS18XFxuYDtcbiAgICBmb3IgKGNvbnN0IFtsZXZlbCwgY291bnRdIG9mIE9iamVjdC5lbnRyaWVzKHN1bW1hcnkuYnlSaXNrTGV2ZWwpKSB7XG4gICAgICBtYXJrZG93biArPSBgfCAke2xldmVsfSB8ICR7Y291bnR9IHxcXG5gO1xuICAgIH1cbiAgICBtYXJrZG93biArPSBgXFxuYDtcblxuICAgIG1hcmtkb3duICs9IGAjIyBSZWNlbnQgRW50cmllc1xcblxcbmA7XG4gICAgbWFya2Rvd24gKz0gYHwgVGltZSB8IEFjdGlvbiB8IFJpc2sgfCBMYXRlbmN5IHxcXG5gO1xuICAgIG1hcmtkb3duICs9IGB8LS0tLS0tfC0tLS0tLS0tfC0tLS0tLXwtLS0tLS0tLXxcXG5gO1xuICAgIGZvciAoY29uc3QgZW50cnkgb2YgcmVjZW50KSB7XG4gICAgICBjb25zdCB0aW1lID0gZW50cnkudGltZXN0YW1wLnNwbGl0KCdUJylbMV0uc3BsaXQoJy4nKVswXTtcbiAgICAgIG1hcmtkb3duICs9IGB8ICR7dGltZX0gfCAke2VudHJ5LmFjdGlvbn0gfCAke2VudHJ5Lm91dHB1dC5yaXNrTGV2ZWx9IHwgJHtlbnRyeS5wZXJmb3JtYW5jZS5sYXRlbmN5TXN9bXMgfFxcbmA7XG4gICAgfVxuXG4gICAgZnMud3JpdGVGaWxlU3luYyhmaWxlUGF0aCwgbWFya2Rvd24sICd1dGYtOCcpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIGFsbCBlbnRyaWVzXG4gICAqL1xuICBjbGVhcigpOiB2b2lkIHtcbiAgICB0aGlzLmVudHJpZXMgPSBbXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGUvZGlzYWJsZSBsb2dnaW5nXG4gICAqL1xuICBzZXRFbmFibGVkKGVuYWJsZWQ6IGJvb2xlYW4pOiB2b2lkIHtcbiAgICB0aGlzLmNvbmZpZy5lbmFibGVkID0gZW5hYmxlZDtcbiAgfVxufVxuXG4vLyBTaW5nbGV0b24gaW5zdGFuY2VcbmxldCBkZWZhdWx0TG9nZ2VyOiBBdWRpdExvZ2dlciB8IG51bGwgPSBudWxsO1xuXG4vKipcbiAqIEdldCBvciBjcmVhdGUgZGVmYXVsdCBhdWRpdCBsb2dnZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEF1ZGl0TG9nZ2VyKGNvbmZpZz86IFBhcnRpYWw8QXVkaXRDb25maWc+KTogQXVkaXRMb2dnZXIge1xuICBpZiAoIWRlZmF1bHRMb2dnZXIgfHwgY29uZmlnKSB7XG4gICAgZGVmYXVsdExvZ2dlciA9IG5ldyBBdWRpdExvZ2dlcihjb25maWcpO1xuICB9XG4gIHJldHVybiBkZWZhdWx0TG9nZ2VyO1xufVxuXG4vKipcbiAqIFF1aWNrIGxvZyBmdW5jdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRMb2coXG4gIGFjdGlvbjogQXVkaXRFbnRyeVsnYWN0aW9uJ10sXG4gIGNvbnRlbnQ6IHN0cmluZyxcbiAgcmVzdWx0OiB7XG4gICAgcmlzaz86IHsgbGV2ZWw6IHN0cmluZzsgb3ZlcmFsbDogbnVtYmVyOyBhY3Rpb246IHN0cmluZyB9O1xuICAgIGZpbmRpbmdzPzogdW5rbm93bltdO1xuICAgIG1ldGE/OiB7IGxhdGVuY3lfbXM/OiBudW1iZXI7IGVuZ2luZXNVc2VkPzogc3RyaW5nW10gfTtcbiAgfSxcbiAgcHJlc2V0Pzogc3RyaW5nXG4pOiBBdWRpdEVudHJ5IHwgbnVsbCB7XG4gIGNvbnN0IGxvZ2dlciA9IGdldEF1ZGl0TG9nZ2VyKCk7XG4gIGlmICghbG9nZ2VyKSByZXR1cm4gbnVsbDtcbiAgXG4gIGNvbnN0IGVudHJ5ID0gbG9nZ2VyLmNyZWF0ZUVudHJ5KGFjdGlvbiwgY29udGVudCwgcmVzdWx0LCBwcmVzZXQpO1xuICByZXR1cm4gbG9nZ2VyLmxvZyhlbnRyeSk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IEF1ZGl0TG9nZ2VyO1xuIl19