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
JavaScript
"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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xvZ2dpbmcvbG9nZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7Ozs7O0dBTUc7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTJZSCw4QkFLQztBQUtELDhCQUVDO0FBS0Qsa0NBRUM7QUE1WkQsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3Qix1Q0FBeUI7QUFDekIsK0JBQW9DO0FBR3BDOztHQUVHO0FBQ0gsSUFBWSxRQUtYO0FBTEQsV0FBWSxRQUFRO0lBQ2xCLDJCQUFlLENBQUE7SUFDZix5QkFBYSxDQUFBO0lBQ2IseUJBQWEsQ0FBQTtJQUNiLDJCQUFlLENBQUE7QUFDakIsQ0FBQyxFQUxXLFFBQVEsd0JBQVIsUUFBUSxRQUtuQjtBQXFDRDs7R0FFRztBQUNILE1BQU0sY0FBYyxHQUFpQjtJQUNuQyxPQUFPLEVBQUUsSUFBSTtJQUNiLEtBQUssRUFBRSxRQUFRLENBQUMsSUFBSTtJQUNwQixNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQztJQUNyRCxXQUFXLEVBQUUsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLEVBQUUsT0FBTztJQUN0QyxRQUFRLEVBQUUsRUFBRTtJQUNaLGVBQWUsRUFBRSxJQUFJO0lBQ3JCLFdBQVcsRUFBRSxJQUFJO0NBQ2xCLENBQUM7QUFFRjs7R0FFRztBQUNILE1BQWEsTUFBTTtJQUtqQixZQUFZLE1BQThCO1FBSGxDLHFCQUFnQixHQUFrQixJQUFJLENBQUM7UUFDdkMscUJBQWdCLEdBQWtCLElBQUksQ0FBQztRQUc3QyxJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsR0FBRyxjQUFjLEVBQUUsR0FBRyxNQUFNLEVBQUUsQ0FBQztRQUMvQyxJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUM1QixDQUFDO0lBRUQ7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LEdBQUcsSUFBSSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDcEMsQ0FBQztJQUNELE9BQU8sWUFBWSxDQUFDO0FBQ3RCLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxNQUFjO0lBQ3RDLFlBQVksR0FBRyxNQUFNLENBQUM7QUFDeEIsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IsV0FBVztJQUN6QixZQUFZLEdBQUcsSUFBSSxDQUFDO0FBQ3RCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFN0cnVjdHVyZWQgTG9nZ2luZyBTeXN0ZW1cbiAqIFxuICogSW5kdXN0cnktZ3JhZGUgbG9nZ2luZyB3aXRoIHJvdGF0aW9uLCByZXF1ZXN0IHRyYWNraW5nLCBhbmQgYXVkaXQgdHJhaWxzXG4gKiBcbiAqIEBtb2R1bGUgbG9nZ2luZy9sb2dnZXJcbiAqL1xuXG5pbXBvcnQgKiBhcyBmcyBmcm9tICdmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0IHsgdjQgYXMgdXVpZHY0IH0gZnJvbSAndXVpZCc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi9lcnJvcnMvY29kZXMnO1xuXG4vKipcbiAqIExvZyBsZXZlbHNcbiAqL1xuZXhwb3J0IGVudW0gTG9nTGV2ZWwge1xuICBERUJVRyA9ICdkZWJ1ZycsXG4gIElORk8gPSAnaW5mbycsXG4gIFdBUk4gPSAnd2FybicsXG4gIEVSUk9SID0gJ2Vycm9yJ1xufVxuXG4vKipcbiAqIExvZyBlbnRyeSBzdHJ1Y3R1cmVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dFbnRyeSB7XG4gIHRpbWVzdGFtcDogc3RyaW5nO1xuICBsZXZlbDogTG9nTGV2ZWw7XG4gIHJlcXVlc3RJZDogc3RyaW5nO1xuICBtZXNzYWdlOiBzdHJpbmc7XG4gIGRhdGE/OiBhbnk7XG4gIGR1cmF0aW9uPzogbnVtYmVyO1xuICBlcnJvcj86IHtcbiAgICBtZXNzYWdlOiBzdHJpbmc7XG4gICAgY29kZT86IEVycm9yQ29kZTtcbiAgICBzdGFjaz86IHN0cmluZztcbiAgfTtcbiAgbWV0YWRhdGE/OiB7XG4gICAgdmVyc2lvbjogc3RyaW5nO1xuICAgIHBpZDogbnVtYmVyO1xuICAgIGhvc3RuYW1lOiBzdHJpbmc7XG4gIH07XG59XG5cbi8qKlxuICogTG9nZ2VyIGNvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBMb2dnZXJDb25maWcge1xuICBlbmFibGVkOiBib29sZWFuO1xuICBsZXZlbDogTG9nTGV2ZWw7XG4gIGxvZ0Rpcj86IHN0cmluZztcbiAgbWF4RmlsZVNpemU/OiBudW1iZXI7IC8vIGJ5dGVzXG4gIG1heEZpbGVzPzogbnVtYmVyO1xuICBpbmNsdWRlTWV0YWRhdGE/OiBib29sZWFuO1xuICBzYW5pdGl6ZVBJST86IGJvb2xlYW47XG59XG5cbi8qKlxuICogRGVmYXVsdCBjb25maWd1cmF0aW9uXG4gKi9cbmNvbnN0IERFRkFVTFRfQ09ORklHOiBMb2dnZXJDb25maWcgPSB7XG4gIGVuYWJsZWQ6IHRydWUsXG4gIGxldmVsOiBMb2dMZXZlbC5JTkZPLFxuICBsb2dEaXI6IHBhdGguam9pbihvcy5ob21lZGlyKCksICcubGxtdmVyaWZ5JywgJ2xvZ3MnKSxcbiAgbWF4RmlsZVNpemU6IDEwICogMTAyNCAqIDEwMjQsIC8vIDEwTUJcbiAgbWF4RmlsZXM6IDEwLFxuICBpbmNsdWRlTWV0YWRhdGE6IHRydWUsXG4gIHNhbml0aXplUElJOiB0cnVlXG59O1xuXG4vKipcbiAqIExvZ2dlciBjbGFzc1xuICovXG5leHBvcnQgY2xhc3MgTG9nZ2VyIHtcbiAgcHJpdmF0ZSBjb25maWc6IExvZ2dlckNvbmZpZztcbiAgcHJpdmF0ZSBjdXJyZW50UmVxdWVzdElkOiBzdHJpbmcgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSByZXF1ZXN0U3RhcnRUaW1lOiBudW1iZXIgfCBudWxsID0gbnVsbDtcbiAgXG4gIGNvbnN0cnVjdG9yKGNvbmZpZz86IFBhcnRpYWw8TG9nZ2VyQ29uZmlnPikge1xuICAgIHRoaXMuY29uZmlnID0geyAuLi5ERUZBVUxUX0NPTkZJRywgLi4uY29uZmlnIH07XG4gICAgdGhpcy5lbnN1cmVMb2dEaXJlY3RvcnkoKTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEVuc3VyZSBsb2cgZGlyZWN0b3J5IGV4aXN0c1xuICAgKi9cbiAgcHJpdmF0ZSBlbnN1cmVMb2dEaXJlY3RvcnkoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmVuYWJsZWQgJiYgdGhpcy5jb25maWcubG9nRGlyKSB7XG4gICAgICB0cnkge1xuICAgICAgICBmcy5ta2RpclN5bmModGhpcy5jb25maWcubG9nRGlyLCB7IHJlY3Vyc2l2ZTogdHJ1ZSB9KTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ0ZhaWxlZCB0byBjcmVhdGUgbG9nIGRpcmVjdG9yeTonLCBlcnJvcik7XG4gICAgICAgIHRoaXMuY29uZmlnLmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZW5lcmF0ZSBuZXcgcmVxdWVzdCBJRFxuICAgKi9cbiAgcHVibGljIHN0YXJ0UmVxdWVzdCgpOiBzdHJpbmcge1xuICAgIHRoaXMuY3VycmVudFJlcXVlc3RJZCA9IHV1aWR2NCgpO1xuICAgIHRoaXMucmVxdWVzdFN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJlcXVlc3RJZDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEVuZCByZXF1ZXN0IGFuZCByZXR1cm4gZHVyYXRpb25cbiAgICovXG4gIHB1YmxpYyBlbmRSZXF1ZXN0KCk6IG51bWJlciB8IG51bGwge1xuICAgIGlmICh0aGlzLnJlcXVlc3RTdGFydFRpbWUpIHtcbiAgICAgIGNvbnN0IGR1cmF0aW9uID0gRGF0ZS5ub3coKSAtIHRoaXMucmVxdWVzdFN0YXJ0VGltZTtcbiAgICAgIHRoaXMucmVxdWVzdFN0YXJ0VGltZSA9IG51bGw7XG4gICAgICByZXR1cm4gZHVyYXRpb247XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG4gIFxuICAvKipcbiAgICogR2V0IGN1cnJlbnQgcmVxdWVzdCBJRFxuICAgKi9cbiAgcHVibGljIGdldFJlcXVlc3RJZCgpOiBzdHJpbmcge1xuICAgIGlmICghdGhpcy5jdXJyZW50UmVxdWVzdElkKSB7XG4gICAgICB0aGlzLmN1cnJlbnRSZXF1ZXN0SWQgPSB1dWlkdjQoKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudFJlcXVlc3RJZDtcbiAgfVxuICBcbiAgLyoqXG4gICAqIEdldCBsb2cgZmlsZSBwYXRoIGZvciB0b2RheVxuICAgKi9cbiAgcHJpdmF0ZSBnZXRMb2dGaWxlUGF0aCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IGRhdGUgPSBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXTtcbiAgICByZXR1cm4gcGF0aC5qb2luKHRoaXMuY29uZmlnLmxvZ0RpciEsIGBsbG12ZXJpZnktJHtkYXRlfS5qc29ubGApO1xuICB9XG4gIFxuICAvKipcbiAgICogU2FuaXRpemUgZGF0YSB0byByZW1vdmUgUElJXG4gICAqL1xuICBwcml2YXRlIHNhbml0aXplRGF0YShkYXRhOiBhbnkpOiBhbnkge1xuICAgIGlmICghdGhpcy5jb25maWcuc2FuaXRpemVQSUkpIHJldHVybiBkYXRhO1xuICAgIFxuICAgIGlmICh0eXBlb2YgZGF0YSA9PT0gJ3N0cmluZycpIHtcbiAgICAgIC8vIFJlbW92ZSBlbWFpbCBhZGRyZXNzZXNcbiAgICAgIGRhdGEgPSBkYXRhLnJlcGxhY2UoL1thLXpBLVowLTkuXyUrLV0rQFthLXpBLVowLTkuLV0rXFwuW2EtekEtWl17Mix9L2csICdbRU1BSUxdJyk7XG4gICAgICAvLyBSZW1vdmUgcGhvbmUgbnVtYmVyc1xuICAgICAgZGF0YSA9IGRhdGEucmVwbGFjZSgvXFxiXFxkezN9Wy0uXT9cXGR7M31bLS5dP1xcZHs0fVxcYi9nLCAnW1BIT05FXScpO1xuICAgICAgLy8gUmVtb3ZlIFNTTlxuICAgICAgZGF0YSA9IGRhdGEucmVwbGFjZSgvXFxiXFxkezN9LVxcZHsyfS1cXGR7NH1cXGIvZywgJ1tTU05dJyk7XG4gICAgICAvLyBSZW1vdmUgQVBJIGtleXMgKGNvbW1vbiBwYXR0ZXJucylcbiAgICAgIGRhdGEgPSBkYXRhLnJlcGxhY2UoL1xcYltBLVphLXowLTldezMyLH1cXGIvZywgJ1tBUElfS0VZXScpO1xuICAgIH0gZWxzZSBpZiAodHlwZW9mIGRhdGEgPT09ICdvYmplY3QnICYmIGRhdGEgIT09IG51bGwpIHtcbiAgICAgIGNvbnN0IHNhbml0aXplZDogYW55ID0gQXJyYXkuaXNBcnJheShkYXRhKSA/IFtdIDoge307XG4gICAgICBmb3IgKGNvbnN0IGtleSBpbiBkYXRhKSB7XG4gICAgICAgIC8vIFNraXAgc2Vuc2l0aXZlIGtleXNcbiAgICAgICAgaWYgKFsncGFzc3dvcmQnLCAnYXBpS2V5JywgJ3Rva2VuJywgJ3NlY3JldCcsICdhdXRob3JpemF0aW9uJ10uaW5jbHVkZXMoa2V5LnRvTG93ZXJDYXNlKCkpKSB7XG4gICAgICAgICAgc2FuaXRpemVkW2tleV0gPSAnW1JFREFDVEVEXSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2FuaXRpemVkW2tleV0gPSB0aGlzLnNhbml0aXplRGF0YShkYXRhW2tleV0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gc2FuaXRpemVkO1xuICAgIH1cbiAgICBcbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFdyaXRlIGxvZyBlbnRyeVxuICAgKi9cbiAgcHJpdmF0ZSB3cml0ZUxvZyhlbnRyeTogTG9nRW50cnkpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmVuYWJsZWQpIHJldHVybjtcbiAgICBcbiAgICB0cnkge1xuICAgICAgY29uc3QgbG9nRmlsZSA9IHRoaXMuZ2V0TG9nRmlsZVBhdGgoKTtcbiAgICAgIGNvbnN0IGxpbmUgPSBKU09OLnN0cmluZ2lmeShlbnRyeSkgKyAnXFxuJztcbiAgICAgIFxuICAgICAgZnMuYXBwZW5kRmlsZVN5bmMobG9nRmlsZSwgbGluZSwgJ3V0Zi04Jyk7XG4gICAgICBcbiAgICAgIC8vIENoZWNrIGZpbGUgc2l6ZSBhbmQgcm90YXRlIGlmIG5lZWRlZFxuICAgICAgdGhpcy5yb3RhdGVMb2dzSWZOZWVkZWQobG9nRmlsZSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEZhaWwgc2lsZW50bHkgdG8gbm90IGRpc3J1cHQgbWFpbiBvcGVyYXRpb25cbiAgICAgIGlmIChwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50Jykge1xuICAgICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gd3JpdGUgbG9nOicsIGVycm9yKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSb3RhdGUgbG9ncyBpZiBmaWxlIGV4Y2VlZHMgbWF4IHNpemVcbiAgICovXG4gIHByaXZhdGUgcm90YXRlTG9nc0lmTmVlZGVkKGxvZ0ZpbGU6IHN0cmluZyk6IHZvaWQge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdGF0cyA9IGZzLnN0YXRTeW5jKGxvZ0ZpbGUpO1xuICAgICAgXG4gICAgICBpZiAoc3RhdHMuc2l6ZSA+IHRoaXMuY29uZmlnLm1heEZpbGVTaXplISkge1xuICAgICAgICBjb25zdCB0aW1lc3RhbXAgPSBEYXRlLm5vdygpO1xuICAgICAgICBjb25zdCByb3RhdGVkRmlsZSA9IGxvZ0ZpbGUucmVwbGFjZSgnLmpzb25sJywgYC4ke3RpbWVzdGFtcH0uanNvbmxgKTtcbiAgICAgICAgZnMucmVuYW1lU3luYyhsb2dGaWxlLCByb3RhdGVkRmlsZSk7XG4gICAgICAgIFxuICAgICAgICAvLyBDbGVhbiB1cCBvbGQgZmlsZXNcbiAgICAgICAgdGhpcy5jbGVhbnVwT2xkTG9ncygpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBJZ25vcmUgcm90YXRpb24gZXJyb3JzXG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogQ2xlYW4gdXAgb2xkIGxvZyBmaWxlc1xuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbnVwT2xkTG9ncygpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmxvZ0RpcikgcmV0dXJuO1xuICAgIFxuICAgIHRyeSB7XG4gICAgICBjb25zdCBmaWxlcyA9IGZzLnJlYWRkaXJTeW5jKHRoaXMuY29uZmlnLmxvZ0RpcilcbiAgICAgICAgLmZpbHRlcihmID0+IGYuc3RhcnRzV2l0aCgnbGxtdmVyaWZ5LScpICYmIGYuZW5kc1dpdGgoJy5qc29ubCcpKVxuICAgICAgICAubWFwKGYgPT4gKHtcbiAgICAgICAgICBuYW1lOiBmLFxuICAgICAgICAgIHBhdGg6IHBhdGguam9pbih0aGlzLmNvbmZpZy5sb2dEaXIhLCBmKSxcbiAgICAgICAgICB0aW1lOiBmcy5zdGF0U3luYyhwYXRoLmpvaW4odGhpcy5jb25maWcubG9nRGlyISwgZikpLm10aW1lLmdldFRpbWUoKVxuICAgICAgICB9KSlcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGIudGltZSAtIGEudGltZSk7XG4gICAgICBcbiAgICAgIC8vIEtlZXAgb25seSBtYXhGaWxlc1xuICAgICAgaWYgKGZpbGVzLmxlbmd0aCA+IHRoaXMuY29uZmlnLm1heEZpbGVzISkge1xuICAgICAgICBmaWxlcy5zbGljZSh0aGlzLmNvbmZpZy5tYXhGaWxlcyEpLmZvckVhY2goZmlsZSA9PiB7XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGZzLnVubGlua1N5bmMoZmlsZS5wYXRoKTtcbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgLy8gSWdub3JlIGRlbGV0aW9uIGVycm9yc1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIElnbm9yZSBjbGVhbnVwIGVycm9yc1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIENyZWF0ZSBsb2cgZW50cnlcbiAgICovXG4gIHByaXZhdGUgY3JlYXRlRW50cnkobGV2ZWw6IExvZ0xldmVsLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBhbnksIGVycm9yPzogRXJyb3IpOiBMb2dFbnRyeSB7XG4gICAgY29uc3QgZW50cnk6IExvZ0VudHJ5ID0ge1xuICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCksXG4gICAgICBsZXZlbCxcbiAgICAgIHJlcXVlc3RJZDogdGhpcy5nZXRSZXF1ZXN0SWQoKSxcbiAgICAgIG1lc3NhZ2U6IHRoaXMuY29uZmlnLnNhbml0aXplUElJID8gdGhpcy5zYW5pdGl6ZURhdGEobWVzc2FnZSkgOiBtZXNzYWdlXG4gICAgfTtcbiAgICBcbiAgICBpZiAoZGF0YSkge1xuICAgICAgZW50cnkuZGF0YSA9IHRoaXMuc2FuaXRpemVEYXRhKGRhdGEpO1xuICAgIH1cbiAgICBcbiAgICBpZiAodGhpcy5yZXF1ZXN0U3RhcnRUaW1lKSB7XG4gICAgICBlbnRyeS5kdXJhdGlvbiA9IERhdGUubm93KCkgLSB0aGlzLnJlcXVlc3RTdGFydFRpbWU7XG4gICAgfVxuICAgIFxuICAgIGlmIChlcnJvcikge1xuICAgICAgZW50cnkuZXJyb3IgPSB7XG4gICAgICAgIG1lc3NhZ2U6IGVycm9yLm1lc3NhZ2UsXG4gICAgICAgIGNvZGU6IChlcnJvciBhcyBhbnkpLmNvZGUsXG4gICAgICAgIHN0YWNrOiBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ2RldmVsb3BtZW50JyA/IGVycm9yLnN0YWNrIDogdW5kZWZpbmVkXG4gICAgICB9O1xuICAgIH1cbiAgICBcbiAgICBpZiAodGhpcy5jb25maWcuaW5jbHVkZU1ldGFkYXRhKSB7XG4gICAgICBlbnRyeS5tZXRhZGF0YSA9IHtcbiAgICAgICAgdmVyc2lvbjogcmVxdWlyZSgnLi4vLi4vcGFja2FnZS5qc29uJykudmVyc2lvbixcbiAgICAgICAgcGlkOiBwcm9jZXNzLnBpZCxcbiAgICAgICAgaG9zdG5hbWU6IG9zLmhvc3RuYW1lKClcbiAgICAgIH07XG4gICAgfVxuICAgIFxuICAgIHJldHVybiBlbnRyeTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENoZWNrIGlmIHNob3VsZCBsb2cgYXQgbGV2ZWxcbiAgICovXG4gIHByaXZhdGUgc2hvdWxkTG9nKGxldmVsOiBMb2dMZXZlbCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGxldmVscyA9IFtMb2dMZXZlbC5ERUJVRywgTG9nTGV2ZWwuSU5GTywgTG9nTGV2ZWwuV0FSTiwgTG9nTGV2ZWwuRVJST1JdO1xuICAgIGNvbnN0IGNvbmZpZ0xldmVsSW5kZXggPSBsZXZlbHMuaW5kZXhPZih0aGlzLmNvbmZpZy5sZXZlbCk7XG4gICAgY29uc3QgbWVzc2FnZUxldmVsSW5kZXggPSBsZXZlbHMuaW5kZXhPZihsZXZlbCk7XG4gICAgXG4gICAgcmV0dXJuIG1lc3NhZ2VMZXZlbEluZGV4ID49IGNvbmZpZ0xldmVsSW5kZXg7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2cgZGVidWcgbWVzc2FnZVxuICAgKi9cbiAgcHVibGljIGRlYnVnKG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IGFueSk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNob3VsZExvZyhMb2dMZXZlbC5ERUJVRykpIHtcbiAgICAgIHRoaXMud3JpdGVMb2codGhpcy5jcmVhdGVFbnRyeShMb2dMZXZlbC5ERUJVRywgbWVzc2FnZSwgZGF0YSkpO1xuICAgIH1cbiAgfVxuICBcbiAgLyoqXG4gICAqIExvZyBpbmZvIG1lc3NhZ2VcbiAgICovXG4gIHB1YmxpYyBpbmZvKG1lc3NhZ2U6IHN0cmluZywgZGF0YT86IGFueSk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNob3VsZExvZyhMb2dMZXZlbC5JTkZPKSkge1xuICAgICAgdGhpcy53cml0ZUxvZyh0aGlzLmNyZWF0ZUVudHJ5KExvZ0xldmVsLklORk8sIG1lc3NhZ2UsIGRhdGEpKTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBMb2cgd2FybmluZyBtZXNzYWdlXG4gICAqL1xuICBwdWJsaWMgd2FybihtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBhbnkpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zaG91bGRMb2coTG9nTGV2ZWwuV0FSTikpIHtcbiAgICAgIHRoaXMud3JpdGVMb2codGhpcy5jcmVhdGVFbnRyeShMb2dMZXZlbC5XQVJOLCBtZXNzYWdlLCBkYXRhKSk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogTG9nIGVycm9yIG1lc3NhZ2VcbiAgICovXG4gIHB1YmxpYyBlcnJvcihtZXNzYWdlOiBzdHJpbmcsIGVycm9yPzogRXJyb3IsIGRhdGE/OiBhbnkpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zaG91bGRMb2coTG9nTGV2ZWwuRVJST1IpKSB7XG4gICAgICB0aGlzLndyaXRlTG9nKHRoaXMuY3JlYXRlRW50cnkoTG9nTGV2ZWwuRVJST1IsIG1lc3NhZ2UsIGRhdGEsIGVycm9yKSk7XG4gICAgfVxuICB9XG4gIFxuICAvKipcbiAgICogUmVhZCBsb2dzIGZvciBhIHNwZWNpZmljIGRhdGVcbiAgICovXG4gIHB1YmxpYyByZWFkTG9ncyhkYXRlPzogc3RyaW5nKTogTG9nRW50cnlbXSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5sb2dEaXIpIHJldHVybiBbXTtcbiAgICBcbiAgICBjb25zdCBkYXRlU3RyID0gZGF0ZSB8fCBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKCkuc3BsaXQoJ1QnKVswXTtcbiAgICBjb25zdCBsb2dGaWxlID0gcGF0aC5qb2luKHRoaXMuY29uZmlnLmxvZ0RpciwgYGxsbXZlcmlmeS0ke2RhdGVTdHJ9Lmpzb25sYCk7XG4gICAgXG4gICAgaWYgKCFmcy5leGlzdHNTeW5jKGxvZ0ZpbGUpKSByZXR1cm4gW107XG4gICAgXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBmcy5yZWFkRmlsZVN5bmMobG9nRmlsZSwgJ3V0Zi04Jyk7XG4gICAgICByZXR1cm4gY29udGVudFxuICAgICAgICAuc3BsaXQoJ1xcbicpXG4gICAgICAgIC5maWx0ZXIobGluZSA9PiBsaW5lLnRyaW0oKSlcbiAgICAgICAgLm1hcChsaW5lID0+IEpTT04ucGFyc2UobGluZSkgYXMgTG9nRW50cnkpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gcmVhZCBsb2dzOicsIGVycm9yKTtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBHZXQgbG9nIHN0YXRpc3RpY3NcbiAgICovXG4gIHB1YmxpYyBnZXRTdGF0cyhkYXRlPzogc3RyaW5nKToge1xuICAgIHRvdGFsRW50cmllczogbnVtYmVyO1xuICAgIGJ5TGV2ZWw6IFJlY29yZDxMb2dMZXZlbCwgbnVtYmVyPjtcbiAgICBlcnJvckNvdW50OiBudW1iZXI7XG4gICAgYXZnRHVyYXRpb246IG51bWJlcjtcbiAgfSB7XG4gICAgY29uc3QgbG9ncyA9IHRoaXMucmVhZExvZ3MoZGF0ZSk7XG4gICAgXG4gICAgY29uc3Qgc3RhdHMgPSB7XG4gICAgICB0b3RhbEVudHJpZXM6IGxvZ3MubGVuZ3RoLFxuICAgICAgYnlMZXZlbDoge1xuICAgICAgICBbTG9nTGV2ZWwuREVCVUddOiAwLFxuICAgICAgICBbTG9nTGV2ZWwuSU5GT106IDAsXG4gICAgICAgIFtMb2dMZXZlbC5XQVJOXTogMCxcbiAgICAgICAgW0xvZ0xldmVsLkVSUk9SXTogMFxuICAgICAgfSxcbiAgICAgIGVycm9yQ291bnQ6IDAsXG4gICAgICBhdmdEdXJhdGlvbjogMFxuICAgIH07XG4gICAgXG4gICAgbGV0IHRvdGFsRHVyYXRpb24gPSAwO1xuICAgIGxldCBkdXJhdGlvbkNvdW50ID0gMDtcbiAgICBcbiAgICBsb2dzLmZvckVhY2gobG9nID0+IHtcbiAgICAgIHN0YXRzLmJ5TGV2ZWxbbG9nLmxldmVsXSsrO1xuICAgICAgaWYgKGxvZy5lcnJvcikgc3RhdHMuZXJyb3JDb3VudCsrO1xuICAgICAgaWYgKGxvZy5kdXJhdGlvbikge1xuICAgICAgICB0b3RhbER1cmF0aW9uICs9IGxvZy5kdXJhdGlvbjtcbiAgICAgICAgZHVyYXRpb25Db3VudCsrO1xuICAgICAgfVxuICAgIH0pO1xuICAgIFxuICAgIGlmIChkdXJhdGlvbkNvdW50ID4gMCkge1xuICAgICAgc3RhdHMuYXZnRHVyYXRpb24gPSB0b3RhbER1cmF0aW9uIC8gZHVyYXRpb25Db3VudDtcbiAgICB9XG4gICAgXG4gICAgcmV0dXJuIHN0YXRzO1xuICB9XG59XG5cbi8qKlxuICogR2xvYmFsIGxvZ2dlciBpbnN0YW5jZVxuICovXG5sZXQgZ2xvYmFsTG9nZ2VyOiBMb2dnZXIgfCBudWxsID0gbnVsbDtcblxuLyoqXG4gKiBHZXQgZ2xvYmFsIGxvZ2dlclxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TG9nZ2VyKGNvbmZpZz86IFBhcnRpYWw8TG9nZ2VyQ29uZmlnPik6IExvZ2dlciB7XG4gIGlmICghZ2xvYmFsTG9nZ2VyKSB7XG4gICAgZ2xvYmFsTG9nZ2VyID0gbmV3IExvZ2dlcihjb25maWcpO1xuICB9XG4gIHJldHVybiBnbG9iYWxMb2dnZXI7XG59XG5cbi8qKlxuICogU2V0IGdsb2JhbCBsb2dnZXJcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldExvZ2dlcihsb2dnZXI6IExvZ2dlcik6IHZvaWQge1xuICBnbG9iYWxMb2dnZXIgPSBsb2dnZXI7XG59XG5cbi8qKlxuICogUmVzZXQgZ2xvYmFsIGxvZ2dlclxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRMb2dnZXIoKTogdm9pZCB7XG4gIGdsb2JhbExvZ2dlciA9IG51bGw7XG59XG4iXX0=