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.
189 lines • 20.4 kB
JavaScript
;
/**
* Hallucination Risk Heuristic Module
*
* Detects potential hallucination signals in LLM output.
* Uses heuristics - not definitive hallucination detection.
*
* LIMITATIONS:
* - Heuristic-based, not ground-truth verification
* - May produce false positives/negatives
* - Cannot detect factually incorrect but plausible statements
* - Requires prompt context for best results
*
* @module engines/classification/hallucination
* @author Haiec
* @license MIT
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_HALLUCINATION_WEIGHTS = void 0;
exports.calculateHallucinationSignals = calculateHallucinationSignals;
exports.calculateHallucinationRisk = calculateHallucinationRisk;
exports.getHallucinationLabel = getHallucinationLabel;
const utils_1 = require("./utils");
/**
* Overconfident language patterns.
*/
const OVERCONFIDENT_PATTERNS = [
'definitely',
'certainly',
'guaranteed',
'proven',
'no doubt',
'undeniable',
'absolutely',
'without question',
'100%',
'always',
'never fails'
];
/**
* Contradiction patterns (simplified).
*/
const CONTRADICTION_PATTERNS = [
{ positive: /is required/i, negative: /is optional/i },
{ positive: /must/i, negative: /does not need to/i },
{ positive: /always/i, negative: /never/i },
{ positive: /can/i, negative: /cannot/i },
{ positive: /will/i, negative: /will not/i }
];
/**
* Calculates speculative facts score.
* Looks for capitalized entities in output not present in prompt.
*/
function calculateSpeculativeFactsScore(prompt, output) {
const promptEntities = (0, utils_1.extractCapitalizedTokens)(prompt);
const outputEntities = (0, utils_1.extractCapitalizedTokens)(output);
let newEntities = 0;
for (const entity of outputEntities) {
if (!promptEntities.has(entity)) {
newEntities++;
}
}
// Score: 0 if no new entities, 1 if 5+ new entities
return (0, utils_1.clamp)(newEntities / 5, 0, 1);
}
/**
* Calculates overconfident language score.
*/
function calculateOverconfidentScore(text) {
const matches = (0, utils_1.countMatches)(text, OVERCONFIDENT_PATTERNS);
if (matches === 0)
return 0;
if (matches === 1)
return 0.5;
return 0.8;
}
/**
* Calculates fabricated JSON keys score.
* Looks for JSON keys not mentioned in prompt.
*/
function calculateFabricatedKeysScore(prompt, normalizedJson) {
if (!normalizedJson || typeof normalizedJson !== 'object' || Array.isArray(normalizedJson)) {
return 0;
}
const obj = normalizedJson;
const keys = Object.keys(obj);
const promptLower = prompt.toLowerCase();
let extraKeys = 0;
for (const key of keys) {
if (!promptLower.includes(key.toLowerCase())) {
extraKeys++;
}
}
return (0, utils_1.clamp)(extraKeys / 5, 0, 1);
}
/**
* Calculates contradiction score.
* Looks for simple contradictory patterns.
*/
function calculateContradictionScore(text) {
for (const pattern of CONTRADICTION_PATTERNS) {
const hasPositive = pattern.positive.test(text);
const hasNegative = pattern.negative.test(text);
if (hasPositive && hasNegative) {
// Found potential contradiction
return 0.7;
}
}
return 0;
}
/**
* Calculates hallucination risk signals.
*
* @param prompt - The original prompt
* @param output - The LLM output
* @param normalizedJson - Parsed JSON if available
* @param customHooks - Optional custom detection hooks
* @returns Hallucination signals and scores
*/
function calculateHallucinationSignals(prompt, output, normalizedJson, customHooks) {
const speculativeFactsScore = calculateSpeculativeFactsScore(prompt, output);
const overconfidentScore = calculateOverconfidentScore(output);
const fabricatedKeysScore = normalizedJson
? calculateFabricatedKeysScore(prompt, normalizedJson)
: 0;
const contradictionScore = calculateContradictionScore(output);
return {
speculativeFactsScore,
fabricatedKeysScore,
overconfidentScore,
contradictionScore,
customHooksCount: customHooks?.length ?? 0
};
}
/** Default weights for hallucination signals (tuned to reduce false positives) */
exports.DEFAULT_HALLUCINATION_WEIGHTS = {
speculative: 0.35, // Reduced from 0.4 - new entities are often legitimate
fabricated: 0.25, // Reduced from 0.3 - JSON keys often aren't in prompt
overconfident: 0.25, // Increased from 0.2 - overconfident language is a stronger signal
contradiction: 0.15 // Increased from 0.1 - contradictions are meaningful
};
/**
* Calculates overall hallucination risk score.
*
* @param signals - Hallucination signals
* @param prompt - Original prompt
* @param output - LLM output
* @param customHooks - Optional custom hooks
* @param weights - Optional weight overrides
* @returns Risk score (0-1)
*/
function calculateHallucinationRisk(signals, prompt, output, customHooks, weights) {
// Merge weights with defaults
const w = {
speculative: weights?.speculative ?? exports.DEFAULT_HALLUCINATION_WEIGHTS.speculative,
fabricated: weights?.fabricated ?? exports.DEFAULT_HALLUCINATION_WEIGHTS.fabricated,
overconfident: weights?.overconfident ?? exports.DEFAULT_HALLUCINATION_WEIGHTS.overconfident,
contradiction: weights?.contradiction ?? exports.DEFAULT_HALLUCINATION_WEIGHTS.contradiction
};
// Base risk from internal signals
let risk = w.speculative * signals.speculativeFactsScore +
w.fabricated * signals.fabricatedKeysScore +
w.overconfident * signals.overconfidentScore +
w.contradiction * signals.contradictionScore;
// Apply custom hooks
if (customHooks) {
for (const hook of customHooks) {
try {
const hookScore = (0, utils_1.clamp)(hook(prompt, output), 0, 1);
risk += 0.2 * hookScore;
}
catch {
// Ignore hook errors
}
}
}
return (0, utils_1.clamp)(risk, 0, 1);
}
/**
* Gets hallucination risk label from score.
*/
function getHallucinationLabel(risk) {
if (risk <= 0.3)
return 'low';
if (risk <= 0.6)
return 'medium';
return 'high';
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGFsbHVjaW5hdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9lbmdpbmVzL2NsYXNzaWZpY2F0aW9uL2hhbGx1Y2luYXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Ozs7Ozs7Ozs7Ozs7R0FlRzs7O0FBb0hILHNFQW9CQztBQW9CRCxnRUF3Q0M7QUFLRCxzREFJQztBQTFNRCxtQ0FBcUY7QUFFckY7O0dBRUc7QUFDSCxNQUFNLHNCQUFzQixHQUFHO0lBQzdCLFlBQVk7SUFDWixXQUFXO0lBQ1gsWUFBWTtJQUNaLFFBQVE7SUFDUixVQUFVO0lBQ1YsWUFBWTtJQUNaLFlBQVk7SUFDWixrQkFBa0I7SUFDbEIsTUFBTTtJQUNOLFFBQVE7SUFDUixhQUFhO0NBQ2QsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSxzQkFBc0IsR0FBRztJQUM3QixFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsUUFBUSxFQUFFLGNBQWMsRUFBRTtJQUN0RCxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLG1CQUFtQixFQUFFO0lBQ3BELEVBQUUsUUFBUSxFQUFFLFNBQVMsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFO0lBQzNDLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFO0lBQ3pDLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFO0NBQzdDLENBQUM7QUFFRjs7O0dBR0c7QUFDSCxTQUFTLDhCQUE4QixDQUFDLE1BQWMsRUFBRSxNQUFjO0lBQ3BFLE1BQU0sY0FBYyxHQUFHLElBQUEsZ0NBQXdCLEVBQUMsTUFBTSxDQUFDLENBQUM7SUFDeEQsTUFBTSxjQUFjLEdBQUcsSUFBQSxnQ0FBd0IsRUFBQyxNQUFNLENBQUMsQ0FBQztJQUV4RCxJQUFJLFdBQVcsR0FBRyxDQUFDLENBQUM7SUFDcEIsS0FBSyxNQUFNLE1BQU0sSUFBSSxjQUFjLEVB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LENBQUMsQ0FBQyxDQUFDO2dCQUNwRCxJQUFJLElBQUksR0FBRyxHQUFHLFNBQVMsQ0FBQztZQUMxQixDQUFDO1lBQUMsTUFBTSxDQUFDO2dCQUNQLHFCQUFxQjtZQUN2QixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLElBQUEsYUFBSyxFQUFDLElBQUksRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7QUFDM0IsQ0FBQztBQUVEOztHQUVHO0FBQ0gsU0FBZ0IscUJBQXFCLENBQUMsSUFBWTtJQUNoRCxJQUFJLElBQUksSUFBSSxHQUFHO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDOUIsSUFBSSxJQUFJLElBQUksR0FBRztRQUFFLE9BQU8sUUFBUSxDQUFDO0lBQ2pDLE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEhhbGx1Y2luYXRpb24gUmlzayBIZXVyaXN0aWMgTW9kdWxlXG4gKiBcbiAqIERldGVjdHMgcG90ZW50aWFsIGhhbGx1Y2luYXRpb24gc2lnbmFscyBpbiBMTE0gb3V0cHV0LlxuICogVXNlcyBoZXVyaXN0aWNzIC0gbm90IGRlZmluaXRpdmUgaGFsbHVjaW5hdGlvbiBkZXRlY3Rpb24uXG4gKiBcbiAqIExJTUlUQVRJT05TOlxuICogLSBIZXVyaXN0aWMtYmFzZWQsIG5vdCBncm91bmQtdHJ1dGggdmVyaWZpY2F0aW9uXG4gKiAtIE1heSBwcm9kdWNlIGZhbHNlIHBvc2l0aXZlcy9uZWdhdGl2ZXNcbiAqIC0gQ2Fubm90IGRldGVjdCBmYWN0dWFsbHkgaW5jb3JyZWN0IGJ1dCBwbGF1c2libGUgc3RhdGVtZW50c1xuICogLSBSZXF1aXJlcyBwcm9tcHQgY29udGV4dCBmb3IgYmVzdCByZXN1bHRzXG4gKiBcbiAqIEBtb2R1bGUgZW5naW5lcy9jbGFzc2lmaWNhdGlvbi9oYWxsdWNpbmF0aW9uXG4gKiBAYXV0aG9yIEhhaWVjXG4gKiBAbGljZW5zZSBNSVRcbiAqL1xuXG5pbXBvcnQgeyBIYWxsdWNpbmF0aW9uU2lnbmFscywgSGFsbHVjaW5hdGlvbkxhYmVsIH0gZnJvbSAnLi90eXBlcyc7XG5pbXBvcnQgeyBjbGFtcCwgZXh0cmFjdENhcGl0YWxpemVkVG9rZW5zLCBjb250YWluc0FueSwgY291bnRNYXRjaGVzIH0gZnJvbSAnLi91dGlscyc7XG5cbi8qKlxuICogT3ZlcmNvbmZpZGVudCBsYW5ndWFnZSBwYXR0ZXJucy5cbiAqL1xuY29uc3QgT1ZFUkNPTkZJREVOVF9QQVRURVJOUyA9IFtcbiAgJ2RlZmluaXRlbHknLFxuICAnY2VydGFpbmx5JyxcbiAgJ2d1YXJhbnRlZWQnLFxuICAncHJvdmVuJyxcbiAgJ25vIGRvdWJ0JyxcbiAgJ3VuZGVuaWFibGUnLFxuICAnYWJzb2x1dGVseScsXG4gICd3aXRob3V0IHF1ZXN0aW9uJyxcbiAgJzEwMCUnLFxuICAnYWx3YXlzJyxcbiAgJ25ldmVyIGZhaWxzJ1xuXTtcblxuLyoqXG4gKiBDb250cmFkaWN0aW9uIHBhdHRlcm5zIChzaW1wbGlmaWVkKS5cbiAqL1xuY29uc3QgQ09OVFJBRElDVElPTl9QQVRURVJOUyA9IFtcbiAgeyBwb3NpdGl2ZTogL2lzIHJlcXVpcmVkL2ksIG5lZ2F0aXZlOiAvaXMgb3B0aW9uYWwvaSB9LFxuICB7IHBvc2l0aXZlOiAvbXVzdC9pLCBuZWdhdGl2ZTogL2RvZXMgbm90IG5lZWQgdG8vaSB9LFxuICB7IHBvc2l0aXZlOiAvYWx3YXlzL2ksIG5lZ2F0aXZlOiAvbmV2ZXIvaSB9LFxuICB7IHBvc2l0aXZlOiAvY2FuL2ksIG5lZ2F0aXZlOiAvY2Fubm90L2kgfSxcbiAgeyBwb3NpdGl2ZTogL3dpbGwvaSwgbmVnYXRpdmU6IC93aWxsIG5vdC9pIH1cbl07XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBzcGVjdWxhdGl2ZSBmYWN0cyBzY29yZS5cbiAqIExvb2tzIGZvciBjYXBpdGFsaXplZCBlbnRpdGllcyBpbiBvdXRwdXQgbm90IHByZXNlbnQgaW4gcHJvbXB0LlxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVTcGVjdWxhdGl2ZUZhY3RzU2NvcmUocHJvbXB0OiBzdHJpbmcsIG91dHB1dDogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3QgcHJvbXB0RW50aXRpZXMgPSBleHRyYWN0Q2FwaXRhbGl6ZWRUb2tlbnMocHJvbXB0KTtcbiAgY29uc3Qgb3V0cHV0RW50aXRpZXMgPSBleHRyYWN0Q2FwaXRhbGl6ZWRUb2tlbnMob3V0cHV0KTtcbiAgXG4gIGxldCBuZXdFbnRpdGllcyA9IDA7XG4gIGZvciAoY29uc3QgZW50aXR5IG9mIG91dHB1dEVudGl0aWVzKSB7XG4gICAgaWYgKCFwcm9tcHRFbnRpdGllcy5oYXMoZW50aXR5KSkge1xuICAgICAgbmV3RW50aXRpZXMrKztcbiAgICB9XG4gIH1cbiAgXG4gIC8vIFNjb3JlOiAwIGlmIG5vIG5ldyBlbnRpdGllcywgMSBpZiA1KyBuZXcgZW50aXRpZXNcbiAgcmV0dXJuIGNsYW1wKG5ld0VudGl0aWVzIC8gNSwgMCwgMSk7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBvdmVyY29uZmlkZW50IGxhbmd1YWdlIHNjb3JlLlxuICovXG5mdW5jdGlvbiBjYWxjdWxhdGVPdmVyY29uZmlkZW50U2NvcmUodGV4dDogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3QgbWF0Y2hlcyA9IGNvdW50TWF0Y2hlcyh0ZXh0LCBPVkVSQ09ORklERU5UX1BBVFRFUk5TKTtcbiAgXG4gIGlmIChtYXRjaGVzID09PSAwKSByZXR1cm4gMDtcbiAgaWYgKG1hdGNoZXMgPT09IDEpIHJldHVybiAwLjU7XG4gIHJldHVybiAwLjg7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlcyBmYWJyaWNhdGVkIEpTT04ga2V5cyBzY29yZS5cbiAqIExvb2tzIGZvciBKU09OIGtleXMgbm90IG1lbnRpb25lZCBpbiBwcm9tcHQuXG4gKi9cbmZ1bmN0aW9uIGNhbGN1bGF0ZUZhYnJpY2F0ZWRLZXlzU2NvcmUoXG4gIHByb21wdDogc3RyaW5nLFxuICBub3JtYWxpemVkSnNvbjogdW5rbm93blxuKTogbnVtYmVyIHtcbiAgaWYgKCFub3JtYWxpemVkSnNvbiB8fCB0eXBlb2Ygbm9ybWFsaXplZEpzb24gIT09ICdvYmplY3QnIHx8IEFycmF5LmlzQXJyYXkobm9ybWFsaXplZEpzb24pKSB7XG4gICAgcmV0dXJuIDA7XG4gIH1cbiAgXG4gIGNvbnN0IG9iaiA9IG5vcm1hbGl6ZWRKc29uIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMob2JqKTtcbiAgY29uc3QgcHJvbXB0TG93ZXIgPSBwcm9tcHQudG9Mb3dlckNhc2UoKTtcbiAgXG4gIGxldCBleHRyYUtleXMgPSAwO1xuICBmb3IgKGNvbnN0IGtleSBvZiBrZXlzKSB7XG4gICAgaWYgKCFwcm9tcHRMb3dlci5pbmNsdWRlcyhrZXkudG9Mb3dlckNhc2UoKSkpIHtcbiAgICAgIGV4dHJhS2V5cysrO1xuICAgIH1cbiAgfVxuICBcbiAgcmV0dXJuIGNsYW1wKGV4dHJhS2V5cyAvIDUsIDAsIDEpO1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZXMgY29udHJhZGljdGlvbiBzY29yZS5cbiAqIExvb2tzIGZvciBzaW1wbGUgY29udHJhZGljdG9yeSBwYXR0ZXJucy5cbiAqL1xuZnVuY3Rpb24gY2FsY3VsYXRlQ29udHJhZGljdGlvblNjb3JlKHRleHQ6IHN0cmluZyk6IG51bWJlciB7XG4gIGZvciAoY29uc3QgcGF0dGVybiBvZiBDT05UUkFESUNUSU9OX1BBVFRFUk5TKSB7XG4gICAgY29uc3QgaGFzUG9zaXRpdmUgPSBwYXR0ZXJuLnBvc2l0aXZlLnRlc3QodGV4dCk7XG4gICAgY29uc3QgaGFzTmVnYXRpdmUgPSBwYXR0ZXJuLm5lZ2F0aXZlLnRlc3QodGV4dCk7XG4gICAgXG4gICAgaWYgKGhhc1Bvc2l0aXZlICYmIGhhc05lZ2F0aXZlKSB7XG4gICAgICAvLyBGb3VuZCBwb3RlbnRpYWwgY29udHJhZGljdGlvblxuICAgICAgcmV0dXJuIDAuNztcbiAgICB9XG4gIH1cbiAgXG4gIHJldHVybiAwO1xufVxuXG4vKipcbiAqIENhbGN1bGF0ZXMgaGFsbHVjaW5hdGlvbiByaXNrIHNpZ25hbHMuXG4gKiBcbiAqIEBwYXJhbSBwcm9tcHQgLSBUaGUgb3JpZ2luYWwgcHJvbXB0XG4gKiBAcGFyYW0gb3V0cHV0IC0gVGhlIExMTSBvdXRwdXRcbiAqIEBwYXJhbSBub3JtYWxpemVkSnNvbiAtIFBhcnNlZCBKU09OIGlmIGF2YWlsYWJsZVxuICogQHBhcmFtIGN1c3RvbUhvb2tzIC0gT3B0aW9uYWwgY3VzdG9tIGRldGVjdGlvbiBob29rc1xuICogQHJldHVybnMgSGFsbHVjaW5hdGlvbiBzaWduYWxzIGFuZCBzY29yZXNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUhhbGx1Y2luYXRpb25TaWduYWxzKFxuICBwcm9tcHQ6IHN0cmluZyxcbiAgb3V0cHV0OiBzdHJpbmcsXG4gIG5vcm1hbGl6ZWRKc29uOiB1bmtub3duIHwgdW5kZWZpbmVkLFxuICBjdXN0b21Ib29rcz86IEFycmF5PChwcm9tcHQ6IHN0cmluZywgb3V0cHV0OiBzdHJpbmcpID0+IG51bWJlcj5cbik6IEhhbGx1Y2luYXRpb25TaWduYWxzIHtcbiAgY29uc3Qgc3BlY3VsYXRpdmVGYWN0c1Njb3JlID0gY2FsY3VsYXRlU3BlY3VsYXRpdmVGYWN0c1Njb3JlKHByb21wdCwgb3V0cHV0KTtcbiAgY29uc3Qgb3ZlcmNvbmZpZGVudFNjb3JlID0gY2FsY3VsYXRlT3ZlcmNvbmZpZGVudFNjb3JlKG91dHB1dCk7XG4gIGNvbnN0IGZhYnJpY2F0ZWRLZXlzU2NvcmUgPSBub3JtYWxpemVkSnNvbiBcbiAgICA/IGNhbGN1bGF0ZUZhYnJpY2F0ZWRLZXlzU2NvcmUocHJvbXB0LCBub3JtYWxpemVkSnNvbikgXG4gICAgOiAwO1xuICBjb25zdCBjb250cmFkaWN0aW9uU2NvcmUgPSBjYWxjdWxhdGVDb250cmFkaWN0aW9uU2NvcmUob3V0cHV0KTtcbiAgXG4gIHJldHVybiB7XG4gICAgc3BlY3VsYXRpdmVGYWN0c1Njb3JlLFxuICAgIGZhYnJpY2F0ZWRLZXlzU2NvcmUsXG4gICAgb3ZlcmNvbmZpZGVudFNjb3JlLFxuICAgIGNvbnRyYWRpY3Rpb25TY29yZSxcbiAgICBjdXN0b21Ib29rc0NvdW50OiBjdXN0b21Ib29rcz8ubGVuZ3RoID8/IDBcbiAgfTtcbn1cblxuLyoqIERlZmF1bHQgd2VpZ2h0cyBmb3IgaGFsbHVjaW5hdGlvbiBzaWduYWxzICh0dW5lZCB0byByZWR1Y2UgZmFsc2UgcG9zaXRpdmVzKSAqL1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfSEFMTFVDSU5BVElPTl9XRUlHSFRTID0ge1xuICBzcGVjdWxhdGl2ZTogMC4zNSwgICAvLyBSZWR1Y2VkIGZyb20gMC40IC0gbmV3IGVudGl0aWVzIGFyZSBvZnRlbiBsZWdpdGltYXRlXG4gIGZhYnJpY2F0ZWQ6IDAuMjUsICAgIC8vIFJlZHVjZWQgZnJvbSAwLjMgLSBKU09OIGtleXMgb2Z0ZW4gYXJlbid0IGluIHByb21wdFxuICBvdmVyY29uZmlkZW50OiAwLjI1LCAvLyBJbmNyZWFzZWQgZnJvbSAwLjIgLSBvdmVyY29uZmlkZW50IGxhbmd1YWdlIGlzIGEgc3Ryb25nZXIgc2lnbmFsXG4gIGNvbnRyYWRpY3Rpb246IDAuMTUgIC8vIEluY3JlYXNlZCBmcm9tIDAuMSAtIGNvbnRyYWRpY3Rpb25zIGFyZSBtZWFuaW5nZnVsXG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZXMgb3ZlcmFsbCBoYWxsdWNpbmF0aW9uIHJpc2sgc2NvcmUuXG4gKiBcbiAqIEBwYXJhbSBzaWduYWxzIC0gSGFsbHVjaW5hdGlvbiBzaWduYWxzXG4gKiBAcGFyYW0gcHJvbXB0IC0gT3JpZ2luYWwgcHJvbXB0XG4gKiBAcGFyYW0gb3V0cHV0IC0gTExNIG91dHB1dFxuICogQHBhcmFtIGN1c3RvbUhvb2tzIC0gT3B0aW9uYWwgY3VzdG9tIGhvb2tzXG4gKiBAcGFyYW0gd2VpZ2h0cyAtIE9wdGlvbmFsIHdlaWdodCBvdmVycmlkZXNcbiAqIEByZXR1cm5zIFJpc2sgc2NvcmUgKDAtMSlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUhhbGx1Y2luYXRpb25SaXNrKFxuICBzaWduYWxzOiBIYWxsdWNpbmF0aW9uU2lnbmFscyxcbiAgcHJvbXB0OiBzdHJpbmcsXG4gIG91dHB1dDogc3RyaW5nLFxuICBjdXN0b21Ib29rcz86IEFycmF5PChwcm9tcHQ6IHN0cmluZywgb3V0cHV0OiBzdHJpbmcpID0+IG51bWJlcj4sXG4gIHdlaWdodHM/OiB7XG4gICAgc3BlY3VsYXRpdmU/OiBudW1iZXI7XG4gICAgZmFicmljYXRlZD86IG51bWJlcjtcbiAgICBvdmVyY29uZmlkZW50PzogbnVtYmVyO1xuICAgIGNvbnRyYWRpY3Rpb24/OiBudW1iZXI7XG4gIH1cbik6IG51bWJlciB7XG4gIC8vIE1lcmdlIHdlaWdodHMgd2l0aCBkZWZhdWx0c1xuICBjb25zdCB3ID0ge1xuICAgIHNwZWN1bGF0aXZlOiB3ZWlnaHRzPy5zcGVjdWxhdGl2ZSA/PyBERUZBVUxUX0hBTExVQ0lOQVRJT05fV0VJR0hUUy5zcGVjdWxhdGl2ZSxcbiAgICBmYWJyaWNhdGVkOiB3ZWlnaHRzPy5mYWJyaWNhdGVkID8/IERFRkFVTFRfSEFMTFVDSU5BVElPTl9XRUlHSFRTLmZhYnJpY2F0ZWQsXG4gICAgb3ZlcmNvbmZpZGVudDogd2VpZ2h0cz8ub3ZlcmNvbmZpZGVudCA/PyBERUZBVUxUX0hBTExVQ0lOQVRJT05fV0VJR0hUUy5vdmVyY29uZmlkZW50LFxuICAgIGNvbnRyYWRpY3Rpb246IHdlaWdodHM/LmNvbnRyYWRpY3Rpb24gPz8gREVGQVVMVF9IQUxMVUNJTkFUSU9OX1dFSUdIVFMuY29udHJhZGljdGlvblxuICB9O1xuICBcbiAgLy8gQmFzZSByaXNrIGZyb20gaW50ZXJuYWwgc2lnbmFsc1xuICBsZXQgcmlzayA9IFxuICAgIHcuc3BlY3VsYXRpdmUgKiBzaWduYWxzLnNwZWN1bGF0aXZlRmFjdHNTY29yZSArXG4gICAgdy5mYWJyaWNhdGVkICogc2lnbmFscy5mYWJyaWNhdGVkS2V5c1Njb3JlICtcbiAgICB3Lm92ZXJjb25maWRlbnQgKiBzaWduYWxzLm92ZXJjb25maWRlbnRTY29yZSArXG4gICAgdy5jb250cmFkaWN0aW9uICogc2lnbmFscy5jb250cmFkaWN0aW9uU2NvcmU7XG4gIFxuICAvLyBBcHBseSBjdXN0b20gaG9va3NcbiAgaWYgKGN1c3RvbUhvb2tzKSB7XG4gICAgZm9yIChjb25zdCBob29rIG9mIGN1c3RvbUhvb2tzKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBob29rU2NvcmUgPSBjbGFtcChob29rKHByb21wdCwgb3V0cHV0KSwgMCwgMSk7XG4gICAgICAgIHJpc2sgKz0gMC4yICogaG9va1Njb3JlO1xuICAgICAgfSBjYXRjaCB7XG4gICAgICAgIC8vIElnbm9yZSBob29rIGVycm9yc1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBcbiAgcmV0dXJuIGNsYW1wKHJpc2ssIDAsIDEpO1xufVxuXG4vKipcbiAqIEdldHMgaGFsbHVjaW5hdGlvbiByaXNrIGxhYmVsIGZyb20gc2NvcmUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRIYWxsdWNpbmF0aW9uTGFiZWwocmlzazogbnVtYmVyKTogSGFsbHVjaW5hdGlvbkxhYmVsIHtcbiAgaWYgKHJpc2sgPD0gMC4zKSByZXR1cm4gJ2xvdyc7XG4gIGlmIChyaXNrIDw9IDAuNikgcmV0dXJuICdtZWRpdW0nO1xuICByZXR1cm4gJ2hpZ2gnO1xufVxuIl19