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.

186 lines 22.6 kB
"use strict"; /** * LLM Monitor Wrapper * * Drop-in wrapper that adds health monitoring to any LLM client. * Tracks latency, token rate, response fingerprint, and overall health. * * WHAT THIS DOES: * ✅ Wraps any LLM client with health monitoring * ✅ Tracks performance metrics over time * ✅ Detects behavioral drift and anomalies * ✅ Provides lifecycle hooks for health changes * ✅ Returns health report with each response * * WHAT THIS DOES NOT DO: * ❌ Modify LLM responses * ❌ Store prompts or responses (ephemeral only) * ❌ Make predictions about LLM behavior * ❌ Block or filter responses (monitoring only) * * PRIVACY GUARANTEE: * - No data is stored or transmitted * - All analysis is in-memory and ephemeral * - Prompts and responses are not logged * * @module wrapper/monitorLLM * @author Haiec * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.monitorLLM = monitorLLM; const baseline_1 = require("../engines/runtime/baseline"); const latency_1 = require("../engines/runtime/latency"); const token_rate_1 = require("../engines/runtime/token-rate"); const fingerprint_1 = require("../engines/runtime/fingerprint"); const structure_1 = require("../engines/runtime/structure"); const health_score_1 = require("../engines/runtime/health-score"); /** * Generates a UUID v4 (browser and Node.js compatible). */ function generateUUID() { if (typeof crypto !== 'undefined' && crypto.randomUUID) { return crypto.randomUUID(); } // Fallback for older environments return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } /** * Wraps an LLM client with health monitoring. * * @param originalClient - The LLM client to wrap * @param config - Optional monitoring configuration * @returns Monitored client with health tracking * * @example * // Basic usage * import { monitorLLM } from 'llmverify'; * * const client = monitorLLM(openaiClient); * const response = await client.generate({ prompt: 'Hello' }); * console.log(response.llmverify.health); // 'stable' * * @example * // With hooks * const client = monitorLLM(openaiClient, { * hooks: { * onUnstable: (report) => alert('LLM unstable!'), * onDegraded: (report) => console.warn('LLM degraded'), * onRecovery: (report) => console.log('LLM recovered') * } * }); * * @example * // With custom thresholds * const client = monitorLLM(openaiClient, { * thresholds: { * latencyWarnRatio: 1.5, * latencyErrorRatio: 4.0 * }, * learningRate: 0.2 * }); */ /** * Checks if client is a unified LlmClient from adapters. */ function isLlmClient(client) { return 'provider' in client && typeof client.provider === 'string'; } function monitorLLM(originalClient, config = {}) { const baselineEngine = new baseline_1.BaselineEngine(config.learningRate ?? 0.1, config.minSamplesForBaseline ?? 5); let lastHealth = 'stable'; // Engine enable flags (all enabled by default) const engines = { latency: config.engines?.latency ?? true, tokenRate: config.engines?.tokenRate ?? true, fingerprint: config.engines?.fingerprint ?? true, structure: config.engines?.structure ?? true }; return { async generate(opts) { const start = Date.now(); // Call original client const resp = await originalClient.generate(opts); const end = Date.now(); // Build call record const call = { id: generateUUID(), timestamp: start, prompt: opts.prompt, model: opts.model || 'unknown', responseText: resp.text || '', responseTokens: resp.tokens ?? (resp.text?.split(/\s+/).length || 0), latencyMs: end - start }; // Get current baseline const baseline = baselineEngine.get(); // Run enabled engines const results = []; if (engines.latency) { results.push((0, latency_1.LatencyEngine)(call, baseline, { warnRatio: config.thresholds?.latencyWarnRatio, errorRatio: config.thresholds?.latencyErrorRatio })); } if (engines.tokenRate) { results.push((0, token_rate_1.TokenRateEngine)(call, baseline, { warnRatio: config.thresholds?.tokenRateWarnRatio, errorRatio: config.thresholds?.tokenRateErrorRatio })); } let currentFingerprint; if (engines.fingerprint) { const fingerprintResult = (0, fingerprint_1.FingerprintEngine)(call, baseline.fingerprint); results.push(fingerprintResult); currentFingerprint = fingerprintResult.details.curr; } else { currentFingerprint = (0, fingerprint_1.extractFingerprint)(call.responseText); } if (engines.structure) { results.push((0, structure_1.StructureEngine)(call)); } // Calculate health score const healthReport = (0, health_score_1.HealthScoreEngine)(results); // Update baseline baselineEngine.update(call, currentFingerprint, 1); // Fire hooks on health state changes if (config.hooks) { if (healthReport.health !== lastHealth) { if (healthReport.health === 'unstable' && config.hooks.onUnstable) { config.hooks.onUnstable(healthReport); } if (healthReport.health === 'degraded' && config.hooks.onDegraded) { config.hooks.onDegraded(healthReport); } if (lastHealth !== 'stable' && healthReport.health === 'stable' && config.hooks.onRecovery) { config.hooks.onRecovery(healthReport); } } if (config.hooks.onHealthCheck) { config.hooks.onHealthCheck(healthReport); } } lastHealth = healthReport.health; return { ...resp, llmverify: healthReport }; }, getBaseline() { return baselineEngine.get(); }, getLastHealth() { return lastHealth; }, resetBaseline() { baselineEngine.reset(); lastHealth = 'stable'; } }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9uaXRvckxMTS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy93cmFwcGVyL21vbml0b3JMTE0udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0EyQkc7O0FBNEhILGdDQXFIQztBQXhPRCwwREFBNkQ7QUFDN0Qsd0RBQTJEO0FBQzNELDhEQUFnRTtBQUNoRSxnRUFBdUY7QUFDdkYsNERBQStEO0FBQy9ELGtFQUFvRTtBQXFEcEU7O0dBRUc7QUFDSCxTQUFTLFlBQVk7SUFDbkIsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLElBQUksTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3ZELE9BQU8sTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQzdCLENBQUM7SUFDRCxrQ0FBa0M7SUFDbEMsT0FBTyxzQ0FBc0MsQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxFQUFFLEVBQUU7UUFDbkUsTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ25DLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDLEdBQUcsR0FBRyxDQUFDO1FBQzFDLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUN4QixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtDRztBQUNIOztHQUVHO0FBQ0gsU0FBUyxXQUFXLENBQUMsTUFBb0I7SUFDdkMsT0FBTyxVQUFVLElBQUksTUFBTSxJQUFJLE9BQVEsTUFBb0IsQ0FBQyxRQUFRLEtBQUssUUFBUSxDQUFDO0FBQ3BGLENBQUM7QUFFRCxTQUFnQixVQUFV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Q0FBQyxhQUFhLENBQUMsWUFBWSxDQUFDLENBQUM7Z0JBQzNDLENBQUM7WUFDSCxDQUFDO1lBRUQsVUFBVSxHQUFHLFlBQVksQ0FBQyxNQUFNLENBQUM7WUFFakMsT0FBTztnQkFDTCxHQUFHLElBQUk7Z0JBQ1AsU0FBUyxFQUFFLFlBQVk7YUFDeEIsQ0FBQztRQUNKLENBQUM7UUFFRCxXQUFXO1lBQ1QsT0FBTyxjQUFjLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDOUIsQ0FBQztRQUVELGFBQWE7WUFDWCxPQUFPLFVBQVUsQ0FBQztRQUNwQixDQUFDO1FBRUQsYUFBYTtZQUNYLGNBQWMsQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUN2QixVQUFVLEdBQUcsUUFBUSxDQUFDO1FBQ3hCLENBQUM7S0FDRixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTExNIE1vbml0b3IgV3JhcHBlclxuICogXG4gKiBEcm9wLWluIHdyYXBwZXIgdGhhdCBhZGRzIGhlYWx0aCBtb25pdG9yaW5nIHRvIGFueSBMTE0gY2xpZW50LlxuICogVHJhY2tzIGxhdGVuY3ksIHRva2VuIHJhdGUsIHJlc3BvbnNlIGZpbmdlcnByaW50LCBhbmQgb3ZlcmFsbCBoZWFsdGguXG4gKiBcbiAqIFdIQVQgVEhJUyBET0VTOlxuICog4pyFIFdyYXBzIGFueSBMTE0gY2xpZW50IHdpdGggaGVhbHRoIG1vbml0b3JpbmdcbiAqIOKchSBUcmFja3MgcGVyZm9ybWFuY2UgbWV0cmljcyBvdmVyIHRpbWVcbiAqIOKchSBEZXRlY3RzIGJlaGF2aW9yYWwgZHJpZnQgYW5kIGFub21hbGllc1xuICog4pyFIFByb3ZpZGVzIGxpZmVjeWNsZSBob29rcyBmb3IgaGVhbHRoIGNoYW5nZXNcbiAqIOKchSBSZXR1cm5zIGhlYWx0aCByZXBvcnQgd2l0aCBlYWNoIHJlc3BvbnNlXG4gKiBcbiAqIFdIQVQgVEhJUyBET0VTIE5PVCBETzpcbiAqIOKdjCBNb2RpZnkgTExNIHJlc3BvbnNlc1xuICog4p2MIFN0b3JlIHByb21wdHMgb3IgcmVzcG9uc2VzIChlcGhlbWVyYWwgb25seSlcbiAqIOKdjCBNYWtlIHByZWRpY3Rpb25zIGFib3V0IExMTSBiZWhhdmlvclxuICog4p2MIEJsb2NrIG9yIGZpbHRlciByZXNwb25zZXMgKG1vbml0b3Jpbmcgb25seSlcbiAqIFxuICogUFJJVkFDWSBHVUFSQU5URUU6XG4gKiAtIE5vIGRhdGEgaXMgc3RvcmVkIG9yIHRyYW5zbWl0dGVkXG4gKiAtIEFsbCBhbmFseXNpcyBpcyBpbi1tZW1vcnkgYW5kIGVwaGVtZXJhbFxuICogLSBQcm9tcHRzIGFuZCByZXNwb25zZXMgYXJlIG5vdCBsb2dnZWRcbiAqIFxuICogQG1vZHVsZSB3cmFwcGVyL21vbml0b3JMTE1cbiAqIEBhdXRob3IgSGFpZWNcbiAqIEBsaWNlbnNlIE1JVFxuICovXG5cbmltcG9ydCB7IFxuICBDYWxsUmVjb3JkLCBcbiAgTW9uaXRvckNvbmZpZywgXG4gIEhlYWx0aFJlcG9ydCwgXG4gIEhlYWx0aFN0YXR1cyxcbiAgUmVzcG9uc2VGaW5nZXJwcmludCBcbn0gZnJvbSAnLi4vdHlwZXMvcnVudGltZSc7XG5pbXBvcnQgeyBCYXNlbGluZUVuZ2luZSB9IGZyb20gJy4uL2VuZ2luZXMvcnVudGltZS9iYXNlbGluZSc7XG5pbXBvcnQgeyBMYXRlbmN5RW5naW5lIH0gZnJvbSAnLi4vZW5naW5lcy9ydW50aW1lL2xhdGVuY3knO1xuaW1wb3J0IHsgVG9rZW5SYXRlRW5naW5lIH0gZnJvbSAnLi4vZW5naW5lcy9ydW50aW1lL3Rva2VuLXJhdGUnO1xuaW1wb3J0IHsgRmluZ2VycHJpbnRFbmdpbmUsIGV4dHJhY3RGaW5nZXJwcmludCB9IGZyb20gJy4uL2VuZ2luZXMvcnVudGltZS9maW5nZXJwcmludCc7XG5pbXBvcnQgeyBTdHJ1Y3R1cmVFbmdpbmUgfSBmcm9tICcuLi9lbmdpbmVzL3J1bnRpbWUvc3RydWN0dXJlJztcbmltcG9ydCB7IEhlYWx0aFNjb3JlRW5naW5lIH0gZnJvbSAnLi4vZW5naW5lcy9ydW50aW1lL2hlYWx0aC1zY29yZSc7XG5pbXBvcnQgeyBMbG1DbGllbnQsIExsbVJlcXVlc3QsIExsbVJlc3BvbnNlIH0gZnJvbSAnLi4vYWRhcHRlcnMvdHlwZXMnO1xuXG4vKipcbiAqIEdlbmVyaWMgTExNIGNsaWVudCBpbnRlcmZhY2UgKGxlZ2FjeSkuXG4gKiBBbnkgY2xpZW50IHdpdGggYSBnZW5lcmF0ZSBtZXRob2QgY2FuIGJlIHdyYXBwZWQuXG4gKiBAZGVwcmVjYXRlZCBVc2UgTGxtQ2xpZW50IGZyb20gYWRhcHRlcnMgZm9yIG5ldyBjb2RlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTExNQ2xpZW50IHtcbiAgZ2VuZXJhdGUob3B0czogR2VuZXJhdGVPcHRpb25zKTogUHJvbWlzZTxHZW5lcmF0ZVJlc3BvbnNlPjtcbiAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHZW5lcmF0ZU9wdGlvbnMge1xuICBwcm9tcHQ6IHN0cmluZztcbiAgbW9kZWw/OiBzdHJpbmc7XG4gIHN5c3RlbT86IHN0cmluZztcbiAgdGVtcGVyYXR1cmU/OiBudW1iZXI7XG4gIG1heFRva2Vucz86IG51bWJlcjtcbiAgW2tleTogc3RyaW5nXTogdW5rbm93bjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHZW5lcmF0ZVJlc3BvbnNlIHtcbiAgdGV4dDogc3RyaW5nO1xuICB0b2tlbnM/OiBudW1iZXI7XG4gIHRvdGFsVG9rZW5zPzogbnVtYmVyO1xuICBtb2RlbD86IHN0cmluZztcbiAgZmluaXNoUmVhc29uPzogc3RyaW5nO1xuICBba2V5OiBzdHJpbmddOiB1bmtub3duO1xufVxuXG4vKipcbiAqIFVuaW9uIHR5cGUgZm9yIGFueSBzdXBwb3J0ZWQgY2xpZW50LlxuICovXG5leHBvcnQgdHlwZSBBbnlMTE1DbGllbnQgPSBMTE1DbGllbnQgfCBMbG1DbGllbnQ7XG5cbi8qKlxuICogUmVzcG9uc2UgZnJvbSBtb25pdG9yZWQgY2xpZW50IGluY2x1ZGVzIGhlYWx0aCByZXBvcnQuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTW9uaXRvcmVkUmVzcG9uc2UgZXh0ZW5kcyBHZW5lcmF0ZVJlc3BvbnNlIHtcbiAgbGxtdmVyaWZ5OiBIZWFsdGhSZXBvcnQ7XG59XG5cbi8qKlxuICogTW9uaXRvcmVkIGNsaWVudCBpbnRlcmZhY2UuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgTW9uaXRvcmVkQ2xpZW50IHtcbiAgZ2VuZXJhdGUob3B0czogR2VuZXJhdGVPcHRpb25zKTogUHJvbWlzZTxNb25pdG9yZWRSZXNwb25zZT47XG4gIGdldEJhc2VsaW5lKCk6IFJldHVyblR5cGU8QmFzZWxpbmVFbmdpbmVbJ2dldCddPjtcbiAgZ2V0TGFzdEhlYWx0aCgpOiBIZWFsdGhTdGF0dXM7XG4gIHJlc2V0QmFzZWxpbmUoKTogdm9pZDtcbn1cblxuLyoqXG4gKiBHZW5lcmF0ZXMgYSBVVUlEIHY0IChicm93c2VyIGFuZCBOb2RlLmpzIGNvbXBhdGlibGUpLlxuICovXG5mdW5jdGlvbiBnZW5lcmF0ZVVVSUQoKTogc3RyaW5nIHtcbiAgaWYgKHR5cGVvZiBjcnlwdG8gIT09ICd1bmRlZmluZWQnICYmIGNyeXB0by5yYW5kb21VVUlEKSB7XG4gICAgcmV0dXJuIGNyeXB0by5yYW5kb21VVUlEKCk7XG4gIH1cbiAgLy8gRmFsbGJhY2sgZm9yIG9sZGVyIGVudmlyb25tZW50c1xuICByZXR1cm4gJ3h4eHh4eHh4LXh4eHgtNHh4eC15eHh4LXh4eHh4eHh4eHh4eCcucmVwbGFjZSgvW3h5XS9nLCAoYykgPT4ge1xuICAgIGNvbnN0IHIgPSAoTWF0aC5yYW5kb20oKSAqIDE2KSB8IDA7XG4gICAgY29uc3QgdiA9IGMgPT09ICd4JyA/IHIgOiAociAmIDB4MykgfCAweDg7XG4gICAgcmV0dXJuIHYudG9TdHJpbmcoMTYpO1xuICB9KTtcbn1cblxuLyoqXG4gKiBXcmFwcyBhbiBMTE0gY2xpZW50IHdpdGggaGVhbHRoIG1vbml0b3JpbmcuXG4gKiBcbiAqIEBwYXJhbSBvcmlnaW5hbENsaWVudCAtIFRoZSBMTE0gY2xpZW50IHRvIHdyYXBcbiAqIEBwYXJhbSBjb25maWcgLSBPcHRpb25hbCBtb25pdG9yaW5nIGNvbmZpZ3VyYXRpb25cbiAqIEByZXR1cm5zIE1vbml0b3JlZCBjbGllbnQgd2l0aCBoZWFsdGggdHJhY2tpbmdcbiAqIFxuICogQGV4YW1wbGVcbiAqIC8vIEJhc2ljIHVzYWdlXG4gKiBpbXBvcnQgeyBtb25pdG9yTExNIH0gZnJvbSAnbGxtdmVyaWZ5JztcbiAqIFxuICogY29uc3QgY2xpZW50ID0gbW9uaXRvckxMTShvcGVuYWlDbGllbnQpO1xuICogY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBjbGllbnQuZ2VuZXJhdGUoeyBwcm9tcHQ6ICdIZWxsbycgfSk7XG4gKiBjb25zb2xlLmxvZyhyZXNwb25zZS5sbG12ZXJpZnkuaGVhbHRoKTsgLy8gJ3N0YWJsZSdcbiAqIFxuICogQGV4YW1wbGVcbiAqIC8vIFdpdGggaG9va3NcbiAqIGNvbnN0IGNsaWVudCA9IG1vbml0b3JMTE0ob3BlbmFpQ2xpZW50LCB7XG4gKiAgIGhvb2tzOiB7XG4gKiAgICAgb25VbnN0YWJsZTogKHJlcG9ydCkgPT4gYWxlcnQoJ0xMTSB1bnN0YWJsZSEnKSxcbiAqICAgICBvbkRlZ3JhZGVkOiAocmVwb3J0KSA9PiBjb25zb2xlLndhcm4oJ0xMTSBkZWdyYWRlZCcpLFxuICogICAgIG9uUmVjb3Zlcnk6IChyZXBvcnQpID0+IGNvbnNvbGUubG9nKCdMTE0gcmVjb3ZlcmVkJylcbiAqICAgfVxuICogfSk7XG4gKiBcbiAqIEBleGFtcGxlXG4gKiAvLyBXaXRoIGN1c3RvbSB0aHJlc2hvbGRzXG4gKiBjb25zdCBjbGllbnQgPSBtb25pdG9yTExNKG9wZW5haUNsaWVudCwge1xuICogICB0aHJlc2hvbGRzOiB7XG4gKiAgICAgbGF0ZW5jeVdhcm5SYXRpbzogMS41LFxuICogICAgIGxhdGVuY3lFcnJvclJhdGlvOiA0LjBcbiAqICAgfSxcbiAqICAgbGVhcm5pbmdSYXRlOiAwLjJcbiAqIH0pO1xuICovXG4vKipcbiAqIENoZWNrcyBpZiBjbGllbnQgaXMgYSB1bmlmaWVkIExsbUNsaWVudCBmcm9tIGFkYXB0ZXJzLlxuICovXG5mdW5jdGlvbiBpc0xsbUNsaWVudChjbGllbnQ6IEFueUxMTUNsaWVudCk6IGNsaWVudCBpcyBMbG1DbGllbnQge1xuICByZXR1cm4gJ3Byb3ZpZGVyJyBpbiBjbGllbnQgJiYgdHlwZW9mIChjbGllbnQgYXMgTGxtQ2xpZW50KS5wcm92aWRlciA9PT0gJ3N0cmluZyc7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBtb25pdG9yTExNKFxuICBvcmlnaW5hbENsaWVudDogQW55TExNQ2xpZW50LFxuICBjb25maWc6IE1vbml0b3JDb25maWcgPSB7fVxuKTogTW9uaXRvcmVkQ2xpZW50IHtcbiAgY29uc3QgYmFzZWxpbmVFbmdpbmUgPSBuZXcgQmFzZWxpbmVFbmdpbmUoXG4gICAgY29uZmlnLmxlYXJuaW5nUmF0ZSA/PyAwLjEsXG4gICAgY29uZmlnLm1pblNhbXBsZXNGb3JCYXNlbGluZSA/PyA1XG4gICk7XG4gIFxuICBsZXQgbGFzdEhlYWx0aDogSGVhbHRoU3RhdHVzID0gJ3N0YWJsZSc7XG5cbiAgLy8gRW5naW5lIGVuYWJsZSBmbGFncyAoYWxsIGVuYWJsZWQgYnkgZGVmYXVsdClcbiAgY29uc3QgZW5naW5lcyA9IHtcbiAgICBsYXRlbmN5OiBjb25maWcuZW5naW5lcz8ubGF0ZW5jeSA/PyB0cnVlLFxuICAgIHRva2VuUmF0ZTogY29uZmlnLmVuZ2luZXM/LnRva2VuUmF0ZSA/PyB0cnVlLFxuICAgIGZpbmdlcnByaW50OiBjb25maWcuZW5naW5lcz8uZmluZ2VycHJpbnQgPz8gdHJ1ZSxcbiAgICBzdHJ1Y3R1cmU6IGNvbmZpZy5lbmdpbmVzPy5zdHJ1Y3R1cmUgPz8gdHJ1ZVxuICB9O1xuXG4gIHJldHVybiB7XG4gICAgYXN5bmMgZ2VuZXJhdGUob3B0czogR2VuZXJhdGVPcHRpb25zKTogUHJvbWlzZTxNb25pdG9yZWRSZXNwb25zZT4ge1xuICAgICAgY29uc3Qgc3RhcnQgPSBEYXRlLm5vdygpO1xuICAgICAgXG4gICAgICAvLyBDYWxsIG9yaWdpbmFsIGNsaWVudFxuICAgICAgY29uc3QgcmVzcCA9IGF3YWl0IG9yaWdpbmFsQ2xpZW50LmdlbmVyYXRlKG9wdHMpO1xuICAgICAgXG4gICAgICBjb25zdCBlbmQgPSBEYXRlLm5vdygpO1xuXG4gICAgICAvLyBCdWlsZCBjYWxsIHJlY29yZFxuICAgICAgY29uc3QgY2FsbDogQ2FsbFJlY29yZCA9IHtcbiAgICAgICAgaWQ6IGdlbmVyYXRlVVVJRCgpLFxuICAgICAgICB0aW1lc3RhbXA6IHN0YXJ0LFxuICAgICAgICBwcm9tcHQ6IG9wdHMucHJvbXB0LFxuICAgICAgICBtb2RlbDogb3B0cy5tb2RlbCB8fCAndW5rbm93bicsXG4gICAgICAgIHJlc3BvbnNlVGV4dDogcmVzcC50ZXh0IHx8ICcnLFxuICAgICAgICByZXNwb25zZVRva2VuczogcmVzcC50b2tlbnMgPz8gKHJlc3AudGV4dD8uc3BsaXQoL1xccysvKS5sZW5ndGggfHwgMCksXG4gICAgICAgIGxhdGVuY3lNczogZW5kIC0gc3RhcnRcbiAgICAgIH07XG5cbiAgICAgIC8vIEdldCBjdXJyZW50IGJhc2VsaW5lXG4gICAgICBjb25zdCBiYXNlbGluZSA9IGJhc2VsaW5lRW5naW5lLmdldCgpO1xuXG4gICAgICAvLyBSdW4gZW5hYmxlZCBlbmdpbmVzXG4gICAgICBjb25zdCByZXN1bHRzID0gW107XG5cbiAgICAgIGlmIChlbmdpbmVzLmxhdGVuY3kpIHtcbiAgICAgICAgcmVzdWx0cy5wdXNoKExhdGVuY3lFbmdpbmUoY2FsbCwgYmFzZWxpbmUsIHtcbiAgICAgICAgICB3YXJuUmF0aW86IGNvbmZpZy50aHJlc2hvbGRzPy5sYXRlbmN5V2FyblJhdGlvLFxuICAgICAgICAgIGVycm9yUmF0aW86IGNvbmZpZy50aHJlc2hvbGRzPy5sYXRlbmN5RXJyb3JSYXRpb1xuICAgICAgICB9KSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbmdpbmVzLnRva2VuUmF0ZSkge1xuICAgICAgICByZXN1bHRzLnB1c2goVG9rZW5SYXRlRW5naW5lKGNhbGwsIGJhc2VsaW5lLCB7XG4gICAgICAgICAgd2FyblJhdGlvOiBjb25maWcudGhyZXNob2xkcz8udG9rZW5SYXRlV2FyblJhdGlvLFxuICAgICAgICAgIGVycm9yUmF0aW86IGNvbmZpZy50aHJlc2hvbGRzPy50b2tlblJhdGVFcnJvclJhdGlvXG4gICAgICAgIH0pKTtcbiAgICAgIH1cblxuICAgICAgbGV0IGN1cnJlbnRGaW5nZXJwcmludDogUmVzcG9uc2VGaW5nZXJwcmludCB8IHVuZGVmaW5lZDtcbiAgICAgIGlmIChlbmdpbmVzLmZpbmdlcnByaW50KSB7XG4gICAgICAgIGNvbnN0IGZpbmdlcnByaW50UmVzdWx0ID0gRmluZ2VycHJpbnRFbmdpbmUoY2FsbCwgYmFzZWxpbmUuZmluZ2VycHJpbnQpO1xuICAgICAgICByZXN1bHRzLnB1c2goZmluZ2VycHJpbnRSZXN1bHQpO1xuICAgICAgICBjdXJyZW50RmluZ2VycHJpbnQgPSBmaW5nZXJwcmludFJlc3VsdC5kZXRhaWxzLmN1cnIgYXMgUmVzcG9uc2VGaW5nZXJwcmludDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGN1cnJlbnRGaW5nZXJwcmludCA9IGV4dHJhY3RGaW5nZXJwcmludChjYWxsLnJlc3BvbnNlVGV4dCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChlbmdpbmVzLnN0cnVjdHVyZSkge1xuICAgICAgICByZXN1bHRzLnB1c2goU3RydWN0dXJlRW5naW5lKGNhbGwpKTtcbiAgICAgIH1cblxuICAgICAgLy8gQ2FsY3VsYXRlIGhlYWx0aCBzY29yZVxuICAgICAgY29uc3QgaGVhbHRoUmVwb3J0ID0gSGVhbHRoU2NvcmVFbmdpbmUocmVzdWx0cyk7XG5cbiAgICAgIC8vIFVwZGF0ZSBiYXNlbGluZVxuICAgICAgYmFzZWxpbmVFbmdpbmUudXBkYXRlKGNhbGwsIGN1cnJlbnRGaW5nZXJwcmludCwgMSk7XG5cbiAgICAgIC8vIEZpcmUgaG9va3Mgb24gaGVhbHRoIHN0YXRlIGNoYW5nZXNcbiAgICAgIGlmIChjb25maWcuaG9va3MpIHtcbiAgICAgICAgaWYgKGhlYWx0aFJlcG9ydC5oZWFsdGggIT09IGxhc3RIZWFsdGgpIHtcbiAgICAgICAgICBpZiAoaGVhbHRoUmVwb3J0LmhlYWx0aCA9PT0gJ3Vuc3RhYmxlJyAmJiBjb25maWcuaG9va3Mub25VbnN0YWJsZSkge1xuICAgICAgICAgICAgY29uZmlnLmhvb2tzLm9uVW5zdGFibGUoaGVhbHRoUmVwb3J0KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGhlYWx0aFJlcG9ydC5oZWFsdGggPT09ICdkZWdyYWRlZCcgJiYgY29uZmlnLmhvb2tzLm9uRGVncmFkZWQpIHtcbiAgICAgICAgICAgIGNvbmZpZy5ob29rcy5vbkRlZ3JhZGVkKGhlYWx0aFJlcG9ydCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChsYXN0SGVhbHRoICE9PSAnc3RhYmxlJyAmJiBoZWFsdGhSZXBvcnQuaGVhbHRoID09PSAnc3RhYmxlJyAmJiBjb25maWcuaG9va3Mub25SZWNvdmVyeSkge1xuICAgICAgICAgICAgY29uZmlnLmhvb2tzLm9uUmVjb3ZlcnkoaGVhbHRoUmVwb3J0KTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGNvbmZpZy5ob29rcy5vbkhlYWx0aENoZWNrKSB7XG4gICAgICAgICAgY29uZmlnLmhvb2tzLm9uSGVhbHRoQ2hlY2soaGVhbHRoUmVwb3J0KTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBsYXN0SGVhbHRoID0gaGVhbHRoUmVwb3J0LmhlYWx0aDtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ucmVzcCxcbiAgICAgICAgbGxtdmVyaWZ5OiBoZWFsdGhSZXBvcnRcbiAgICAgIH07XG4gICAgfSxcblxuICAgIGdldEJhc2VsaW5lKCkge1xuICAgICAgcmV0dXJuIGJhc2VsaW5lRW5naW5lLmdldCgpO1xuICAgIH0sXG5cbiAgICBnZXRMYXN0SGVhbHRoKCkge1xuICAgICAgcmV0dXJuIGxhc3RIZWFsdGg7XG4gICAgfSxcblxuICAgIHJlc2V0QmFzZWxpbmUoKSB7XG4gICAgICBiYXNlbGluZUVuZ2luZS5yZXNldCgpO1xuICAgICAgbGFzdEhlYWx0aCA9ICdzdGFibGUnO1xuICAgIH1cbiAgfTtcbn1cbiJdfQ==