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.
305 lines • 38.2 kB
JavaScript
"use strict";
/**
* llmverify - Main Verification Function
*
* AI Output Verification with honest limitations.
* Local-first, privacy-preserving, transparent.
*
* @module verify
* @author Haiec
* @license MIT
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.verify = verify;
const uuid_1 = require("uuid");
const config_1 = require("./types/config");
const constants_1 = require("./constants");
const errors_1 = require("./errors");
const codes_1 = require("./errors/codes");
const hallucination_1 = require("./engines/hallucination");
const consistency_1 = require("./engines/consistency");
const json_validator_1 = require("./engines/json-validator");
const baseline_1 = require("./csm6/baseline");
const risk_scoring_1 = require("./engines/risk-scoring");
const config_2 = require("./config");
const logger_1 = require("./logging/logger");
const audit_1 = require("./logging/audit");
const storage_1 = require("./baseline/storage");
const registry_1 = require("./plugins/registry");
/**
* Main verification function
*
* PRIVACY GUARANTEE: Free tier never makes network requests.
* All processing is local unless explicit API key is provided.
*
* @param options - Verification options
* @returns Complete verification result with limitations
*
* @example
* ```typescript
* const result = await verify({
* content: "The Earth is flat."
* });
*
* console.log(result.risk.level); // "moderate"
* console.log(result.limitations); // ["Pattern-based detection only", ...]
* ```
*/
async function verify(options) {
const startTime = Date.now();
const verificationId = (0, uuid_1.v4)();
// Initialize logging
const logger = (0, logger_1.getLogger)();
const auditLogger = (0, audit_1.getAuditLogger)();
const requestId = logger.startRequest();
logger.info('Verification started', {
requestId,
contentLength: options.content.length,
hasContext: !!options.context
});
// Load config from file/env, then merge with runtime options
const baseConfig = (0, config_2.loadConfig)(options.config);
const config = mergeConfig(baseConfig);
// CRITICAL: Validate privacy compliance
validatePrivacyCompliance(config);
// Validate input
validateInput(options.content, config);
const result = {
limitations: [],
notChecked: []
};
const enginesUsed = [];
try {
const enginePromises = [];
// Hallucination detection
if (config.engines.hallucination.enabled &&
!options.context?.skipEngines?.includes('hallucination')) {
const engine = new hallucination_1.HallucinationEngine(config);
enginesUsed.push('hallucination');
enginePromises.push(engine.detect(options.content).then(res => {
result.hallucination = res;
result.limitations?.push(...res.limitations);
}));
}
else {
result.notChecked?.push('hallucination');
}
// Consistency check
if (config.engines.consistency.enabled &&
!options.context?.skipEngines?.includes('consistency')) {
const engine = new consistency_1.ConsistencyEngine(config);
enginesUsed.push('consistency');
enginePromises.push(engine.check(options.content).then(res => {
result.consistency = res;
result.limitations?.push(...res.limitations);
}));
}
else {
result.notChecked?.push('consistency');
}
// JSON validation
if (config.engines.jsonValidator.enabled &&
options.context?.isJSON &&
!options.context?.skipEngines?.includes('json')) {
const engine = new json_validator_1.JSONValidatorEngine(config);
enginesUsed.push('json');
enginePromises.push(engine.validate(options.content, options.context?.expectedSchema).then(res => {
result.json = res;
result.limitations?.push(...res.limitations);
}));
}
// CSM6 checks
if (config.engines.csm6.enabled &&
!options.context?.skipEngines?.includes('csm6')) {
const engine = new baseline_1.CSM6Baseline(config);
enginesUsed.push('csm6');
enginePromises.push(engine.audit(options.content, options.content).then(res => {
result.csm6 = res;
result.limitations?.push(...res.limitations);
}));
}
else {
result.notChecked?.push('csm6');
}
// Wait for all engines
await Promise.all(enginePromises);
// Execute plugins
const pluginRegistry = (0, registry_1.getPluginRegistry)();
const pluginResults = await pluginRegistry.executeAll({
content: options.content,
prompt: options.context?.isJSON ? 'JSON validation' : undefined,
config,
metadata: { requestId }
});
// Add plugin findings to result
if (pluginResults.length > 0) {
logger.info('Plugins executed', {
requestId,
pluginCount: pluginResults.length
});
}
// Calculate overall risk
const riskEngine = new risk_scoring_1.RiskScoringEngine(config);
result.risk = riskEngine.calculate(result);
// De-duplicate limitations
result.limitations = [...new Set(result.limitations)];
}
catch (error) {
logger.error('Verification failed', error, {
requestId,
contentLength: options.content.length
});
throw new errors_1.VerificationError(`Verification failed: ${error.message}`);
}
const endTime = Date.now();
const duration = logger.endRequest() || (endTime - startTime);
// Log completion
logger.info('Verification completed', {
requestId,
duration,
riskLevel: result.risk?.level,
findingsCount: result.csm6?.findings?.length || 0
});
// Audit trail
auditLogger.logVerification({
requestId,
content: options.content,
prompt: options.context?.isJSON ? 'JSON validation' : undefined,
riskLevel: result.risk?.level || 'unknown',
findingsCount: result.csm6?.findings?.length || 0,
blocked: result.risk?.action === 'block',
duration,
enginesUsed,
configTier: config.tier
});
// Baseline tracking and drift detection
const baselineStorage = (0, storage_1.getBaselineStorage)();
baselineStorage.updateBaseline({
latency: duration,
contentLength: options.content.length,
riskScore: result.risk?.overall || 0,
riskLevel: result.risk?.level || 'low',
engineScores: {
hallucination: result.hallucination?.riskScore,
consistency: result.consistency?.score || 0,
csm6: result.csm6?.riskScore
}
});
// Check for drift
const drifts = baselineStorage.checkDrift({
latency: duration,
contentLength: options.content.length,
riskScore: result.risk?.overall || 0
});
if (drifts.length > 0) {
logger.warn('Baseline drift detected', {
requestId,
drifts: drifts.map(d => ({
metric: d.metric,
driftPercent: d.driftPercent.toFixed(2) + '%',
severity: d.severity
}))
});
}
return {
...result,
risk: result.risk,
meta: {
verification_id: verificationId,
timestamp: new Date().toISOString(),
latency_ms: endTime - startTime,
version: constants_1.VERSION,
tier: config.tier,
enginesUsed
},
limitations: result.limitations,
notChecked: result.notChecked
};
}
/**
* Merge user config with defaults and tier limits
*/
function mergeConfig(userConfig) {
const tier = userConfig?.tier || 'free';
const tierConfig = config_1.TIER_LIMITS[tier];
// Deep merge
const merged = {
...config_1.DEFAULT_CONFIG,
...userConfig,
tier,
privacy: {
...config_1.DEFAULT_CONFIG.privacy,
...userConfig?.privacy
},
engines: {
...config_1.DEFAULT_CONFIG.engines,
...userConfig?.engines,
csm6: {
...config_1.DEFAULT_CONFIG.engines.csm6,
...userConfig?.engines?.csm6,
checks: {
...config_1.DEFAULT_CONFIG.engines.csm6.checks,
...userConfig?.engines?.csm6?.checks
}
}
},
performance: {
...config_1.DEFAULT_CONFIG.performance,
...tierConfig?.performance,
...userConfig?.performance
},
output: {
...config_1.DEFAULT_CONFIG.output,
...userConfig?.output,
includeLimitations: true // ALWAYS true
}
};
return merged;
}
/**
* CRITICAL: Validate privacy compliance
*/
function validatePrivacyCompliance(config) {
// Free tier must never allow network requests
if (config.tier === 'free' && config.privacy.allowNetworkRequests) {
throw new errors_1.PrivacyViolationError('Free tier cannot enable network requests. ' +
'This is a privacy violation. ' +
'Upgrade to Team tier for ML-enhanced features.');
}
// Free tier must never have telemetry
if (config.tier === 'free' && config.privacy.telemetryEnabled) {
throw new errors_1.PrivacyViolationError('Free tier cannot enable telemetry. ' +
'This is a privacy violation.');
}
}
/**
* Validate input with comprehensive checks
*/
function validateInput(content, config) {
// Check for empty input
if (!content || content.trim().length === 0) {
throw new errors_1.ValidationError('Content cannot be empty', codes_1.ErrorCode.EMPTY_INPUT, { contentLength: content?.length || 0 });
}
// Check for invalid characters
if (!/^[\x00-\x7F\u0080-\uFFFF]*$/.test(content)) {
throw new errors_1.ValidationError('Content contains invalid characters', codes_1.ErrorCode.INVALID_ENCODING, { contentLength: content.length });
}
// Check tier limits
const limits = config_1.TIER_LIMITS[config.tier];
if (content.length > (limits.performance?.maxContentLength || 100000)) {
throw new errors_1.ValidationError(`Content exceeds tier limit (${limits.performance?.maxContentLength || 100000} chars). ` +
`Current: ${content.length} chars. ` +
`Upgrade to ${config.tier === 'free' ? 'Team' : 'Professional'} tier for higher limits.`, codes_1.ErrorCode.CONTENT_TOO_LARGE, {
contentLength: content.length,
maxLength: limits.performance?.maxContentLength || 100000,
currentTier: config.tier,
suggestedTier: config.tier === 'free' ? 'team' : 'professional'
});
}
// Absolute maximum safety check (prevent DoS)
const ABSOLUTE_MAX = 10 * 1024 * 1024; // 10MB
if (content.length > ABSOLUTE_MAX) {
throw new errors_1.ValidationError(`Content exceeds absolute maximum size (${ABSOLUTE_MAX} bytes)`, codes_1.ErrorCode.CONTENT_TOO_LARGE, { contentLength: content.length, maxLength: ABSOLUTE_MAX });
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyaWZ5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3ZlcmlmeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7OztHQVNHOztBQWdESCx3QkEwTUM7QUF4UEQsK0JBQW9DO0FBQ3BDLDJDQUFxRTtBQUVyRSwyQ0FBc0M7QUFDdEMscUNBQXFGO0FBQ3JGLDBDQUEyQztBQUMzQywyREFBOEQ7QUFDOUQsdURBQTBEO0FBQzFELDZEQUErRDtBQUMvRCw4Q0FBK0M7QUFDL0MseURBQTJEO0FBQzNELHFDQUFzQztBQUN0Qyw2Q0FBNkM7QUFDN0MsMkNBQWlEO0FBQ2pELGdEQUF3RDtBQUN4RCxpREFBdUQ7QUFZdkQ7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtCRztBQUNJLEtBQUssVUFBVSxNQUFNLENBQUMsT0FBc0I7SUFDakQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBQzdCLE1BQU0sY0FBYyxHQUFHLElBQUEsU0FBTSxHQUFFLENBQUM7SUFFaEMscUJBQXFCO0lBQ3JCLE1BQU0sTUFBTSxHQUFHLElBQUEsa0JBQVMsR0FBRSxDQUFDO0lBQzNCLE1BQU0sV0FBVyxHQUFHLElBQUEsc0JBQWMsR0FBRSxDQUFDO0lBQ3JDLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUV4QyxNQUFNLENBQUMsSUFBSSxDQUFDLHNCQUFzQixFQUFFO1FBQ2xDLFNBQVM7UUFDVCxhQUFhLEVBQUUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNO1FBQ3JDLFVBQVUsRUFBRSxDQUFDLENBQUMsT0FBTyxDQUFDLE9BQU87S0FDOUIsQ0FBQyxDQUFDO0lBRUgsNkRBQTZEO0lBQzdELE1BQU0sVUFBVSxHQUFHLElBQUEsbUJBQVUsRUFBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDOUMsTUFBTSxNQUFNLEdBQUcsV0FBVyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBRXZDLHdDQUF3QztJ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LEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLGNBQWMsMEJBQTBCLEVBQ3hGLGlCQUFTLENBQUMsaUJBQWlCLEVBQzNCO1lBQ0UsYUFBYSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQzdCLFNBQVMsRUFBRSxNQUFNLENBQUMsV0FBVyxFQUFFLGdCQUFnQixJQUFJLE1BQU07WUFDekQsV0FBVyxFQUFFLE1BQU0sQ0FBQyxJQUFJO1lBQ3hCLGFBQWEsRUFBRSxNQUFNLENBQUMsSUFBSSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxjQUFjO1NBQ2hFLENBQ0YsQ0FBQztJQUNKLENBQUM7SUFFRCw4Q0FBOEM7SUFDOUMsTUFBTSxZQUFZLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJLENBQUMsQ0FBQyxPQUFPO0lBQzlDLElBQUksT0FBTyxDQUFDLE1BQU0sR0FBRyxZQUFZLEVBQUUsQ0FBQztRQUNsQyxNQUFNLElBQUksd0JBQWUsQ0FDdkIsMENBQTBDLFlBQVksU0FBUyxFQUMvRCxpQkFBUyxDQUFDLGlCQUFpQixFQUMzQixFQUFFLGFBQWEsRUFBRSxPQUFPLENBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsQ0FDM0QsQ0FBQztJQUNKLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBsbG12ZXJpZnkgLSBNYWluIFZlcmlmaWNhdGlvbiBGdW5jdGlvblxuICogXG4gKiBBSSBPdXRwdXQgVmVyaWZpY2F0aW9uIHdpdGggaG9uZXN0IGxpbWl0YXRpb25zLlxuICogTG9jYWwtZmlyc3QsIHByaXZhY3ktcHJlc2VydmluZywgdHJhbnNwYXJlbnQuXG4gKiBcbiAqIEBtb2R1bGUgdmVyaWZ5XG4gKiBAYXV0aG9yIEhhaWVjXG4gKiBAbGljZW5zZSBNSVRcbiAqL1xuXG5pbXBvcnQgeyB2NCBhcyB1dWlkdjQgfSBmcm9tICd1dWlkJztcbmltcG9ydCB7IENvbmZpZywgREVGQVVMVF9DT05GSUcsIFRJRVJfTElNSVRTIH0gZnJvbSAnLi90eXBlcy9jb25maWcnO1xuaW1wb3J0IHsgVmVyaWZ5UmVzdWx0IH0gZnJvbSAnLi90eXBlcy9yZXN1bHRzJztcbmltcG9ydCB7IFZFUlNJT04gfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgeyBQcml2YWN5VmlvbGF0aW9uRXJyb3IsIFZhbGlkYXRpb25FcnJvciwgVmVyaWZpY2F0aW9uRXJyb3IgfSBmcm9tICcuL2Vycm9ycyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuL2Vycm9ycy9jb2Rlcyc7XG5pbXBvcnQgeyBIYWxsdWNpbmF0aW9uRW5naW5lIH0gZnJvbSAnLi9lbmdpbmVzL2hhbGx1Y2luYXRpb24nO1xuaW1wb3J0IHsgQ29uc2lzdGVuY3lFbmdpbmUgfSBmcm9tICcuL2VuZ2luZXMvY29uc2lzdGVuY3knO1xuaW1wb3J0IHsgSlNPTlZhbGlkYXRvckVuZ2luZSB9IGZyb20gJy4vZW5naW5lcy9qc29uLXZhbGlkYXRvcic7XG5pbXBvcnQgeyBDU002QmFzZWxpbmUgfSBmcm9tICcuL2NzbTYvYmFzZWxpbmUnO1xuaW1wb3J0IHsgUmlza1Njb3JpbmdFbmdpbmUgfSBmcm9tICcuL2VuZ2luZXMvcmlzay1zY29yaW5nJztcbmltcG9ydCB7IGxvYWRDb25maWcgfSBmcm9tICcuL2NvbmZpZyc7XG5pbXBvcnQgeyBnZXRMb2dnZXIgfSBmcm9tICcuL2xvZ2dpbmcvbG9nZ2VyJztcbmltcG9ydCB7IGdldEF1ZGl0TG9nZ2VyIH0gZnJvbSAnLi9sb2dnaW5nL2F1ZGl0JztcbmltcG9ydCB7IGdldEJhc2VsaW5lU3RvcmFnZSB9IGZyb20gJy4vYmFzZWxpbmUvc3RvcmFnZSc7XG5pbXBvcnQgeyBnZXRQbHVnaW5SZWdpc3RyeSB9IGZyb20gJy4vcGx1Z2lucy9yZWdpc3RyeSc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgVmVyaWZ5T3B0aW9ucyB7XG4gIGNvbnRlbnQ6IHN0cmluZztcbiAgY29uZmlnPzogUGFydGlhbDxDb25maWc+O1xuICBjb250ZXh0Pzoge1xuICAgIGlzSlNPTj86IGJvb2xlYW47XG4gICAgZXhwZWN0ZWRTY2hlbWE/OiB1bmtub3duO1xuICAgIHNraXBFbmdpbmVzPzogc3RyaW5nW107XG4gIH07XG59XG5cbi8qKlxuICogTWFpbiB2ZXJpZmljYXRpb24gZnVuY3Rpb25cbiAqIFxuICogUFJJVkFDWSBHVUFSQU5URUU6IEZyZWUgdGllciBuZXZlciBtYWtlcyBuZXR3b3JrIHJlcXVlc3RzLlxuICogQWxsIHByb2Nlc3NpbmcgaXMgbG9jYWwgdW5sZXNzIGV4cGxpY2l0IEFQSSBrZXkgaXMgcHJvdmlkZWQuXG4gKiBcbiAqIEBwYXJhbSBvcHRpb25zIC0gVmVyaWZpY2F0aW9uIG9wdGlvbnNcbiAqIEByZXR1cm5zIENvbXBsZXRlIHZlcmlmaWNhdGlvbiByZXN1bHQgd2l0aCBsaW1pdGF0aW9uc1xuICogXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgcmVzdWx0ID0gYXdhaXQgdmVyaWZ5KHtcbiAqICAgY29udGVudDogXCJUaGUgRWFydGggaXMgZmxhdC5cIlxuICogfSk7XG4gKiBcbiAqIGNvbnNvbGUubG9nKHJlc3VsdC5yaXNrLmxldmVsKTsgLy8gXCJtb2RlcmF0ZVwiXG4gKiBjb25zb2xlLmxvZyhyZXN1bHQubGltaXRhdGlvbnMpOyAvLyBbXCJQYXR0ZXJuLWJhc2VkIGRldGVjdGlvbiBvbmx5XCIsIC4uLl1cbiAqIGBgYFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gdmVyaWZ5KG9wdGlvbnM6IFZlcmlmeU9wdGlvbnMpOiBQcm9taXNlPFZlcmlmeVJlc3VsdD4ge1xuICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICBjb25zdCB2ZXJpZmljYXRpb25JZCA9IHV1aWR2NCgpO1xuICBcbiAgLy8gSW5pdGlhbGl6ZSBsb2dnaW5nXG4gIGNvbnN0IGxvZ2dlciA9IGdldExvZ2dlcigpO1xuICBjb25zdCBhdWRpdExvZ2dlciA9IGdldEF1ZGl0TG9nZ2VyKCk7XG4gIGNvbnN0IHJlcXVlc3RJZCA9IGxvZ2dlci5zdGFydFJlcXVlc3QoKTtcbiAgXG4gIGxvZ2dlci5pbmZvKCdWZXJpZmljYXRpb24gc3RhcnRlZCcsIHtcbiAgICByZXF1ZXN0SWQsXG4gICAgY29udGVudExlbmd0aDogb3B0aW9ucy5jb250ZW50Lmxlbmd0aCxcbiAgICBoYXNDb250ZXh0OiAhIW9wdGlvbnMuY29udGV4dFxuICB9KTtcbiAgXG4gIC8vIExvYWQgY29uZmlnIGZyb20gZmlsZS9lbnYsIHRoZW4gbWVyZ2Ugd2l0aCBydW50aW1lIG9wdGlvbnNcbiAgY29uc3QgYmFzZUNvbmZpZyA9IGxvYWRDb25maWcob3B0aW9ucy5jb25maWcpO1xuICBjb25zdCBjb25maWcgPSBtZXJnZUNvbmZpZyhiYXNlQ29uZmlnKTtcbiAgXG4gIC8vIENSSVRJQ0FMOiBWYWxpZGF0ZSBwcml2YWN5IGNvbXBsaWFuY2VcbiAgdmFsaWRhdGVQcml2YWN5Q29tcGxpYW5jZShjb25maWcpO1xuICBcbiAgLy8gVmFsaWRhdGUgaW5wdXRcbiAgdmFsaWRhdGVJbnB1dChvcHRpb25zLmNvbnRlbnQsIGNvbmZpZyk7XG4gIFxuICBjb25zdCByZXN1bHQ6IFBhcnRpYWw8VmVyaWZ5UmVzdWx0PiA9IHtcbiAgICBsaW1pdGF0aW9uczogW10sXG4gICAgbm90Q2hlY2tlZDogW11cbiAgfTtcbiAgXG4gIGNvbnN0IGVuZ2luZXNVc2VkOiBzdHJpbmdbXSA9IFtdO1xuICBcbiAgdHJ5IHtcbiAgICBjb25zdCBlbmdpbmVQcm9taXNlczogUHJvbWlzZTx2b2lkPltdID0gW107XG4gICAgXG4gICAgLy8gSGFsbHVjaW5hdGlvbiBkZXRlY3Rpb25cbiAgICBpZiAoY29uZmlnLmVuZ2luZXMuaGFsbHVjaW5hdGlvbi5lbmFibGVkICYmIFxuICAgICAgICAhb3B0aW9ucy5jb250ZXh0Py5za2lwRW5naW5lcz8uaW5jbHVkZXMoJ2hhbGx1Y2luYXRpb24nKSkge1xuICAgICAgY29uc3QgZW5naW5lID0gbmV3IEhhbGx1Y2luYXRpb25FbmdpbmUoY29uZmlnKTtcbiAgICAgIGVuZ2luZXNVc2VkLnB1c2goJ2hhbGx1Y2luYXRpb24nKTtcbiAgICAgIGVuZ2luZVByb21pc2VzLnB1c2goXG4gICAgICAgIGVuZ2luZS5kZXRlY3Qob3B0aW9ucy5jb250ZW50KS50aGVuKHJlcyA9PiB7XG4gICAgICAgICAgcmVzdWx0LmhhbGx1Y2luYXRpb24gPSByZXM7XG4gICAgICAgICAgcmVzdWx0LmxpbWl0YXRpb25zPy5wdXNoKC4uLnJlcy5saW1pdGF0aW9ucyk7XG4gICAgICAgIH0pXG4gICAgICApO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXN1bHQubm90Q2hlY2tlZD8ucHVzaCgnaGFsbHVjaW5hdGlvbicpO1xuICAgIH1cbiAgICBcbiAgICAvLyBDb25zaXN0ZW5jeSBjaGVja1xuICAgIGlmIChjb25maWcuZW5naW5lcy5jb25zaXN0ZW5jeS5lbmFibGVkICYmXG4gICAgICAgICFvcHRpb25zLmNvbnRleHQ/LnNraXBFbmdpbmVzPy5pbmNsdWRlcygnY29uc2lzdGVuY3knKSkge1xuICAgICAgY29uc3QgZW5naW5lID0gbmV3IENvbnNpc3RlbmN5RW5naW5lKGNvbmZpZyk7XG4gICAgICBlbmdpbmVzVXNlZC5wdXNoKCdjb25zaXN0ZW5jeScpO1xuICAgICAgZW5naW5lUHJvbWlzZXMucHVzaChcbiAgICAgICAgZW5naW5lLmNoZWNrKG9wdGlvbnMuY29udGVudCkudGhlbihyZXMgPT4ge1xuICAgICAgICAgIHJlc3VsdC5jb25zaXN0ZW5jeSA9IHJlcztcbiAgICAgICAgICByZXN1bHQubGltaXRhdGlvbnM/LnB1c2goLi4ucmVzLmxpbWl0YXRpb25zKTtcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdC5ub3RDaGVja2VkPy5wdXNoKCdjb25zaXN0ZW5jeScpO1xuICAgIH1cbiAgICBcbiAgICAvLyBKU09OIHZhbGlkYXRpb25cbiAgICBpZiAoY29uZmlnLmVuZ2luZXMuanNvblZhbGlkYXRvci5lbmFibGVkICYmXG4gICAgICAgIG9wdGlvbnMuY29udGV4dD8uaXNKU09OICYmXG4gICAgICAgICFvcHRpb25zLmNvbnRleHQ/LnNraXBFbmdpbmVzPy5pbmNsdWRlcygnanNvbicpKSB7XG4gICAgICBjb25zdCBlbmdpbmUgPSBuZXcgSlNPTlZhbGlkYXRvckVuZ2luZShjb25maWcpO1xuICAgICAgZW5naW5lc1VzZWQucHVzaCgnanNvbicpO1xuICAgICAgZW5naW5lUHJvbWlzZXMucHVzaChcbiAgICAgICAgZW5naW5lLnZhbGlkYXRlKFxuICAgICAgICAgIG9wdGlvbnMuY29udGVudCxcbiAgICAgICAgICBvcHRpb25zLmNvbnRleHQ/LmV4cGVjdGVkU2NoZW1hXG4gICAgICAgICkudGhlbihyZXMgPT4ge1xuICAgICAgICAgIHJlc3VsdC5qc29uID0gcmVzO1xuICAgICAgICAgIHJlc3VsdC5saW1pdGF0aW9ucz8ucHVzaCguLi5yZXMubGltaXRhdGlvbnMpO1xuICAgICAgICB9KVxuICAgICAgKTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ1NNNiBjaGVja3NcbiAgICBpZiAoY29uZmlnLmVuZ2luZXMuY3NtNi5lbmFibGVkICYmXG4gICAgICAgICFvcHRpb25zLmNvbnRleHQ/LnNraXBFbmdpbmVzPy5pbmNsdWRlcygnY3NtNicpKSB7XG4gICAgICBjb25zdCBlbmdpbmUgPSBuZXcgQ1NNNkJhc2VsaW5lKGNvbmZpZyk7XG4gICAgICBlbmdpbmVzVXNlZC5wdXNoKCdjc202Jyk7XG4gICAgICBlbmdpbmVQcm9taXNlcy5wdXNoKFxuICAgICAgICBlbmdpbmUuYXVkaXQob3B0aW9ucy5jb250ZW50LCBvcHRpb25zLmNvbnRlbnQpLnRoZW4ocmVzID0+IHtcbiAgICAgICAgICByZXN1bHQuY3NtNiA9IHJlcztcbiAgICAgICAgICByZXN1bHQubGltaXRhdGlvbnM/LnB1c2goLi4ucmVzLmxpbWl0YXRpb25zKTtcbiAgICAgICAgfSlcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHJlc3VsdC5ub3RDaGVja2VkPy5wdXNoKCdjc202Jyk7XG4gICAgfVxuICAgIFxuICAgIC8vIFdhaXQgZm9yIGFsbCBlbmdpbmVzXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoZW5naW5lUHJvbWlzZXMpO1xuICAgIFxuICAgIC8vIEV4ZWN1dGUgcGx1Z2luc1xuICAgIGNvbnN0IHBsdWdpblJlZ2lzdHJ5ID0gZ2V0UGx1Z2luUmVnaXN0cnkoKTtcbiAgICBjb25zdCBwbHVnaW5SZXN1bHRzID0gYXdhaXQgcGx1Z2luUmVnaXN0cnkuZXhlY3V0ZUFsbCh7XG4gICAgICBjb250ZW50OiBvcHRpb25zLmNvbnRlbnQsXG4gICAgICBwcm9tcHQ6IG9wdGlvbnMuY29udGV4dD8uaXNKU09OID8gJ0pTT04gdmFsaWRhdGlvbicgOiB1bmRlZmluZWQsXG4gICAgICBjb25maWcsXG4gICAgICBtZXRhZGF0YTogeyByZXF1ZXN0SWQgfVxuICAgIH0pO1xuICAgIFxuICAgIC8vIEFkZCBwbHVnaW4gZmluZGluZ3MgdG8gcmVzdWx0XG4gICAgaWYgKHBsdWdpblJlc3VsdHMubGVuZ3RoID4gMCkge1xuICAgICAgbG9nZ2VyLmluZm8oJ1BsdWdpbnMgZXhlY3V0ZWQnLCB7XG4gICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgcGx1Z2luQ291bnQ6IHBsdWdpblJlc3VsdHMubGVuZ3RoXG4gICAgICB9KTtcbiAgICB9XG4gICAgXG4gICAgLy8gQ2FsY3VsYXRlIG92ZXJhbGwgcmlza1xuICAgIGNvbnN0IHJpc2tFbmdpbmUgPSBuZXcgUmlza1Njb3JpbmdFbmdpbmUoY29uZmlnKTtcbiAgICByZXN1bHQucmlzayA9IHJpc2tFbmdpbmUuY2FsY3VsYXRlKHJlc3VsdCBhcyBWZXJpZnlSZXN1bHQpO1xuICAgIFxuICAgIC8vIERlLWR1cGxpY2F0ZSBsaW1pdGF0aW9uc1xuICAgIHJlc3VsdC5saW1pdGF0aW9ucyA9IFsuLi5uZXcgU2V0KHJlc3VsdC5saW1pdGF0aW9ucyldO1xuICAgIFxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGxvZ2dlci5lcnJvcignVmVyaWZpY2F0aW9uIGZhaWxlZCcsIGVycm9yIGFzIEVycm9yLCB7XG4gICAgICByZXF1ZXN0SWQsXG4gICAgICBjb250ZW50TGVuZ3RoOiBvcHRpb25zLmNvbnRlbnQubGVuZ3RoXG4gICAgfSk7XG4gICAgdGhyb3cgbmV3IFZlcmlmaWNhdGlvbkVycm9yKGBWZXJpZmljYXRpb24gZmFpbGVkOiAkeyhlcnJvciBhcyBFcnJvcikubWVzc2FnZX1gKTtcbiAgfVxuICBcbiAgY29uc3QgZW5kVGltZSA9IERhdGUubm93KCk7XG4gIGNvbnN0IGR1cmF0aW9uID0gbG9nZ2VyLmVuZFJlcXVlc3QoKSB8fCAoZW5kVGltZSAtIHN0YXJ0VGltZSk7XG4gIFxuICAvLyBMb2cgY29tcGxldGlvblxuICBsb2dnZXIuaW5mbygnVmVyaWZpY2F0aW9uIGNvbXBsZXRlZCcsIHtcbiAgICByZXF1ZXN0SWQsXG4gICAgZHVyYXRpb24sXG4gICAgcmlza0xldmVsOiByZXN1bHQucmlzaz8ubGV2ZWwsXG4gICAgZmluZGluZ3NDb3VudDogcmVzdWx0LmNzbTY/LmZpbmRpbmdzPy5sZW5ndGggfHwgMFxuICB9KTtcbiAgXG4gIC8vIEF1ZGl0IHRyYWlsXG4gIGF1ZGl0TG9nZ2VyLmxvZ1ZlcmlmaWNhdGlvbih7XG4gICAgcmVxdWVzdElkLFxuICAgIGNvbnRlbnQ6IG9wdGlvbnMuY29udGVudCxcbiAgICBwcm9tcHQ6IG9wdGlvbnMuY29udGV4dD8uaXNKU09OID8gJ0pTT04gdmFsaWRhdGlvbicgOiB1bmRlZmluZWQsXG4gICAgcmlza0xldmVsOiByZXN1bHQucmlzaz8ubGV2ZWwgfHwgJ3Vua25vd24nLFxuICAgIGZpbmRpbmdzQ291bnQ6IHJlc3VsdC5jc202Py5maW5kaW5ncz8ubGVuZ3RoIHx8IDAsXG4gICAgYmxvY2tlZDogcmVzdWx0LnJpc2s/LmFjdGlvbiA9PT0gJ2Jsb2NrJyxcbiAgICBkdXJhdGlvbixcbiAgICBlbmdpbmVzVXNlZCxcbiAgICBjb25maWdUaWVyOiBjb25maWcudGllclxuICB9KTtcbiAgXG4gIC8vIEJhc2VsaW5lIHRyYWNraW5nIGFuZCBkcmlmdCBkZXRlY3Rpb25cbiAgY29uc3QgYmFzZWxpbmVTdG9yYWdlID0gZ2V0QmFzZWxpbmVTdG9yYWdlKCk7XG4gIGJhc2VsaW5lU3RvcmFnZS51cGRhdGVCYXNlbGluZSh7XG4gICAgbGF0ZW5jeTogZHVyYXRpb24sXG4gICAgY29udGVudExlbmd0aDogb3B0aW9ucy5jb250ZW50Lmxlbmd0aCxcbiAgICByaXNrU2NvcmU6IHJlc3VsdC5yaXNrPy5vdmVyYWxsIHx8IDAsXG4gICAgcmlza0xldmVsOiByZXN1bHQucmlzaz8ubGV2ZWwgfHwgJ2xvdycsXG4gICAgZW5naW5lU2NvcmVzOiB7XG4gICAgICBoYWxsdWNpbmF0aW9uOiByZXN1bHQuaGFsbHVjaW5hdGlvbj8ucmlza1Njb3JlLFxuICAgICAgY29uc2lzdGVuY3k6IChyZXN1bHQuY29uc2lzdGVuY3kgYXMgYW55KT8uc2NvcmUgfHwgMCxcbiAgICAgIGNzbTY6IHJlc3VsdC5jc202Py5yaXNrU2NvcmVcbiAgICB9XG4gIH0pO1xuICBcbiAgLy8gQ2hlY2sgZm9yIGRyaWZ0XG4gIGNvbnN0IGRyaWZ0cyA9IGJhc2VsaW5lU3RvcmFnZS5jaGVja0RyaWZ0KHtcbiAgICBsYXRlbmN5OiBkdXJhdGlvbixcbiAgICBjb250ZW50TGVuZ3RoOiBvcHRpb25zLmNvbnRlbnQubGVuZ3RoLFxuICAgIHJpc2tTY29yZTogcmVzdWx0LnJpc2s/Lm92ZXJhbGwgfHwgMFxuICB9KTtcbiAgXG4gIGlmIChkcmlmdHMubGVuZ3RoID4gMCkge1xuICAgIGxvZ2dlci53YXJuKCdCYXNlbGluZSBkcmlmdCBkZXRlY3RlZCcsIHtcbiAgICAgIHJlcXVlc3RJZCxcbiAgICAgIGRyaWZ0czogZHJpZnRzLm1hcChkID0+ICh7XG4gICAgICAgIG1ldHJpYzogZC5tZXRyaWMsXG4gICAgICAgIGRyaWZ0UGVyY2VudDogZC5kcmlmdFBlcmNlbnQudG9GaXhlZCgyKSArICclJyxcbiAgICAgICAgc2V2ZXJpdHk6IGQuc2V2ZXJpdHlcbiAgICAgIH0pKVxuICAgIH0pO1xuICB9XG4gIFxuICByZXR1cm4ge1xuICAgIC4uLnJlc3VsdCxcbiAgICByaXNrOiByZXN1bHQucmlzayEsXG4gICAgbWV0YToge1xuICAgICAgdmVyaWZpY2F0aW9uX2lkOiB2ZXJpZmljYXRpb25JZCxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgbGF0ZW5jeV9tczogZW5kVGltZSAtIHN0YXJ0VGltZSxcbiAgICAgIHZlcnNpb246IFZFUlNJT04sXG4gICAgICB0aWVyOiBjb25maWcudGllcixcbiAgICAgIGVuZ2luZXNVc2VkXG4gICAgfSxcbiAgICBsaW1pdGF0aW9uczogcmVzdWx0LmxpbWl0YXRpb25zISxcbiAgICBub3RDaGVja2VkOiByZXN1bHQubm90Q2hlY2tlZCFcbiAgfSBhcyBWZXJpZnlSZXN1bHQ7XG59XG5cbi8qKlxuICogTWVyZ2UgdXNlciBjb25maWcgd2l0aCBkZWZhdWx0cyBhbmQgdGllciBsaW1pdHNcbiAqL1xuZnVuY3Rpb24gbWVyZ2VDb25maWcodXNlckNvbmZpZz86IFBhcnRpYWw8Q29uZmlnPik6IENvbmZpZyB7XG4gIGNvbnN0IHRpZXIgPSB1c2VyQ29uZmlnPy50aWVyIHx8ICdmcmVlJztcbiAgY29uc3QgdGllckNvbmZpZyA9IFRJRVJfTElNSVRTW3RpZXJdO1xuICBcbiAgLy8gRGVlcCBtZXJnZVxuICBjb25zdCBtZXJnZWQ6IENvbmZpZyA9IHtcbiAgICAuLi5ERUZBVUxUX0NPTkZJRyxcbiAgICAuLi51c2VyQ29uZmlnLFxuICAgIHRpZXIsXG4gICAgcHJpdmFjeToge1xuICAgICAgLi4uREVGQVVMVF9DT05GSUcucHJpdmFjeSxcbiAgICAgIC4uLnVzZXJDb25maWc/LnByaXZhY3lcbiAgICB9LFxuICAgIGVuZ2luZXM6IHtcbiAgICAgIC4uLkRFRkFVTFRfQ09ORklHLmVuZ2luZXMsXG4gICAgICAuLi51c2VyQ29uZmlnPy5lbmdpbmVzLFxuICAgICAgY3NtNjoge1xuICAgICAgICAuLi5ERUZBVUxUX0NPTkZJRy5lbmdpbmVzLmNzbTYsXG4gICAgICAgIC4uLnVzZXJDb25maWc/LmVuZ2luZXM/LmNzbTYsXG4gICAgICAgIGNoZWNrczoge1xuICAgICAgICAgIC4uLkRFRkFVTFRfQ09ORklHLmVuZ2luZXMuY3NtNi5jaGVja3MsXG4gICAgICAgICAgLi4udXNlckNvbmZpZz8uZW5naW5lcz8uY3NtNj8uY2hlY2tzXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHBlcmZvcm1hbmNlOiB7XG4gICAgICAuLi5ERUZBVUxUX0NPTkZJRy5wZXJmb3JtYW5jZSxcbiAgICAgIC4uLnRpZXJDb25maWc/LnBlcmZvcm1hbmNlLFxuICAgICAgLi4udXNlckNvbmZpZz8ucGVyZm9ybWFuY2VcbiAgICB9LFxuICAgIG91dHB1dDoge1xuICAgICAgLi4uREVGQVVMVF9DT05GSUcub3V0cHV0LFxuICAgICAgLi4udXNlckNvbmZpZz8ub3V0cHV0LFxuICAgICAgaW5jbHVkZUxpbWl0YXRpb25zOiB0cnVlIC8vIEFMV0FZUyB0cnVlXG4gICAgfVxuICB9O1xuICBcbiAgcmV0dXJuIG1lcmdlZDtcbn1cblxuLyoqXG4gKiBDUklUSUNBTDogVmFsaWRhdGUgcHJpdmFjeSBjb21wbGlhbmNlXG4gKi9cbmZ1bmN0aW9uIHZhbGlkYXRlUHJpdmFjeUNvbXBsaWFuY2UoY29uZmlnOiBDb25maWcpOiB2b2lkIHtcbiAgLy8gRnJlZSB0aWVyIG11c3QgbmV2ZXIgYWxsb3cgbmV0d29yayByZXF1ZXN0c1xuICBpZiAoY29uZmlnLnRpZXIgPT09ICdmcmVlJyAmJiBjb25maWcucHJpdmFjeS5hbGxvd05ldHdvcmtSZXF1ZXN0cykge1xuICAgIHRocm93IG5ldyBQcml2YWN5VmlvbGF0aW9uRXJyb3IoXG4gICAgICAnRnJlZSB0aWVyIGNhbm5vdCBlbmFibGUgbmV0d29yayByZXF1ZXN0cy4gJyArXG4gICAgICAnVGhpcyBpcyBhIHByaXZhY3kgdmlvbGF0aW9uLiAnICtcbiAgICAgICdVcGdyYWRlIHRvIFRlYW0gdGllciBmb3IgTUwtZW5oYW5jZWQgZmVhdHVyZXMuJ1xuICAgICk7XG4gIH1cbiAgXG4gIC8vIEZyZWUgdGllciBtdXN0IG5ldmVyIGhhdmUgdGVsZW1ldHJ5XG4gIGlmIChjb25maWcudGllciA9PT0gJ2ZyZWUnICYmIGNvbmZpZy5wcml2YWN5LnRlbGVtZXRyeUVuYWJsZWQpIHtcbiAgICB0aHJvdyBuZXcgUHJpdmFjeVZpb2xhdGlvbkVycm9yKFxuICAgICAgJ0ZyZWUgdGllciBjYW5ub3QgZW5hYmxlIHRlbGVtZXRyeS4gJyArXG4gICAgICAnVGhpcyBpcyBhIHByaXZhY3kgdmlvbGF0aW9uLidcbiAgICApO1xuICB9XG59XG5cbi8qKlxuICogVmFsaWRhdGUgaW5wdXQgd2l0aCBjb21wcmVoZW5zaXZlIGNoZWNrc1xuICovXG5mdW5jdGlvbiB2YWxpZGF0ZUlucHV0KGNvbnRlbnQ6IHN0cmluZywgY29uZmlnOiBDb25maWcpOiB2b2lkIHtcbiAgLy8gQ2hlY2sgZm9yIGVtcHR5IGlucHV0XG4gIGlmICghY29udGVudCB8fCBjb250ZW50LnRyaW0oKS5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKFxuICAgICAgJ0NvbnRlbnQgY2Fubm90IGJlIGVtcHR5JyxcbiAgICAgIEVycm9yQ29kZS5FTVBUWV9JTlBVVCxcbiAgICAgIHsgY29udGVudExlbmd0aDogY29udGVudD8ubGVuZ3RoIHx8IDAgfVxuICAgICk7XG4gIH1cbiAgXG4gIC8vIENoZWNrIGZvciBpbnZhbGlkIGNoYXJhY3RlcnNcbiAgaWYgKCEvXltcXHgwMC1cXHg3RlxcdTAwODAtXFx1RkZGRl0qJC8udGVzdChjb250ZW50KSkge1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICAnQ29udGVudCBjb250YWlucyBpbnZhbGlkIGNoYXJhY3RlcnMnLFxuICAgICAgRXJyb3JDb2RlLklOVkFMSURfRU5DT0RJTkcsXG4gICAgICB7IGNvbnRlbnRMZW5ndGg6IGNvbnRlbnQubGVuZ3RoIH1cbiAgICApO1xuICB9XG4gIFxuICAvLyBDaGVjayB0aWVyIGxpbWl0c1xuICBjb25zdCBsaW1pdHMgPSBUSUVSX0xJTUlUU1tjb25maWcudGllcl07XG4gIGlmIChjb250ZW50Lmxlbmd0aCA+IChsaW1pdHMucGVyZm9ybWFuY2U/Lm1heENvbnRlbnRMZW5ndGggfHwgMTAwMDAwKSkge1xuICAgIHRocm93IG5ldyBWYWxpZGF0aW9uRXJyb3IoXG4gICAgICBgQ29udGVudCBleGNlZWRzIHRpZXIgbGltaXQgKCR7bGltaXRzLnBlcmZvcm1hbmNlPy5tYXhDb250ZW50TGVuZ3RoIHx8IDEwMDAwMH0gY2hhcnMpLiBgICtcbiAgICAgIGBDdXJyZW50OiAke2NvbnRlbnQubGVuZ3RofSBjaGFycy4gYCArXG4gICAgICBgVXBncmFkZSB0byAke2NvbmZpZy50aWVyID09PSAnZnJlZScgPyAnVGVhbScgOiAnUHJvZmVzc2lvbmFsJ30gdGllciBmb3IgaGlnaGVyIGxpbWl0cy5gLFxuICAgICAgRXJyb3JDb2RlLkNPTlRFTlRfVE9PX0xBUkdFLFxuICAgICAge1xuICAgICAgICBjb250ZW50TGVuZ3RoOiBjb250ZW50Lmxlbmd0aCxcbiAgICAgICAgbWF4TGVuZ3RoOiBsaW1pdHMucGVyZm9ybWFuY2U/Lm1heENvbnRlbnRMZW5ndGggfHwgMTAwMDAwLFxuICAgICAgICBjdXJyZW50VGllcjogY29uZmlnLnRpZXIsXG4gICAgICAgIHN1Z2dlc3RlZFRpZXI6IGNvbmZpZy50aWVyID09PSAnZnJlZScgPyAndGVhbScgOiAncHJvZmVzc2lvbmFsJ1xuICAgICAgfVxuICAgICk7XG4gIH1cbiAgXG4gIC8vIEFic29sdXRlIG1heGltdW0gc2FmZXR5IGNoZWNrIChwcmV2ZW50IERvUylcbiAgY29uc3QgQUJTT0xVVEVfTUFYID0gMTAgKiAxMDI0ICogMTAyNDsgLy8gMTBNQlxuICBpZiAoY29udGVudC5sZW5ndGggPiBBQlNPTFVURV9NQVgpIHtcbiAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKFxuICAgICAgYENvbnRlbnQgZXhjZWVkcyBhYnNvbHV0ZSBtYXhpbXVtIHNpemUgKCR7QUJTT0xVVEVfTUFYfSBieXRlcylgLFxuICAgICAgRXJyb3JDb2RlLkNPTlRFTlRfVE9PX0xBUkdFLFxuICAgICAgeyBjb250ZW50TGVuZ3RoOiBjb250ZW50Lmxlbmd0aCwgbWF4TGVuZ3RoOiBBQlNPTFVURV9NQVggfVxuICAgICk7XG4gIH1cbn1cbiJdfQ==