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.

214 lines 20.1 kB
"use strict"; /** * JSON Detection and Repair Module * * Detects JSON in text and attempts to repair malformed JSON. * * @module engines/classification/json-repair * @author Haiec * @license MIT */ Object.defineProperty(exports, "__esModule", { value: true }); exports.detectAndRepairJson = detectAndRepairJson; /** * Extracts JSON candidate from text. */ function extractJsonCandidate(text) { // Try to find JSON in code blocks first const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/); if (codeBlockMatch) { const content = codeBlockMatch[1].trim(); if (content.startsWith('{') || content.startsWith('[')) { return content; } } // Look for JSON-like patterns const jsonPatterns = [ /(\{[\s\S]*\})/, // Object /(\[[\s\S]*\])/ // Array ]; for (const pattern of jsonPatterns) { const match = text.match(pattern); if (match) { return match[1]; } } return null; } /** * Attempts to parse JSON, returns null if invalid. */ function tryParse(json) { try { return JSON.parse(json); } catch { return null; } } /** * Repair step: Remove trailing commas. */ function removeTrailingCommas(json) { // Remove trailing commas before } or ] return json.replace(/,(\s*[}\]])/g, '$1'); } /** * Repair step: Add missing quotes to keys. */ function quoteUnquotedKeys(json) { // Match unquoted keys followed by : return json.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)(\s*:)/g, '$1"$2"$3'); } /** * Repair step: Replace single quotes with double quotes. */ function replaceSingleQuotes(json) { // Simple replacement - may not handle all edge cases let result = ''; let inString = false; let stringChar = ''; for (let i = 0; i < json.length; i++) { const char = json[i]; const prevChar = i > 0 ? json[i - 1] : ''; if (!inString) { if (char === '"' || char === "'") { inString = true; stringChar = char; result += '"'; } else { result += char; } } else { if (char === stringChar && prevChar !== '\\') { inString = false; result += '"'; } else if (char === '"' && stringChar === "'") { result += '\\"'; } else { result += char; } } } return result; } /** * Repair step: Escape unescaped newlines in strings. */ function escapeNewlines(json) { // This is a simplified version - proper implementation would need state tracking return json.replace(/(?<!\\)\n/g, '\\n'); } /** * Repair step: Close unclosed braces and brackets. */ function closeBrackets(json) { let braceCount = 0; let bracketCount = 0; for (const char of json) { if (char === '{') braceCount++; else if (char === '}') braceCount--; else if (char === '[') bracketCount++; else if (char === ']') bracketCount--; } let result = json; while (bracketCount > 0) { result += ']'; bracketCount--; } while (braceCount > 0) { result += '}'; braceCount--; } return result; } /** * Repair step: Remove JavaScript-style comments. */ function removeComments(json) { // Remove single-line comments let result = json.replace(/\/\/[^\n]*/g, ''); // Remove multi-line comments result = result.replace(/\/\*[\s\S]*?\*\//g, ''); return result; } /** Default maximum repair steps */ const DEFAULT_MAX_REPAIR_STEPS = 6; /** * Detects and repairs JSON in text. * * @param text - The text to analyze * @param maxRepairSteps - Maximum repair steps to attempt (default: 6) * @returns JSON detection result with repair information */ function detectAndRepairJson(text, maxRepairSteps = DEFAULT_MAX_REPAIR_STEPS) { const candidate = extractJsonCandidate(text); const repairSteps = []; if (!candidate) { return { isJson: false, candidate: null, repairSteps: [], repairSucceeded: false }; } // Try parsing as-is first let parsed = tryParse(candidate); if (parsed !== null) { return { isJson: true, normalizedJson: parsed, candidate, repairSteps: [], repairSucceeded: true }; } // Apply repair steps in order (limited by maxRepairSteps) const repairs = [ { name: 'remove_comments', fn: removeComments }, { name: 'remove_trailing_commas', fn: removeTrailingCommas }, { name: 'quote_unquoted_keys', fn: quoteUnquotedKeys }, { name: 'replace_single_quotes', fn: replaceSingleQuotes }, { name: 'escape_newlines', fn: escapeNewlines }, { name: 'close_brackets', fn: closeBrackets } ].slice(0, maxRepairSteps); let current = candidate; for (const repair of repairs) { const before = current; current = repair.fn(current); const applied = current !== before; repairSteps.push({ step: repair.name, applied }); // Try parsing after each step if (applied) { parsed = tryParse(current); if (parsed !== null) { return { isJson: true, normalizedJson: parsed, candidate, repairSteps, repairSucceeded: true }; } } } // All repairs failed return { isJson: false, candidate, repairSteps, repairSucceeded: false }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNvbi1yZXBhaXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvZW5naW5lcy9jbGFzc2lmaWNhdGlvbi9qc29uLXJlcGFpci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7O0dBUUc7O0FBbUtILGtEQXdFQztBQTVORDs7R0FFRztBQUNILFNBQVMsb0JBQW9CLENBQUMsSUFBWTtJQUN4Qyx3Q0FBd0M7SUFDeEMsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO0lBQ2xFLElBQUksY0FBYyxFQUFFLENBQUM7UUFDbkIsTUFBTSxPQUFPLEdBQUcsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3pDLElBQUksT0FBTyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsSUFBSSxPQUFPLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDdkQsT0FBTyxPQUFPLENBQUM7UUFDakIsQ0FBQztJQUNILENBQUM7SUFFRCw4QkFBOEI7SUFDOUIsTUFBTSxZQUFZLEdBQUc7UUFDbkIsZUFBZSxFQUFHLFNBQVM7UUFDM0IsZUFBZSxDQUFHLFFBQVE7S0FDM0IsQ0FBQztJQUVGLEtBQUssTUFBTSxPQUFPLElBQUksWUFBWSxFQUFFLENBQUM7UUFDbkMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsQyxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ1YsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbEIsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRDs7R0FFRztBQUNILFNBQVMsUUFBUSxDQUFDLElBQVk7SUFDNUIsSUFBSSxDQUFDO1FBQ0gsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUM7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Q0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQztJQUVELHFCQUFxQjtJQUNyQixPQUFPO1FBQ0wsTUFBTSxFQUFFLEtBQUs7UUFDYixTQUFTO1FBQ1QsV0FBVztRQUNYLGVBQWUsRUFBRSxLQUFLO0tBQ3ZCLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBKU09OIERldGVjdGlvbiBhbmQgUmVwYWlyIE1vZHVsZVxuICogXG4gKiBEZXRlY3RzIEpTT04gaW4gdGV4dCBhbmQgYXR0ZW1wdHMgdG8gcmVwYWlyIG1hbGZvcm1lZCBKU09OLlxuICogXG4gKiBAbW9kdWxlIGVuZ2luZXMvY2xhc3NpZmljYXRpb24vanNvbi1yZXBhaXJcbiAqIEBhdXRob3IgSGFpZWNcbiAqIEBsaWNlbnNlIE1JVFxuICovXG5cbmltcG9ydCB7IEpzb25SZXBhaXJTdGVwIH0gZnJvbSAnLi90eXBlcyc7XG5cbi8qKlxuICogUmVzdWx0IG9mIEpTT04gZGV0ZWN0aW9uIGFuZCByZXBhaXIuXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSnNvbkRldGVjdGlvblJlc3VsdCB7XG4gIGlzSnNvbjogYm9vbGVhbjtcbiAgbm9ybWFsaXplZEpzb24/OiB1bmtub3duO1xuICBjYW5kaWRhdGU6IHN0cmluZyB8IG51bGw7XG4gIHJlcGFpclN0ZXBzOiBKc29uUmVwYWlyU3RlcFtdO1xuICByZXBhaXJTdWNjZWVkZWQ6IGJvb2xlYW47XG59XG5cbi8qKlxuICogRXh0cmFjdHMgSlNPTiBjYW5kaWRhdGUgZnJvbSB0ZXh0LlxuICovXG5mdW5jdGlvbiBleHRyYWN0SnNvbkNhbmRpZGF0ZSh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcgfCBudWxsIHtcbiAgLy8gVHJ5IHRvIGZpbmQgSlNPTiBpbiBjb2RlIGJsb2NrcyBmaXJzdFxuICBjb25zdCBjb2RlQmxvY2tNYXRjaCA9IHRleHQubWF0Y2goL2BgYCg/Ompzb24pP1xccyooW1xcc1xcU10qPylgYGAvKTtcbiAgaWYgKGNvZGVCbG9ja01hdGNoKSB7XG4gICAgY29uc3QgY29udGVudCA9IGNvZGVCbG9ja01hdGNoWzFdLnRyaW0oKTtcbiAgICBpZiAoY29udGVudC5zdGFydHNXaXRoKCd7JykgfHwgY29udGVudC5zdGFydHNXaXRoKCdbJykpIHtcbiAgICAgIHJldHVybiBjb250ZW50O1xuICAgIH1cbiAgfVxuICBcbiAgLy8gTG9vayBmb3IgSlNPTi1saWtlIHBhdHRlcm5zXG4gIGNvbnN0IGpzb25QYXR0ZXJucyA9IFtcbiAgICAvKFxce1tcXHNcXFNdKlxcfSkvLCAgLy8gT2JqZWN0XG4gICAgLyhcXFtbXFxzXFxTXSpcXF0pLyAgIC8vIEFycmF5XG4gIF07XG4gIFxuICBmb3IgKGNvbnN0IHBhdHRlcm4gb2YganNvblBhdHRlcm5zKSB7XG4gICAgY29uc3QgbWF0Y2ggPSB0ZXh0Lm1hdGNoKHBhdHRlcm4pO1xuICAgIGlmIChtYXRjaCkge1xuICAgICAgcmV0dXJuIG1hdGNoWzFdO1xuICAgIH1cbiAgfVxuICBcbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogQXR0ZW1wdHMgdG8gcGFyc2UgSlNPTiwgcmV0dXJucyBudWxsIGlmIGludmFsaWQuXG4gKi9cbmZ1bmN0aW9uIHRyeVBhcnNlKGpzb246IHN0cmluZyk6IHVua25vd24gfCBudWxsIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gSlNPTi5wYXJzZShqc29uKTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXBhaXIgc3RlcDogUmVtb3ZlIHRyYWlsaW5nIGNvbW1hcy5cbiAqL1xuZnVuY3Rpb24gcmVtb3ZlVHJhaWxpbmdDb21tYXMoanNvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gUmVtb3ZlIHRyYWlsaW5nIGNvbW1hcyBiZWZvcmUgfSBvciBdXG4gIHJldHVybiBqc29uLnJlcGxhY2UoLywoXFxzKlt9XFxdXSkvZywgJyQxJyk7XG59XG5cbi8qKlxuICogUmVwYWlyIHN0ZXA6IEFkZCBtaXNzaW5nIHF1b3RlcyB0byBrZXlzLlxuICovXG5mdW5jdGlvbiBxdW90ZVVucXVvdGVkS2V5cyhqc29uOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBNYXRjaCB1bnF1b3RlZCBrZXlzIGZvbGxvd2VkIGJ5IDpcbiAgcmV0dXJuIGpzb24ucmVwbGFjZSgvKFt7LF1cXHMqKShbYS16QS1aX11bYS16QS1aMC05X10qKShcXHMqOikvZywgJyQxXCIkMlwiJDMnKTtcbn1cblxuLyoqXG4gKiBSZXBhaXIgc3RlcDogUmVwbGFjZSBzaW5nbGUgcXVvdGVzIHdpdGggZG91YmxlIHF1b3Rlcy5cbiAqL1xuZnVuY3Rpb24gcmVwbGFjZVNpbmdsZVF1b3Rlcyhqc29uOiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBTaW1wbGUgcmVwbGFjZW1lbnQgLSBtYXkgbm90IGhhbmRsZSBhbGwgZWRnZSBjYXNlc1xuICBsZXQgcmVzdWx0ID0gJyc7XG4gIGxldCBpblN0cmluZyA9IGZhbHNlO1xuICBsZXQgc3RyaW5nQ2hhciA9ICcnO1xuICBcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBqc29uLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3QgY2hhciA9IGpzb25baV07XG4gICAgY29uc3QgcHJldkNoYXIgPSBpID4gMCA/IGpzb25baSAtIDFdIDogJyc7XG4gICAgXG4gICAgaWYgKCFpblN0cmluZykge1xuICAgICAgaWYgKGNoYXIgPT09ICdcIicgfHwgY2hhciA9PT0gXCInXCIpIHtcbiAgICAgICAgaW5TdHJpbmcgPSB0cnVlO1xuICAgICAgICBzdHJpbmdDaGFyID0gY2hhcjtcbiAgICAgICAgcmVzdWx0ICs9ICdcIic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgKz0gY2hhcjtcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKGNoYXIgPT09IHN0cmluZ0NoYXIgJiYgcHJldkNoYXIgIT09ICdcXFxcJykge1xuICAgICAgICBpblN0cmluZyA9IGZhbHNlO1xuICAgICAgICByZXN1bHQgKz0gJ1wiJztcbiAgICAgIH0gZWxzZSBpZiAoY2hhciA9PT0gJ1wiJyAmJiBzdHJpbmdDaGFyID09PSBcIidcIikge1xuICAgICAgICByZXN1bHQgKz0gJ1xcXFxcIic7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXN1bHQgKz0gY2hhcjtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgXG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKlxuICogUmVwYWlyIHN0ZXA6IEVzY2FwZSB1bmVzY2FwZWQgbmV3bGluZXMgaW4gc3RyaW5ncy5cbiAqL1xuZnVuY3Rpb24gZXNjYXBlTmV3bGluZXMoanNvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gVGhpcyBpcyBhIHNpbXBsaWZpZWQgdmVyc2lvbiAtIHByb3BlciBpbXBsZW1lbnRhdGlvbiB3b3VsZCBuZWVkIHN0YXRlIHRyYWNraW5nXG4gIHJldHVybiBqc29uLnJlcGxhY2UoLyg/PCFcXFxcKVxcbi9nLCAnXFxcXG4nKTtcbn1cblxuLyoqXG4gKiBSZXBhaXIgc3RlcDogQ2xvc2UgdW5jbG9zZWQgYnJhY2VzIGFuZCBicmFja2V0cy5cbiAqL1xuZnVuY3Rpb24gY2xvc2VCcmFja2V0cyhqc29uOiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgYnJhY2VDb3VudCA9IDA7XG4gIGxldCBicmFja2V0Q291bnQgPSAwO1xuICBcbiAgZm9yIChjb25zdCBjaGFyIG9mIGpzb24pIHtcbiAgICBpZiAoY2hhciA9PT0gJ3snKSBicmFjZUNvdW50Kys7XG4gICAgZWxzZSBpZiAoY2hhciA9PT0gJ30nKSBicmFjZUNvdW50LS07XG4gICAgZWxzZSBpZiAoY2hhciA9PT0gJ1snKSBicmFja2V0Q291bnQrKztcbiAgICBlbHNlIGlmIChjaGFyID09PSAnXScpIGJyYWNrZXRDb3VudC0tO1xuICB9XG4gIFxuICBsZXQgcmVzdWx0ID0ganNvbjtcbiAgd2hpbGUgKGJyYWNrZXRDb3VudCA+IDApIHtcbiAgICByZXN1bHQgKz0gJ10nO1xuICAgIGJyYWNrZXRDb3VudC0tO1xuICB9XG4gIHdoaWxlIChicmFjZUNvdW50ID4gMCkge1xuICAgIHJlc3VsdCArPSAnfSc7XG4gICAgYnJhY2VDb3VudC0tO1xuICB9XG4gIFxuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIFJlcGFpciBzdGVwOiBSZW1vdmUgSmF2YVNjcmlwdC1zdHlsZSBjb21tZW50cy5cbiAqL1xuZnVuY3Rpb24gcmVtb3ZlQ29tbWVudHMoanNvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gUmVtb3ZlIHNpbmdsZS1saW5lIGNvbW1lbnRzXG4gIGxldCByZXN1bHQgPSBqc29uLnJlcGxhY2UoL1xcL1xcL1teXFxuXSovZywgJycpO1xuICAvLyBSZW1vdmUgbXVsdGktbGluZSBjb21tZW50c1xuICByZXN1bHQgPSByZXN1bHQucmVwbGFjZSgvXFwvXFwqW1xcc1xcU10qP1xcKlxcLy9nLCAnJyk7XG4gIHJldHVybiByZXN1bHQ7XG59XG5cbi8qKiBEZWZhdWx0IG1heGltdW0gcmVwYWlyIHN0ZXBzICovXG5jb25zdCBERUZBVUxUX01BWF9SRVBBSVJfU1RFUFMgPSA2O1xuXG4vKipcbiAqIERldGVjdHMgYW5kIHJlcGFpcnMgSlNPTiBpbiB0ZXh0LlxuICogXG4gKiBAcGFyYW0gdGV4dCAtIFRoZSB0ZXh0IHRvIGFuYWx5emVcbiAqIEBwYXJhbSBtYXhSZXBhaXJTdGVwcyAtIE1heGltdW0gcmVwYWlyIHN0ZXBzIHRvIGF0dGVtcHQgKGRlZmF1bHQ6IDYpXG4gKiBAcmV0dXJucyBKU09OIGRldGVjdGlvbiByZXN1bHQgd2l0aCByZXBhaXIgaW5mb3JtYXRpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRldGVjdEFuZFJlcGFpckpzb24oXG4gIHRleHQ6IHN0cmluZyxcbiAgbWF4UmVwYWlyU3RlcHM6IG51bWJlciA9IERFRkFVTFRfTUFYX1JFUEFJUl9TVEVQU1xuKTogSnNvbkRldGVjdGlvblJlc3VsdCB7XG4gIGNvbnN0IGNhbmRpZGF0ZSA9IGV4dHJhY3RKc29uQ2FuZGlkYXRlKHRleHQpO1xuICBjb25zdCByZXBhaXJTdGVwczogSnNvblJlcGFpclN0ZXBbXSA9IFtdO1xuICBcbiAgaWYgKCFjYW5kaWRhdGUpIHtcbiAgICByZXR1cm4ge1xuICAgICAgaXNKc29uOiBmYWxzZSxcbiAgICAgIGNhbmRpZGF0ZTogbnVsbCxcbiAgICAgIHJlcGFpclN0ZXBzOiBbXSxcbiAgICAgIHJlcGFpclN1Y2NlZWRlZDogZmFsc2VcbiAgICB9O1xuICB9XG4gIFxuICAvLyBUcnkgcGFyc2luZyBhcy1pcyBmaXJzdFxuICBsZXQgcGFyc2VkID0gdHJ5UGFyc2UoY2FuZGlkYXRlKTtcbiAgaWYgKHBhcnNlZCAhPT0gbnVsbCkge1xuICAgIHJldHVybiB7XG4gICAgICBpc0pzb246IHRydWUsXG4gICAgICBub3JtYWxpemVkSnNvbjogcGFyc2VkLFxuICAgICAgY2FuZGlkYXRlLFxuICAgICAgcmVwYWlyU3RlcHM6IFtdLFxuICAgICAgcmVwYWlyU3VjY2VlZGVkOiB0cnVlXG4gICAgfTtcbiAgfVxuICBcbiAgLy8gQXBwbHkgcmVwYWlyIHN0ZXBzIGluIG9yZGVyIChsaW1pdGVkIGJ5IG1heFJlcGFpclN0ZXBzKVxuICBjb25zdCByZXBhaXJzOiBBcnJheTx7IG5hbWU6IHN0cmluZzsgZm46IChzOiBzdHJpbmcpID0+IHN0cmluZyB9PiA9IFtcbiAgICB7IG5hbWU6ICdyZW1vdmVfY29tbWVudHMnLCBmbjogcmVtb3ZlQ29tbWVudHMgfSxcbiAgICB7IG5hbWU6ICdyZW1vdmVfdHJhaWxpbmdfY29tbWFzJywgZm46IHJlbW92ZVRyYWlsaW5nQ29tbWFzIH0sXG4gICAgeyBuYW1lOiAncXVvdGVfdW5xdW90ZWRfa2V5cycsIGZuOiBxdW90ZVVucXVvdGVkS2V5cyB9LFxuICAgIHsgbmFtZTogJ3JlcGxhY2Vfc2luZ2xlX3F1b3RlcycsIGZuOiByZXBsYWNlU2luZ2xlUXVvdGVzIH0sXG4gICAgeyBuYW1lOiAnZXNjYXBlX25ld2xpbmVzJywgZm46IGVzY2FwZU5ld2xpbmVzIH0sXG4gICAgeyBuYW1lOiAnY2xvc2VfYnJhY2tldHMnLCBmbjogY2xvc2VCcmFja2V0cyB9XG4gIF0uc2xpY2UoMCwgbWF4UmVwYWlyU3RlcHMpO1xuICBcbiAgbGV0IGN1cnJlbnQgPSBjYW5kaWRhdGU7XG4gIFxuICBmb3IgKGNvbnN0IHJlcGFpciBvZiByZXBhaXJzKSB7XG4gICAgY29uc3QgYmVmb3JlID0gY3VycmVudDtcbiAgICBjdXJyZW50ID0gcmVwYWlyLmZuKGN1cnJlbnQpO1xuICAgIGNvbnN0IGFwcGxpZWQgPSBjdXJyZW50ICE9PSBiZWZvcmU7XG4gICAgXG4gICAgcmVwYWlyU3RlcHMucHVzaCh7XG4gICAgICBzdGVwOiByZXBhaXIubmFtZSxcbiAgICAgIGFwcGxpZWRcbiAgICB9KTtcbiAgICBcbiAgICAvLyBUcnkgcGFyc2luZyBhZnRlciBlYWNoIHN0ZXBcbiAgICBpZiAoYXBwbGllZCkge1xuICAgICAgcGFyc2VkID0gdHJ5UGFyc2UoY3VycmVudCk7XG4gICAgICBpZiAocGFyc2VkICE9PSBudWxsKSB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgaXNKc29uOiB0cnVlLFxuICAgICAgICAgIG5vcm1hbGl6ZWRKc29uOiBwYXJzZWQsXG4gICAgICAgICAgY2FuZGlkYXRlLFxuICAgICAgICAgIHJlcGFpclN0ZXBzLFxuICAgICAgICAgIHJlcGFpclN1Y2NlZWRlZDogdHJ1ZVxuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBcbiAgLy8gQWxsIHJlcGFpcnMgZmFpbGVkXG4gIHJldHVybiB7XG4gICAgaXNKc29uOiBmYWxzZSxcbiAgICBjYW5kaWRhdGUsXG4gICAgcmVwYWlyU3RlcHMsXG4gICAgcmVwYWlyU3VjY2VlZGVkOiBmYWxzZVxuICB9O1xufVxuIl19