UNPKG

@openfisca/json-model

Version:

Library to handle informations extracted in JSON or YAML format from OpenFisca parameters, variables, etc

552 lines (543 loc) 107 kB
import { auditArray, auditBoolean, auditChain, auditCleanArray, auditFunction, auditHttpUrl, auditKeyValueDictionary, auditNonEmpty, auditNoop, auditNullish, auditNumber, auditOptions, auditRequire, auditSetNullish, auditString, auditSwitch, auditTest, auditTrimString, auditTuple, auditUnique } from "@auditors/core"; import { // BracketBase, // MaybeNumberValue, mergeReferences, ParameterClass, revaluationTypes, ScaleType, ValueType } from "../../parameters.js"; import { dateRegExp } from "../../periods.js"; import { auditDate } from "../periods.js"; export function auditRawBracketToEditablePhase1(audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "amount", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditRawValueOrExpectedToEditable(auditNumber), auditRequire])); audit.attribute(data, "average_rate", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditRawValueOrExpectedToEditable(auditNumber), auditRequire])); audit.attribute(data, "base", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditRawValueOrExpectedToEditable(auditNumber), auditRequire])); audit.attribute(data, "rate", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditRawValueOrExpectedToEditable(auditNumber), auditRequire])); audit.attribute(data, "threshold", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditRawValueOrExpectedToEditable(auditNumber), auditRequire]), auditRequire); return audit.reduceRemaining(data, errors, remainingKeys); } export function auditRawBracketToEditablePhase2Amount(audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "amount", true, errors, remainingKeys, auditRequire); audit.attribute(data, "average_rate", true, errors, remainingKeys, auditNullish); audit.attribute(data, "base", true, errors, remainingKeys, auditNullish); audit.attribute(data, "rate", true, errors, remainingKeys, auditNullish); audit.attribute(data, "threshold", true, errors, remainingKeys, auditRequire); return audit.reduceRemaining(data, errors, remainingKeys); } export function auditRawBracketPhase2AverageRate(audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "amount", true, errors, remainingKeys, auditNullish); audit.attribute(data, "average_rate", true, errors, remainingKeys, auditRequire); audit.attribute(data, "base", true, errors, remainingKeys); audit.attribute(data, "rate", true, errors, remainingKeys, auditNullish); audit.attribute(data, "threshold", true, errors, remainingKeys, auditRequire); if (errors.average_rate === undefined && errors.rate === undefined) { data.rate = data.average_rate; delete data.average_rate; } return audit.reduceRemaining(data, errors, remainingKeys); } export function auditRawBracketToEditablePhase2MarginalRate(audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "amount", true, errors, remainingKeys, auditNullish); audit.attribute(data, "base", true, errors, remainingKeys); audit.attribute(data, "average_rate", true, errors, remainingKeys, auditNullish); audit.attribute(data, "rate", true, errors, remainingKeys, auditRequire); audit.attribute(data, "threshold", true, errors, remainingKeys, auditRequire); return audit.reduceRemaining(data, errors, remainingKeys); } function auditRawMetadataBaseAttributesToEditable(audit, data, errors, remainingKeys) { for (const key of ["description", "label_en", "documentation", "short_label", "short_label_en"]) { audit.attribute(data, key, true, errors, remainingKeys, auditTrimString); } audit.attribute(data, "documentation_start", true, errors, remainingKeys, auditBoolean, auditFunction(value => value ? true : null)); audit.attribute(data, "inflator", true, errors, remainingKeys, auditTrimString); audit.attribute(data, "inflator_reference", true, errors, remainingKeys, auditRawReferencesToEditable); audit.attribute(data, "income_tax_year", true, errors, remainingKeys, auditBoolean); audit.attribute(data, "last_value_still_valid_on", true, errors, remainingKeys, auditDate); audit.attribute(data, "notes", true, errors, remainingKeys, auditRawNotesToEditable); audit.attribute(data, "official_journal_date", true, errors, remainingKeys, auditKeyValueDictionary(auditDate, [auditSwitch(auditDate, [auditString, auditFunction(text => text.split(";")), auditArray(auditDate), // TODO: Return something other than a string? auditFunction(instants => instants.join("; "))]), auditRequire])); audit.attribute(data, "reference", true, errors, remainingKeys, auditRawReferencesToEditable); audit.attribute(data, "revaluation_reference", true, errors, remainingKeys, auditRawReferencesToEditable); audit.attribute(data, "revaluation_type", true, errors, remainingKeys, auditTrimString, auditOptions(revaluationTypes)); } export function auditRawNodeParameterMetadataToEditable(parameterData, units, childrenId) { const children = parameterData.children; const testOrderItems = childrenId !== undefined || children != null && typeof children === "object"; if (childrenId === undefined) { childrenId = testOrderItems ? Object.keys(children) : []; } return function (audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); auditRawMetadataBaseAttributesToEditable(audit, data, errors, remainingKeys); // Used for compatibility with "barèmes IPP". audit.attribute(data, "order", true, errors, remainingKeys, auditCleanArray(auditString, testOrderItems ? auditOptions(childrenId) : auditNoop), auditUnique); audit.attribute(data, "unit", true, errors, remainingKeys, auditRawUnitNameToEditable(units)); return audit.reduceRemaining(data, errors, remainingKeys); }; } export function auditRawParameterToEditable(units, childrenId) { return function (audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); // Repair data before validation. // Move some attributes from data to metadata. // See function `_set_backward_compatibility_metadata` of // https://github.com/openfisca/openfisca-core/blob/master/openfisca_core/parameters/helpers.py for (const key of ["reference", "unit"]) { if (data[key] !== undefined) { let metadata = data.metadata; if (metadata === undefined) { metadata = data.metadata = {}; } metadata[key] = data[key]; delete data[key]; remainingKeys.delete(key); } } for (const key of ["description", "documentation", "file_path"]) { audit.attribute(data, key, true, errors, remainingKeys, auditTrimString); } audit.attribute(data, "documentation_start", true, errors, remainingKeys, auditBoolean, auditFunction(value => value ? true : null)); audit.attribute(data, "name", true, errors, remainingKeys, auditTrimString // auditTest((name) => { // const ids = name.split(".") // return ids[ids.length - 1].match(/^[\d]/) === null // }, "Last part of name must not start with a digit"), ); if (data["brackets"] !== undefined) { // Data is a Scale. data.class = ParameterClass.Scale; audit.attribute(data, "brackets", true, errors, remainingKeys, auditArray(auditRawBracketToEditablePhase1, auditNonEmpty, auditRequire), auditRequire); audit.attribute(data, "metadata", true, errors, remainingKeys, auditRawScaleMetadataToEditable(units)); } else { // Repair data before validation. // Create `values` attribute if it is omitted. const childrenKeys = [...remainingKeys].filter(key => !["metadata", "values"].includes(key)); if (data.values === undefined && childrenKeys.length > 0 && childrenKeys.every(key => dateRegExp.test(key))) { const values = data.values = {}; for (const instant of childrenKeys) { values[instant] = data[instant]; delete data[instant]; remainingKeys.delete(instant); } } if (data["values"] !== undefined) { // Data is a Parameter. data.class = ParameterClass.Value; audit.attribute(data, "metadata", true, errors, remainingKeys, auditRawValueParameterMetadataToEditable(units)); audit.attribute(data, "values", true, errors, remainingKeys, auditSwitch([auditKeyValueDictionary(auditDate, [auditRawValueOrValueOrExpectedToEditable(auditBoolean), auditRequire]), auditFunction(values => { data.type = ValueType.Boolean; return values; })], [auditKeyValueDictionary(auditDate, [auditRawValueOrValueOrExpectedToEditable(auditNumber), auditRequire]), auditFunction(values => { data.type = ValueType.Number; return values; })], [auditKeyValueDictionary(auditDate, [auditRawValueOrValueOrExpectedToEditable(auditArray(auditString)), auditRequire]), auditFunction(values => { data.type = ValueType.StringArray; return values; })], [auditKeyValueDictionary(auditDate, [auditRawValueOrValueOrExpectedToEditable(auditKeyValueDictionary(auditString, auditString)), auditRequire]), auditFunction(values => { data.type = ValueType.StringByString; return values; })]), auditRequire); } else { // Data is a NodeParameter. data.class = ParameterClass.Node; // Repair data before validation. // Create `children` attribute from children keys. const children = {}; for (const key of childrenKeys) { children[key] = data[key]; delete data[key]; remainingKeys.delete(key); } data.children = children; audit.attribute(data, "children", true, errors, remainingKeys, auditKeyValueDictionary(auditString, [auditRawParameterToEditable(units), auditRequire]) // auditRequire, // A node may have no child (especially unprocessed nodes). ); if (errors.children !== undefined && typeof errors.children === "object") { Object.assign(errors, errors.children); delete errors.children; } audit.attribute(data, "metadata", true, errors, remainingKeys, auditRawNodeParameterMetadataToEditable(data, units, childrenId)); } } if (errors.metadata === undefined && data.metadata != null) { // Merge metadata into parameter. const metadataErrors = {}; for (const [key, metadataValue] of Object.entries(data.metadata)) { const dataValue = data[key]; if (metadataValue !== dataValue) { if (dataValue === undefined) { data[key] = metadataValue; } else { metadataErrors[key] = "Field is present both in parameter and its metadata, with different values"; } } } if (Object.keys(metadataErrors).length > 0) { errors.metadata = metadataErrors; } else { delete data.metadata; } } if (data.class === ParameterClass.Scale && errors.brackets === undefined) { // For heuristic, see function `_get_at_instant` of // https://github.com/openfisca/openfisca-core/blob/master/openfisca_core/parameters/parameter_scale.py if (data.type === ScaleType.SingleAmount) { audit.attribute(data, "brackets", true, errors, remainingKeys, auditArray(auditRawBracketToEditablePhase2Amount, auditRequire), auditRequire); // An amount scale can only contain an amount_unit or threshold_unit. audit.attribute(data, "rate_unit", true, errors, remainingKeys, auditNullish); } else if (data.brackets.some(({ amount }) => amount != null)) { audit.attribute(data, "brackets", true, errors, remainingKeys, auditArray(auditRawBracketToEditablePhase2Amount, auditRequire), auditRequire); // An amount scale can only contain an amount_unit or threshold_unit. audit.attribute(data, "rate_unit", true, errors, remainingKeys, auditNullish); audit.attribute(data, "type", true, errors, remainingKeys, auditOptions([ScaleType.MarginalAmount]), auditSetNullish(ScaleType.MarginalAmount)); } else if (data.brackets.some(({ average_rate }) => average_rate != null)) { // A rate scale can only contain a rate_unit and a threshold_unit audit.attribute(data, "amount_unit", true, errors, remainingKeys, auditNullish); audit.attribute(data, "brackets", true, errors, remainingKeys, auditArray(auditRawBracketPhase2AverageRate, auditRequire), auditRequire); audit.attribute(data, "type", true, errors, remainingKeys, auditOptions([ScaleType.LinearAverageRate]), auditSetNullish(ScaleType.LinearAverageRate)); } else { // A rate scale can only contain a rate_unit and a threshold_unit audit.attribute(data, "amount_unit", true, errors, remainingKeys, auditNullish); audit.attribute(data, "brackets", true, errors, remainingKeys, auditArray(auditRawBracketToEditablePhase2MarginalRate, auditRequire), auditRequire); audit.attribute(data, "type", true, errors, remainingKeys, auditOptions([ScaleType.MarginalRate]), auditSetNullish(ScaleType.MarginalRate)); } // if (errors.brackets === undefined) { // // Sort brackets by thresholds // const brackets = data.brackets as BracketBase[] // // Si pour toutes les dates de threshold1, les values de threshold2 (qui ne sont pas expected ou null) sont inférieures // for (const [index, bracket2] of brackets.slice(1).entries()) { // const threshold1 = brackets[index].threshold // for (const [instant, value1] of Object.entries(threshold1)) { // if (value1 === "expected" || value1.value === null) { // continue // } // const value2 = bracket2.threshold[instant] // if ( // value2 === undefined || // value2 === "expected" || // value2.value === null || // Object.values(bracket2).every( // (valueAtInstant: { // [instant: string]: MaybeNumberValue | "expected" // }) => { // const value = valueAtInstant[instant] // return ( // value === undefined || // value === "expected" || // value.value === null || // value.value === 0 // ) // }, // ) // ) { // continue // } // if (value1.value > value2.value) { // errors.brackets = { // [index]: `At ${instant}, value ${value1.value} of this bracket is greater than the value ${value2.value} of the next bracket`, // } // break // } // } // } // } if (errors.brackets === undefined && errors.metadata?.ipp_csv_id === undefined) { const ippCsvId = data.metadata?.ipp_csv_id; if (ippCsvId !== undefined && typeof ippCsvId === "string") { const bracketsName = Object.keys(data.brackets); const ippCsvIdErrorByBracketName = {}; for (const bracketName of Object.keys(ippCsvId)) { if (!bracketsName.includes(bracketName)) { ippCsvIdErrorByBracketName[bracketName] = `Expected ${bracketsName.map(bracketName => `${JSON.stringify(bracketName)}`).join(" or ")} as key, got: ${JSON.stringify(bracketName)}`; } } if (Object.keys(ippCsvIdErrorByBracketName).length > 0) { if (errors.metadata === undefined) { errors.metadata = {}; } ; errors.metadata.ipp_csv_id = ippCsvIdErrorByBracketName; } } } // => Convert `unit` to `threshold unit` and delete it. if (errors.unit === undefined && data.unit != null) { if (errors.threshold_unit === undefined) { audit.attribute(data, "threshold_unit", true, errors, remainingKeys, auditNullish); if (errors.threshold_unit === undefined) { data.threshold_unit = data.unit; } } delete data.unit; } } // Merge the references of the parameter values (and brackets values) with // the references of the parameter. if (Object.keys(errors).length === 0) { const referencesByInstant = data.reference ?? {}; switch (data.class) { case ParameterClass.Node: { break; } case ParameterClass.Scale: { const brackets = data.brackets; for (const bracket of brackets) { for (const key of ["amount", "base", "rate", "threshold"]) { const values = bracket[key]; if (values === undefined) { continue; } for (const [instant, valueAtInstant] of Object.entries(values)) { if (valueAtInstant !== "expected" && valueAtInstant.reference != null) { const mergedReferences = mergeReferences(referencesByInstant[instant], valueAtInstant.reference); if (mergedReferences !== undefined) { referencesByInstant[instant] = mergedReferences; } delete valueAtInstant.reference; } } } } break; } case ParameterClass.Value: { for (const [instant, valueAtInstant] of Object.entries(data.values)) { if (valueAtInstant !== "expected" && valueAtInstant.reference != null) { const mergedReferences = mergeReferences(referencesByInstant[instant], valueAtInstant.reference); if (mergedReferences !== undefined) { referencesByInstant[instant] = mergedReferences; } delete valueAtInstant.reference; } } break; } } if (Object.keys(referencesByInstant).length > 0) { data.reference = referencesByInstant; } } return audit.reduceRemaining(data, errors, remainingKeys); }; } export function auditRawReferenceObjectToEditable(audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "href", true, errors, remainingKeys, auditHttpUrl, // Remove jsessionid from Légifrance URLs. auditFunction(url => url.replace(/;jsessionid=[^\/?]*/, ""))); audit.attribute(data, "long_title", true, errors, remainingKeys, auditTrimString); audit.attribute(data, "note", true, errors, remainingKeys, auditTrimString); audit.attribute(data, "title", true, errors, remainingKeys, auditTrimString); if (errors.href === undefined && errors.long_title === undefined && errors.note === undefined && errors.title === undefined && data.href == null && data.long_title == null && data.note == null && data.title == null) { errors.href = errors.long_title = errors.note = errors.title = "A reference object must contain a href and/or a note and/or a title and/or a long_title"; } return audit.reduceRemaining(data, errors, remainingKeys); } export function auditRawScaleMetadataToEditable(units) { return function (audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); auditRawMetadataBaseAttributesToEditable(audit, data, errors, remainingKeys); // Used only for compatibility with "barèmes IPP". audit.attribute(data, "ipp_csv_id", true, errors, remainingKeys, auditSwitch(auditTrimString, auditKeyValueDictionary(auditOptions(["amount", "average_rate", "base", "rate", "threshold"]), [auditTrimString, auditRequire]))); audit.attribute(data, "amount_unit", true, errors, remainingKeys, auditRawUnitNameToEditable(units)); audit.attribute( // UK data, "period", true, errors, remainingKeys, auditString, auditOptions(["hour", // UK "month", // UK "week", // UK "year" // UK ])); audit.attribute(data, "rate_unit", true, errors, remainingKeys, auditRawUnitNameToEditable(units)); audit.attribute(data, "threshold_unit", true, errors, remainingKeys, auditRawUnitNameToEditable(units)); audit.attribute(data, "type", true, errors, remainingKeys, auditString, auditOptions(["marginal_rate", "single_amount"])); audit.attribute(data, "unit", true, errors, remainingKeys, auditRawUnitNameToEditable(units)); return audit.reduceRemaining(data, errors, remainingKeys); }; } export function auditRawUnitNameToEditable(units) { const unitsName = units.map(({ name }) => name); return auditChain(auditString, auditTrimString, auditOptions(unitsName)); } export function auditRawValueParameterMetadataToEditable(units) { return function (audit, dataUnknown) { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); auditRawMetadataBaseAttributesToEditable(audit, data, errors, remainingKeys); // Used only for compatibility "barèmes IPP". audit.attribute(data, "ipp_csv_id", true, errors, remainingKeys, auditTrimString); audit.attribute( // UK data, "period", true, errors, remainingKeys, auditString, auditOptions(["hour", // UK "month", // UK "week", // UK "year" // UK ])); audit.attribute(data, "unit", true, errors, remainingKeys, // Remove US "bool" unit. auditFunction(unit => unit === "bool" ? undefined : unit), auditRawUnitNameToEditable(units)); return audit.reduceRemaining(data, errors, remainingKeys); }; } export function auditRawValueToEditable(...auditors) { return (audit, dataUnknown) => { if (dataUnknown == null) { return [dataUnknown, null]; } if (typeof dataUnknown !== "object") { return audit.unexpectedType(dataUnknown, "object"); } const data = { ...dataUnknown }; const errors = {}; const remainingKeys = new Set(Object.keys(data)); audit.attribute(data, "reference", true, errors, remainingKeys, auditSwitch([auditRawReferenceToEditable, auditFunction(reference => [reference])], auditCleanArray(auditRawReferenceToEditable))); audit.attribute(data, "value", false, // Keep null value. errors, remainingKeys, ...auditors); return audit.reduceRemaining(data, errors, remainingKeys); }; } export const auditRawReferenceToEditable = auditSwitch([auditString, auditSwitch([auditHttpUrl, // Remove jsessionid from Légifrance URLs. auditFunction(url => url.replace(/;jsessionid=[^\/?]*/, "")), auditFunction(href => ({ href }))], auditChain( // When string has the form "title: url", replace it with a reference object. auditTest(text => (text.match(/:/g) ?? []).length > 1), auditFunction(text => { const textSplit = text.split(":"); return [textSplit[0], textSplit.slice(1).join(":")]; }), auditTuple(auditTrimString, auditHttpUrl), auditFunction(([title, href]) => ({ href, title }))), auditFunction(title => ({ title })))], auditRawReferenceObjectToEditable); export const auditRawReferencesToEditable = auditSwitch([auditRawReferenceToEditable, auditFunction(reference => ({ "0001-01-01": [reference] }))], [auditArray(), auditCleanArray(auditRawReferenceToEditable), auditFunction(references => ({ "0001-01-01": references }))], auditKeyValueDictionary(auditDate, auditSwitch([auditRawReferenceToEditable, auditFunction(reference => [reference])], [auditArray(), auditCleanArray(auditRawReferenceToEditable)]))); // export const auditRawNotesToEditable = auditSwitch( // [auditTrimString, auditFunction((note) => ({ "0001-01-01": note }))], // [ // auditArray(), // auditCleanArray(auditTrimString), // auditFunction((notes: string[]) => ({ // "0001-01-01": notes.map((note) => `- ${note}`).join("\n"), // })), // ], // auditKeyValueDictionary(auditDate, auditTrimString), // ) export const auditRawNotesToEditable = auditRawReferencesToEditable; export const auditRawValueOrExpectedToEditable = (...auditors) => auditSwitch(auditTest(value => value === "expected", 'Value is not "expected"'), auditRawValueToEditable(...auditors)); export const auditRawValueOrValueOrExpectedToEditable = (...auditors) => auditSwitch(auditTest(value => value === "expected", 'Value is not "expected"'), auditRawValueToEditable(...auditors), [...auditors, auditFunction(value => ({ value }))]); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhdWRpdEFycmF5IiwiYXVkaXRCb29sZWFuIiwiYXVkaXRDaGFpbiIsImF1ZGl0Q2xlYW5BcnJheSIsImF1ZGl0RnVuY3Rpb24iLCJhdWRpdEh0dHBVcmwiLCJhdWRpdEtleVZhbHVlRGljdGlvbmFyeSIsImF1ZGl0Tm9uRW1wdHkiLCJhdWRpdE5vb3AiLCJhdWRpdE51bGxpc2giLCJhdWRpdE51bWJlciIsImF1ZGl0T3B0aW9ucyIsImF1ZGl0UmVxdWlyZSIsImF1ZGl0U2V0TnVsbGlzaCIsImF1ZGl0U3RyaW5nIiwiYXVkaXRTd2l0Y2giLCJhdWRpdFRlc3QiLCJhdWRpdFRyaW1TdHJpbmciLCJhdWRpdFR1cGxlIiwiYXVkaXRVbmlxdWUiLCJtZXJnZVJlZmVyZW5jZXMiLCJQYXJhbWV0ZXJDbGFzcyIsInJldmFsdWF0aW9uVHlwZXMiLCJTY2FsZVR5cGUiLCJWYWx1ZVR5cGUiLCJkYXRlUmVnRXhwIiwiYXVkaXREYXRlIiwiYXVkaXRSYXdCcmFja2V0VG9FZGl0YWJsZVBoYXNlMSIsImF1ZGl0IiwiZGF0YVVua25vd24iLCJ1bmV4cGVjdGVkVHlwZSIsImRhdGEiLCJlcnJvcnMiLCJyZW1haW5pbmdLZXlzIiwiU2V0IiwiT2JqZWN0Iiwia2V5cyIsImF0dHJpYnV0ZSIsImF1ZGl0UmF3VmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZSIsInJlZHVjZVJlbWFpbmluZyIsImF1ZGl0UmF3QnJhY2tldFRvRWRpdGFibGVQaGFzZTJBbW91bnQiLCJhdWRpdFJhd0JyYWNrZXRQaGFzZTJBdmVyYWdlUmF0ZSIsImF2ZXJhZ2VfcmF0ZSIsInVuZGVmaW5lZCIsInJhdGUiLCJhdWRpdFJhd0JyYWNrZXRUb0VkaXRhYmxlUGhhc2UyTWFyZ2luYWxSYXRlIiwiYXVkaXRSYXdNZXRhZGF0YUJhc2VBdHRyaWJ1dGVzVG9FZGl0YWJsZSIsImtleSIsInZhbHVlIiwiYXVkaXRSYXdSZWZlcmVuY2VzVG9FZGl0YWJsZSIsImF1ZGl0UmF3Tm90ZXNUb0VkaXRhYmxlIiwidGV4dCIsInNwbGl0IiwiaW5zdGFudHMiLCJqb2luIiwiYXVkaXRSYXdOb2RlUGFyYW1ldGVyTWV0YWRhdGFUb0VkaXRhYmxlIiwicGFyYW1ldGVyRGF0YSIsInVuaXRzIiwiY2hpbGRyZW5JZCIsImNoaWxkcmVuIiwidGVzdE9yZGVySXRlbXMiLCJhdWRpdFJhd1VuaXROYW1lVG9FZGl0YWJsZSIsImF1ZGl0UmF3UGFyYW1ldGVyVG9FZGl0YWJsZSIsIm1ldGFkYXRhIiwiZGVsZXRlIiwiY2xhc3MiLCJTY2FsZSIsImF1ZGl0UmF3U2NhbGVNZXRhZGF0YVRvRWRpdGFibGUiLCJjaGlsZHJlbktleXMiLCJmaWx0ZXIiLCJpbmNsdWRlcyIsInZhbHVlcyIsImxlbmd0aCIsImV2ZXJ5IiwidGVzdCIsImluc3RhbnQiLCJWYWx1ZSIsImF1ZGl0UmF3VmFsdWVQYXJhbWV0ZXJNZXRhZGF0YVRvRWRpdGFibGUiLCJhdWRpdFJhd1ZhbHVlT3JWYWx1ZU9yRXhwZWN0ZWRUb0VkaXRhYmxlIiwidHlwZSIsIkJvb2xlYW4iLCJOdW1iZXIiLCJTdHJpbmdBcnJheSIsIlN0cmluZ0J5U3RyaW5nIiwiTm9kZSIsImFzc2lnbiIsIm1ldGFkYXRhRXJyb3JzIiwibWV0YWRhdGFWYWx1ZSIsImVudHJpZXMiLCJkYXRhVmFsdWUiLCJicmFja2V0cyIsIlNpbmdsZUFtb3VudCIsInNvbWUiLCJhbW91bnQiLCJNYXJnaW5hbEFtb3VudCIsIkxpbmVhckF2ZXJhZ2VSYXRlIiwiTWFyZ2luYWxSYXRlIiwiaXBwX2Nzdl9pZCIsImlwcENzdklkIiwiYnJhY2tldHNOYW1lIiwiaXBwQ3N2SWRFcnJvckJ5QnJhY2tldE5hbWUiLCJicmFja2V0TmFtZSIsIm1hcCIsIkpTT04iLCJzdHJpbmdpZnkiLCJ1bml0IiwidGhyZXNob2xkX3VuaXQiLCJyZWZlcmVuY2VzQnlJbnN0YW50IiwicmVmZXJlbmNlIiwiYnJhY2tldCIsInZhbHVlQXRJbnN0YW50IiwibWVyZ2VkUmVmZXJlbmNlcyIsImF1ZGl0UmF3UmVmZXJlbmNlT2JqZWN0VG9FZGl0YWJsZSIsInVybCIsInJlcGxhY2UiLCJocmVmIiwibG9uZ190aXRsZSIsIm5vdGUiLCJ0aXRsZSIsInVuaXRzTmFtZSIsIm5hbWUiLCJhdWRpdFJhd1ZhbHVlVG9FZGl0YWJsZSIsImF1ZGl0b3JzIiwiYXVkaXRSYXdSZWZlcmVuY2VUb0VkaXRhYmxlIiwibWF0Y2giLCJ0ZXh0U3BsaXQiLCJzbGljZSIsInJlZmVyZW5jZXMiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXVkaXRvcnMvcGFyYW1ldGVycy9yYXdfdG9fZWRpdGFibGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgQXVkaXQsXG4gIGF1ZGl0QXJyYXksXG4gIGF1ZGl0Qm9vbGVhbixcbiAgYXVkaXRDaGFpbixcbiAgYXVkaXRDbGVhbkFycmF5LFxuICBhdWRpdEZ1bmN0aW9uLFxuICBhdWRpdEh0dHBVcmwsXG4gIGF1ZGl0S2V5VmFsdWVEaWN0aW9uYXJ5LFxuICBhdWRpdE5vbkVtcHR5LFxuICBhdWRpdE5vb3AsXG4gIGF1ZGl0TnVsbGlzaCxcbiAgYXVkaXROdW1iZXIsXG4gIGF1ZGl0T3B0aW9ucyxcbiAgYXVkaXRSZXF1aXJlLFxuICBhdWRpdFNldE51bGxpc2gsXG4gIGF1ZGl0U3RyaW5nLFxuICBhdWRpdFN3aXRjaCxcbiAgYXVkaXRUZXN0LFxuICBhdWRpdFRyaW1TdHJpbmcsXG4gIGF1ZGl0VHVwbGUsXG4gIGF1ZGl0VW5pcXVlLFxuICB0eXBlIEF1ZGl0b3IsXG59IGZyb20gXCJAYXVkaXRvcnMvY29yZVwiXG5cbmltcG9ydCB7XG4gIC8vIEJyYWNrZXRCYXNlLFxuICAvLyBNYXliZU51bWJlclZhbHVlLFxuICBtZXJnZVJlZmVyZW5jZXMsXG4gIFBhcmFtZXRlckNsYXNzLFxuICByZXZhbHVhdGlvblR5cGVzLFxuICBTY2FsZVR5cGUsXG4gIFZhbHVlVHlwZSxcbiAgdHlwZSBBbW91bnRCcmFja2V0LFxuICB0eXBlIEJyYWNrZXRWYWx1ZUF0SW5zdGFudCxcbiAgdHlwZSBSYXRlQnJhY2tldCxcbiAgdHlwZSBWYWx1ZUF0SW5zdGFudCxcbn0gZnJvbSBcIi4uLy4uL3BhcmFtZXRlcnNcIlxuaW1wb3J0IHsgZGF0ZVJlZ0V4cCB9IGZyb20gXCIuLi8uLi9wZXJpb2RzXCJcbmltcG9ydCB0eXBlIHsgUmVmZXJlbmNlc0J5SW5zdGFudCB9IGZyb20gXCIuLi8uLi9yZWZlcmVuY2VzXCJcbmltcG9ydCB0eXBlIHsgVW5pdCB9IGZyb20gXCIuLi8uLi91bml0c1wiXG5cbmltcG9ydCB7IGF1ZGl0RGF0ZSB9IGZyb20gXCIuLi9wZXJpb2RzXCJcblxuZXhwb3J0IGZ1bmN0aW9uIGF1ZGl0UmF3QnJhY2tldFRvRWRpdGFibGVQaGFzZTEoXG4gIGF1ZGl0OiBBdWRpdCxcbiAgZGF0YVVua25vd246IHVua25vd24sXG4pOiBbdW5rbm93biwgdW5rbm93bl0ge1xuICBpZiAoZGF0YVVua25vd24gPT0gbnVsbCkge1xuICAgIHJldHVybiBbZGF0YVVua25vd24sIG51bGxdXG4gIH1cbiAgaWYgKHR5cGVvZiBkYXRhVW5rbm93biAhPT0gXCJvYmplY3RcIikge1xuICAgIHJldHVybiBhdWRpdC51bmV4cGVjdGVkVHlwZShkYXRhVW5rbm93biwgXCJvYmplY3RcIilcbiAgfVxuXG4gIGNvbnN0IGRhdGE6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0geyAuLi5kYXRhVW5rbm93biB9XG4gIGNvbnN0IGVycm9yczogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7fVxuICBjb25zdCByZW1haW5pbmdLZXlzID0gbmV3IFNldChPYmplY3Qua2V5cyhkYXRhKSlcblxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcImFtb3VudFwiLFxuICAgIHRydWUsXG4gICAgZXJyb3JzLFxuICAgIHJlbWFpbmluZ0tleXMsXG4gICAgYXVkaXRLZXlWYWx1ZURpY3Rpb25hcnkoYXVkaXREYXRlLCBbXG4gICAgICBhdWRpdFJhd1ZhbHVlT3JFeHBlY3RlZFRvRWRpdGFibGUoYXVkaXROdW1iZXIpLFxuICAgICAgYXVkaXRSZXF1aXJlLFxuICAgIF0pLFxuICApXG4gIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICBkYXRhLFxuICAgIFwiYXZlcmFnZV9yYXRlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdEtleVZhbHVlRGljdGlvbmFyeShhdWRpdERhdGUsIFtcbiAgICAgIGF1ZGl0UmF3VmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZShhdWRpdE51bWJlciksXG4gICAgICBhdWRpdFJlcXVpcmUsXG4gICAgXSksXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJiYXNlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdEtleVZhbHVlRGljdGlvbmFyeShhdWRpdERhdGUsIFtcbiAgICAgIGF1ZGl0UmF3VmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZShhdWRpdE51bWJlciksXG4gICAgICBhdWRpdFJlcXVpcmUsXG4gICAgXSksXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJyYXRlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdEtleVZhbHVlRGljdGlvbmFyeShhdWRpdERhdGUsIFtcbiAgICAgIGF1ZGl0UmF3VmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZShhdWRpdE51bWJlciksXG4gICAgICBhdWRpdFJlcXVpcmUsXG4gICAgXSksXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJ0aHJlc2hvbGRcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0S2V5VmFsdWVEaWN0aW9uYXJ5KGF1ZGl0RGF0ZSwgW1xuICAgICAgYXVkaXRSYXdWYWx1ZU9yRXhwZWN0ZWRUb0VkaXRhYmxlKGF1ZGl0TnVtYmVyKSxcbiAgICAgIGF1ZGl0UmVxdWlyZSxcbiAgICBdKSxcbiAgICBhdWRpdFJlcXVpcmUsXG4gIClcblxuICByZXR1cm4gYXVkaXQucmVkdWNlUmVtYWluaW5nKGRhdGEsIGVycm9ycywgcmVtYWluaW5nS2V5cylcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGF1ZGl0UmF3QnJhY2tldFRvRWRpdGFibGVQaGFzZTJBbW91bnQoXG4gIGF1ZGl0OiBBdWRpdCxcbiAgZGF0YVVua25vd246IHVua25vd24sXG4pOiBbdW5rbm93biwgdW5rbm93bl0ge1xuICBpZiAoZGF0YVVua25vd24gPT0gbnVsbCkge1xuICAgIHJldHVybiBbZGF0YVVua25vd24sIG51bGxdXG4gIH1cbiAgaWYgKHR5cGVvZiBkYXRhVW5rbm93biAhPT0gXCJvYmplY3RcIikge1xuICAgIHJldHVybiBhdWRpdC51bmV4cGVjdGVkVHlwZShkYXRhVW5rbm93biwgXCJvYmplY3RcIilcbiAgfVxuXG4gIGNvbnN0IGRhdGE6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0geyAuLi5kYXRhVW5rbm93biB9XG4gIGNvbnN0IGVycm9yczogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7fVxuICBjb25zdCByZW1haW5pbmdLZXlzID0gbmV3IFNldChPYmplY3Qua2V5cyhkYXRhKSlcblxuICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwgXCJhbW91bnRcIiwgdHJ1ZSwgZXJyb3JzLCByZW1haW5pbmdLZXlzLCBhdWRpdFJlcXVpcmUpXG4gIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICBkYXRhLFxuICAgIFwiYXZlcmFnZV9yYXRlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdE51bGxpc2gsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIFwiYmFzZVwiLCB0cnVlLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMsIGF1ZGl0TnVsbGlzaClcbiAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIFwicmF0ZVwiLCB0cnVlLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMsIGF1ZGl0TnVsbGlzaClcbiAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIFwidGhyZXNob2xkXCIsIHRydWUsIGVycm9ycywgcmVtYWluaW5nS2V5cywgYXVkaXRSZXF1aXJlKVxuXG4gIHJldHVybiBhdWRpdC5yZWR1Y2VSZW1haW5pbmcoZGF0YSwgZXJyb3JzLCByZW1haW5pbmdLZXlzKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gYXVkaXRSYXdCcmFja2V0UGhhc2UyQXZlcmFnZVJhdGUoXG4gIGF1ZGl0OiBBdWRpdCxcbiAgZGF0YVVua25vd246IHVua25vd24sXG4pOiBbdW5rbm93biwgdW5rbm93bl0ge1xuICBpZiAoZGF0YVVua25vd24gPT0gbnVsbCkge1xuICAgIHJldHVybiBbZGF0YVVua25vd24sIG51bGxdXG4gIH1cbiAgaWYgKHR5cGVvZiBkYXRhVW5rbm93biAhPT0gXCJvYmplY3RcIikge1xuICAgIHJldHVybiBhdWRpdC51bmV4cGVjdGVkVHlwZShkYXRhVW5rbm93biwgXCJvYmplY3RcIilcbiAgfVxuXG4gIGNvbnN0IGRhdGE6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0geyAuLi5kYXRhVW5rbm93biB9XG4gIGNvbnN0IGVycm9yczogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7fVxuICBjb25zdCByZW1haW5pbmdLZXlzID0gbmV3IFNldChPYmplY3Qua2V5cyhkYXRhKSlcblxuICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwgXCJhbW91bnRcIiwgdHJ1ZSwgZXJyb3JzLCByZW1haW5pbmdLZXlzLCBhdWRpdE51bGxpc2gpXG4gIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICBkYXRhLFxuICAgIFwiYXZlcmFnZV9yYXRlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdFJlcXVpcmUsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIFwiYmFzZVwiLCB0cnVlLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMpXG4gIGF1ZGl0LmF0dHJpYnV0ZShkYXRhLCBcInJhdGVcIiwgdHJ1ZSwgZXJyb3JzLCByZW1haW5pbmdLZXlzLCBhdWRpdE51bGxpc2gpXG4gIGF1ZGl0LmF0dHJpYnV0ZShkYXRhLCBcInRocmVzaG9sZFwiLCB0cnVlLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMsIGF1ZGl0UmVxdWlyZSlcblxuICBpZiAoZXJyb3JzLmF2ZXJhZ2VfcmF0ZSA9PT0gdW5kZWZpbmVkICYmIGVycm9ycy5yYXRlID09PSB1bmRlZmluZWQpIHtcbiAgICBkYXRhLnJhdGUgPSBkYXRhLmF2ZXJhZ2VfcmF0ZVxuICAgIGRlbGV0ZSBkYXRhLmF2ZXJhZ2VfcmF0ZVxuICB9XG5cbiAgcmV0dXJuIGF1ZGl0LnJlZHVjZVJlbWFpbmluZyhkYXRhLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMpXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhdWRpdFJhd0JyYWNrZXRUb0VkaXRhYmxlUGhhc2UyTWFyZ2luYWxSYXRlKFxuICBhdWRpdDogQXVkaXQsXG4gIGRhdGFVbmtub3duOiB1bmtub3duLFxuKTogW3Vua25vd24sIHVua25vd25dIHtcbiAgaWYgKGRhdGFVbmtub3duID09IG51bGwpIHtcbiAgICByZXR1cm4gW2RhdGFVbmtub3duLCBudWxsXVxuICB9XG4gIGlmICh0eXBlb2YgZGF0YVVua25vd24gIT09IFwib2JqZWN0XCIpIHtcbiAgICByZXR1cm4gYXVkaXQudW5leHBlY3RlZFR5cGUoZGF0YVVua25vd24sIFwib2JqZWN0XCIpXG4gIH1cblxuICBjb25zdCBkYXRhOiB7IFtrZXk6IHN0cmluZ106IHVua25vd24gfSA9IHsgLi4uZGF0YVVua25vd24gfVxuICBjb25zdCBlcnJvcnM6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0ge31cbiAgY29uc3QgcmVtYWluaW5nS2V5cyA9IG5ldyBTZXQoT2JqZWN0LmtleXMoZGF0YSkpXG5cbiAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIFwiYW1vdW50XCIsIHRydWUsIGVycm9ycywgcmVtYWluaW5nS2V5cywgYXVkaXROdWxsaXNoKVxuICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwgXCJiYXNlXCIsIHRydWUsIGVycm9ycywgcmVtYWluaW5nS2V5cylcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJhdmVyYWdlX3JhdGVcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0TnVsbGlzaCxcbiAgKVxuICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwgXCJyYXRlXCIsIHRydWUsIGVycm9ycywgcmVtYWluaW5nS2V5cywgYXVkaXRSZXF1aXJlKVxuICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwgXCJ0aHJlc2hvbGRcIiwgdHJ1ZSwgZXJyb3JzLCByZW1haW5pbmdLZXlzLCBhdWRpdFJlcXVpcmUpXG5cbiAgcmV0dXJuIGF1ZGl0LnJlZHVjZVJlbWFpbmluZyhkYXRhLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMpXG59XG5cbmZ1bmN0aW9uIGF1ZGl0UmF3TWV0YWRhdGFCYXNlQXR0cmlidXRlc1RvRWRpdGFibGUoXG4gIGF1ZGl0OiBBdWRpdCxcbiAgZGF0YTogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0sXG4gIGVycm9yczogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0sXG4gIHJlbWFpbmluZ0tleXM6IFNldDxzdHJpbmc+LFxuKTogdm9pZCB7XG4gIGZvciAoY29uc3Qga2V5IG9mIFtcbiAgICBcImRlc2NyaXB0aW9uXCIsXG4gICAgXCJsYWJlbF9lblwiLFxuICAgIFwiZG9jdW1lbnRhdGlvblwiLFxuICAgIFwic2hvcnRfbGFiZWxcIixcbiAgICBcInNob3J0X2xhYmVsX2VuXCIsXG4gIF0pIHtcbiAgICBhdWRpdC5hdHRyaWJ1dGUoZGF0YSwga2V5LCB0cnVlLCBlcnJvcnMsIHJlbWFpbmluZ0tleXMsIGF1ZGl0VHJpbVN0cmluZylcbiAgfVxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcImRvY3VtZW50YXRpb25fc3RhcnRcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0Qm9vbGVhbixcbiAgICBhdWRpdEZ1bmN0aW9uKCh2YWx1ZSkgPT4gKHZhbHVlID8gdHJ1ZSA6IG51bGwpKSxcbiAgKVxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcImluZmxhdG9yXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdFRyaW1TdHJpbmcsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJpbmZsYXRvcl9yZWZlcmVuY2VcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0UmF3UmVmZXJlbmNlc1RvRWRpdGFibGUsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJpbmNvbWVfdGF4X3llYXJcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0Qm9vbGVhbixcbiAgKVxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcImxhc3RfdmFsdWVfc3RpbGxfdmFsaWRfb25cIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0RGF0ZSxcbiAgKVxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcIm5vdGVzXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdFJhd05vdGVzVG9FZGl0YWJsZSxcbiAgKVxuICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgZGF0YSxcbiAgICBcIm9mZmljaWFsX2pvdXJuYWxfZGF0ZVwiLFxuICAgIHRydWUsXG4gICAgZXJyb3JzLFxuICAgIHJlbWFpbmluZ0tleXMsXG4gICAgYXVkaXRLZXlWYWx1ZURpY3Rpb25hcnkoYXVkaXREYXRlLCBbXG4gICAgICBhdWRpdFN3aXRjaChhdWRpdERhdGUsIFtcbiAgICAgICAgYXVkaXRTdHJpbmcsXG4gICAgICAgIGF1ZGl0RnVuY3Rpb24oKHRleHQpID0+IHRleHQuc3BsaXQoXCI7XCIpKSxcbiAgICAgICAgYXVkaXRBcnJheShhdWRpdERhdGUpLFxuICAgICAgICAvLyBUT0RPOiBSZXR1cm4gc29tZXRoaW5nIG90aGVyIHRoYW4gYSBzdHJpbmc/XG4gICAgICAgIGF1ZGl0RnVuY3Rpb24oKGluc3RhbnRzKSA9PiBpbnN0YW50cy5qb2luKFwiOyBcIikpLFxuICAgICAgXSksXG4gICAgICBhdWRpdFJlcXVpcmUsXG4gICAgXSksXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJyZWZlcmVuY2VcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0UmF3UmVmZXJlbmNlc1RvRWRpdGFibGUsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJyZXZhbHVhdGlvbl9yZWZlcmVuY2VcIixcbiAgICB0cnVlLFxuICAgIGVycm9ycyxcbiAgICByZW1haW5pbmdLZXlzLFxuICAgIGF1ZGl0UmF3UmVmZXJlbmNlc1RvRWRpdGFibGUsXG4gIClcbiAgYXVkaXQuYXR0cmlidXRlKFxuICAgIGRhdGEsXG4gICAgXCJyZXZhbHVhdGlvbl90eXBlXCIsXG4gICAgdHJ1ZSxcbiAgICBlcnJvcnMsXG4gICAgcmVtYWluaW5nS2V5cyxcbiAgICBhdWRpdFRyaW1TdHJpbmcsXG4gICAgYXVkaXRPcHRpb25zKHJldmFsdWF0aW9uVHlwZXMpLFxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhdWRpdFJhd05vZGVQYXJhbWV0ZXJNZXRhZGF0YVRvRWRpdGFibGUoXG4gIHBhcmFtZXRlckRhdGE6IHtcbiAgICBba2V5OiBzdHJpbmddOiB1bmtub3duXG4gIH0sXG4gIHVuaXRzOiBVbml0W10sXG4gIGNoaWxkcmVuSWQ6IHN0cmluZ1tdIHwgdW5kZWZpbmVkLFxuKTogQXVkaXRvciB7XG4gIGNvbnN0IGNoaWxkcmVuID0gcGFyYW1ldGVyRGF0YS5jaGlsZHJlblxuICBjb25zdCB0ZXN0T3JkZXJJdGVtcyA9XG4gICAgY2hpbGRyZW5JZCAhPT0gdW5kZWZpbmVkIHx8XG4gICAgKGNoaWxkcmVuICE9IG51bGwgJiYgdHlwZW9mIGNoaWxkcmVuID09PSBcIm9iamVjdFwiKVxuICBpZiAoY2hpbGRyZW5JZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgY2hpbGRyZW5JZCA9IHRlc3RPcmRlckl0ZW1zID8gT2JqZWN0LmtleXMoY2hpbGRyZW4hKSA6IFtdXG4gIH1cblxuICByZXR1cm4gZnVuY3Rpb24gKGF1ZGl0OiBBdWRpdCwgZGF0YVVua25vd246IHVua25vd24pOiBbdW5rbm93biwgdW5rbm93bl0ge1xuICAgIGlmIChkYXRhVW5rbm93biA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gW2RhdGFVbmtub3duLCBudWxsXVxuICAgIH1cbiAgICBpZiAodHlwZW9mIGRhdGFVbmtub3duICE9PSBcIm9iamVjdFwiKSB7XG4gICAgICByZXR1cm4gYXVkaXQudW5leHBlY3RlZFR5cGUoZGF0YVVua25vd24sIFwib2JqZWN0XCIpXG4gICAgfVxuXG4gICAgY29uc3QgZGF0YTogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7IC4uLmRhdGFVbmtub3duIH1cbiAgICBjb25zdCBlcnJvcnM6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0ge31cbiAgICBjb25zdCByZW1haW5pbmdLZXlzID0gbmV3IFNldChPYmplY3Qua2V5cyhkYXRhKSlcblxuICAgIGF1ZGl0UmF3TWV0YWRhdGFCYXNlQXR0cmlidXRlc1RvRWRpdGFibGUoYXVkaXQsIGRhdGEsIGVycm9ycywgcmVtYWluaW5nS2V5cylcbiAgICAvLyBVc2VkIGZvciBjb21wYXRpYmlsaXR5IHdpdGggXCJiYXLDqG1lcyBJUFBcIi5cbiAgICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgICBkYXRhLFxuICAgICAgXCJvcmRlclwiLFxuICAgICAgdHJ1ZSxcbiAgICAgIGVycm9ycyxcbiAgICAgIHJlbWFpbmluZ0tleXMsXG4gICAgICBhdWRpdENsZWFuQXJyYXkoXG4gICAgICAgIGF1ZGl0U3RyaW5nLFxuICAgICAgICB0ZXN0T3JkZXJJdGVtcyA/IGF1ZGl0T3B0aW9ucyhjaGlsZHJlbklkISkgOiBhdWRpdE5vb3AsXG4gICAgICApLFxuICAgICAgYXVkaXRVbmlxdWUsXG4gICAgKVxuICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgIGRhdGEsXG4gICAgICBcInVuaXRcIixcbiAgICAgIHRydWUsXG4gICAgICBlcnJvcnMsXG4gICAgICByZW1haW5pbmdLZXlzLFxuICAgICAgYXVkaXRSYXdVbml0TmFtZVRvRWRpdGFibGUodW5pdHMpLFxuICAgIClcblxuICAgIHJldHVybiBhdWRpdC5yZWR1Y2VSZW1haW5pbmcoZGF0YSwgZXJyb3JzLCByZW1haW5pbmdLZXlzKVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBhdWRpdFJhd1BhcmFtZXRlclRvRWRpdGFibGUoXG4gIHVuaXRzOiBVbml0W10sXG4gIGNoaWxkcmVuSWQ/OiBzdHJpbmdbXSB8IHVuZGVmaW5lZCxcbik6IEF1ZGl0b3Ige1xuICByZXR1cm4gZnVuY3Rpb24gKGF1ZGl0OiBBdWRpdCwgZGF0YVVua25vd246IHVua25vd24pOiBbdW5rbm93biwgdW5rbm93bl0ge1xuICAgIGlmIChkYXRhVW5rbm93biA9PSBudWxsKSB7XG4gICAgICByZXR1cm4gW2RhdGFVbmtub3duLCBudWxsXVxuICAgIH1cbiAgICBpZiAodHlwZW9mIGRhdGFVbmtub3duICE9PSBcIm9iamVjdFwiKSB7XG4gICAgICByZXR1cm4gYXVkaXQudW5leHBlY3RlZFR5cGUoZGF0YVVua25vd24sIFwib2JqZWN0XCIpXG4gICAgfVxuXG4gICAgY29uc3QgZGF0YTogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7IC4uLmRhdGFVbmtub3duIH1cbiAgICBjb25zdCBlcnJvcnM6IHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9ID0ge31cbiAgICBjb25zdCByZW1haW5pbmdLZXlzID0gbmV3IFNldChPYmplY3Qua2V5cyhkYXRhKSlcblxuICAgIC8vIFJlcGFpciBkYXRhIGJlZm9yZSB2YWxpZGF0aW9uLlxuICAgIC8vIE1vdmUgc29tZSBhdHRyaWJ1dGVzIGZyb20gZGF0YSB0byBtZXRhZGF0YS5cbiAgICAvLyBTZWUgZnVuY3Rpb24gYF9zZXRfYmFja3dhcmRfY29tcGF0aWJpbGl0eV9tZXRhZGF0YWAgb2ZcbiAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vb3BlbmZpc2NhL29wZW5maXNjYS1jb3JlL2Jsb2IvbWFzdGVyL29wZW5maXNjYV9jb3JlL3BhcmFtZXRlcnMvaGVscGVycy5weVxuICAgIGZvciAoY29uc3Qga2V5IG9mIFtcInJlZmVyZW5jZVwiLCBcInVuaXRcIl0pIHtcbiAgICAgIGlmIChkYXRhW2tleV0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBsZXQgbWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhIGFzIHsgW2tleTogc3RyaW5nXTogdW5rbm93biB9IHwgdW5kZWZpbmVkXG4gICAgICAgIGlmIChtZXRhZGF0YSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgbWV0YWRhdGEgPSBkYXRhLm1ldGFkYXRhID0ge31cbiAgICAgICAgfVxuICAgICAgICBtZXRhZGF0YVtrZXldID0gZGF0YVtrZXldXG4gICAgICAgIGRlbGV0ZSBkYXRhW2tleV1cbiAgICAgICAgcmVtYWluaW5nS2V5cy5kZWxldGUoa2V5KVxuICAgICAgfVxuICAgIH1cblxuICAgIGZvciAoY29uc3Qga2V5IG9mIFtcImRlc2NyaXB0aW9uXCIsIFwiZG9jdW1lbnRhdGlvblwiLCBcImZpbGVfcGF0aFwiXSkge1xuICAgICAgYXVkaXQuYXR0cmlidXRlKGRhdGEsIGtleSwgdHJ1ZSwgZXJyb3JzLCByZW1haW5pbmdLZXlzLCBhdWRpdFRyaW1TdHJpbmcpXG4gICAgfVxuICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgIGRhdGEsXG4gICAgICBcImRvY3VtZW50YXRpb25fc3RhcnRcIixcbiAgICAgIHRydWUsXG4gICAgICBlcnJvcnMsXG4gICAgICByZW1haW5pbmdLZXlzLFxuICAgICAgYXVkaXRCb29sZWFuLFxuICAgICAgYXVkaXRGdW5jdGlvbigodmFsdWUpID0+ICh2YWx1ZSA/IHRydWUgOiBudWxsKSksXG4gICAgKVxuICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgIGRhdGEsXG4gICAgICBcIm5hbWVcIixcbiAgICAgIHRydWUsXG4gICAgICBlcnJvcnMsXG4gICAgICByZW1haW5pbmdLZXlzLFxuICAgICAgYXVkaXRUcmltU3RyaW5nLFxuICAgICAgLy8gYXVkaXRUZXN0KChuYW1lKSA9PiB7XG4gICAgICAvLyAgIGNvbnN0IGlkcyA9IG5hbWUuc3BsaXQoXCIuXCIpXG4gICAgICAvLyAgIHJldHVybiBpZHNbaWRzLmxlbmd0aCAtIDFdLm1hdGNoKC9eW1xcZF0vKSA9PT0gbnVsbFxuICAgICAgLy8gfSwgXCJMYXN0IHBhcnQgb2YgbmFtZSBtdXN0IG5vdCBzdGFydCB3aXRoIGEgZGlnaXRcIiksXG4gICAgKVxuXG4gICAgaWYgKGRhdGFbXCJicmFja2V0c1wiXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAvLyBEYXRhIGlzIGEgU2NhbGUuXG4gICAgICBkYXRhLmNsYXNzID0gUGFyYW1ldGVyQ2xhc3MuU2NhbGVcbiAgICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgICAgZGF0YSxcbiAgICAgICAgXCJicmFja2V0c1wiLFxuICAgICAgICB0cnVlLFxuICAgICAgICBlcnJvcnMsXG4gICAgICAgIHJlbWFpbmluZ0tleXMsXG4gICAgICAgIGF1ZGl0QXJyYXkoXG4gICAgICAgICAgYXVkaXRSYXdCcmFja2V0VG9FZGl0YWJsZVBoYXNlMSxcbiAgICAgICAgICBhdWRpdE5vbkVtcHR5LFxuICAgICAgICAgIGF1ZGl0UmVxdWlyZSxcbiAgICAgICAgKSxcbiAgICAgICAgYXVkaXRSZXF1aXJlLFxuICAgICAgKVxuICAgICAgYXVkaXQuYXR0cmlidXRlKFxuICAgICAgICBkYXRhLFxuICAgICAgICBcIm1ldGFkYXRhXCIsXG4gICAgICAgIHRydWUsXG4gICAgICAgIGVycm9ycyxcbiAgICAgICAgcmVtYWluaW5nS2V5cyxcbiAgICAgICAgYXVkaXRSYXdTY2FsZU1ldGFkYXRhVG9FZGl0YWJsZSh1bml0cyksXG4gICAgICApXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFJlcGFpciBkYXRhIGJlZm9yZSB2YWxpZGF0aW9uLlxuICAgICAgLy8gQ3JlYXRlIGB2YWx1ZXNgIGF0dHJpYnV0ZSBpZiBpdCBpcyBvbWl0dGVkLlxuICAgICAgY29uc3QgY2hpbGRyZW5LZXlzID0gWy4uLnJlbWFpbmluZ0tleXNdLmZpbHRlcihcbiAgICAgICAgKGtleSkgPT4gIVtcIm1ldGFkYXRhXCIsIFwidmFsdWVzXCJdLmluY2x1ZGVzKGtleSksXG4gICAgICApXG4gICAgICBpZiAoXG4gICAgICAgIGRhdGEudmFsdWVzID09PSB1bmRlZmluZWQgJiZcbiAgICAgICAgY2hpbGRyZW5LZXlzLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgY2hpbGRyZW5LZXlzLmV2ZXJ5KChrZXkpID0+IGRhdGVSZWdFeHAudGVzdChrZXkpKVxuICAgICAgKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlczogeyBbaW5zdGFudDogc3RyaW5nXTogdW5rbm93biB9ID0gKGRhdGEudmFsdWVzID0ge30pXG4gICAgICAgIGZvciAoY29uc3QgaW5zdGFudCBvZiBjaGlsZHJlbktleXMpIHtcbiAgICAgICAgICB2YWx1ZXNbaW5zdGFudF0gPSBkYXRhW2luc3RhbnRdXG4gICAgICAgICAgZGVsZXRlIGRhdGFbaW5zdGFudF1cbiAgICAgICAgICByZW1haW5pbmdLZXlzLmRlbGV0ZShpbnN0YW50KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChkYXRhW1widmFsdWVzXCJdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgLy8gRGF0YSBpcyBhIFBhcmFtZXRlci5cbiAgICAgICAgZGF0YS5jbGFzcyA9IFBhcmFtZXRlckNsYXNzLlZhbHVlXG4gICAgICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgICAgICBkYXRhLFxuICAgICAgICAgIFwibWV0YWRhdGFcIixcbiAgICAgICAgICB0cnVlLFxuICAgICAgICAgIGVycm9ycyxcbiAgICAgICAgICByZW1haW5pbmdLZXlzLFxuICAgICAgICAgIGF1ZGl0UmF3VmFsdWVQYXJhbWV0ZXJNZXRhZGF0YVRvRWRpdGFibGUodW5pdHMpLFxuICAgICAgICApXG4gICAgICAgIGF1ZGl0LmF0dHJpYnV0ZShcbiAgICAgICAgICBkYXRhLFxuICAgICAgICAgIFwidmFsdWVzXCIsXG4gICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICBlcnJvcnMsXG4gICAgICAgICAgcmVtYWluaW5nS2V5cyxcbiAgICAgICAgICBhdWRpdFN3aXRjaChcbiAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgYXVkaXRLZXlWYWx1ZURpY3Rpb25hcnkoYXVkaXREYXRlLCBbXG4gICAgICAgICAgICAgICAgYXVkaXRSYXdWYWx1ZU9yVmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZShhdWRpdEJvb2xlYW4pLFxuICAgICAgICAgICAgICAgIGF1ZGl0UmVxdWlyZSxcbiAgICAgICAgICAgICAgXSksXG4gICAgICAgICAgICAgIGF1ZGl0RnVuY3Rpb24oKHZhbHVlcykgPT4ge1xuICAgICAgICAgICAgICAgIGRhdGEudHlwZSA9IFZhbHVlVHlwZS5Cb29sZWFuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlc1xuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIF0sXG4gICAgICAgICAgICBbXG4gICAgICAgICAgICAgIGF1ZGl0S2V5VmFsdWVEaWN0aW9uYXJ5KGF1ZGl0RGF0ZSwgW1xuICAgICAgICAgICAgICAgIGF1ZGl0UmF3VmFsdWVPclZhbHVlT3JFeHBlY3RlZFRvRWRpdGFibGUoYXVkaXROdW1iZXIpLFxuICAgICAgICAgICAgICAgIGF1ZGl0UmVxdWlyZSxcbiAgICAgICAgICAgICAgXSksXG4gICAgICAgICAgICAgIGF1ZGl0RnVuY3Rpb24oKHZhbHVlcykgPT4ge1xuICAgICAgICAgICAgICAgIGRhdGEudHlwZSA9IFZhbHVlVHlwZS5OdW1iZXJcbiAgICAgICAgICAgICAgICByZXR1cm4gdmFsdWVzXG4gICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIFtcbiAgICAgICAgICAgICAgYXVkaXRLZXlWYWx1ZURpY3Rpb25hcnkoYXVkaXREYXRlLCBbXG4gICAgICAgICAgICAgICAgYXVkaXRSYXdWYWx1ZU9yVmFsdWVPckV4cGVjdGVkVG9FZGl0YWJsZShcbiAgICAgICAgICAgICAgICAgIGF1ZGl0QXJyYXkoYXVkaXRTdHJpbmcpLFxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgYXVkaXRSZXF1aXJlLFxuICAgICAgICAgICAgICBdKSxcbiAgICAgICAgICAgICAgYXVkaXRGdW5jdGlvbigodmFsdWVzKSA9PiB7XG4gICAgICAgICAgICAgICAgZGF0YS50eXBlID0gVmFsdWVUeXBlLlN0cmluZ0FycmF5XG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlc1xuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIF0sXG4gICAgICAgICAgICBbXG4gICAgICAgICAgICAgIGF1ZGl0S2V5VmFsdWVEaWN0aW9uYXJ5KGF1ZGl0RGF0ZSwgW1xuICAgICAgICAgICAgICAgIGF1ZGl0UmF3VmFsdWVPclZhbHVlT3JFeHBlY3RlZFRvRWRpdGFibGUoXG4gICAgICAgICAgICAgICAgICBhdWRpdEtleVZhbHVlRGljdGlvbmFyeShhdWRpdFN0cmluZywgYXVkaXRTdHJpbmcpLFxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgYXVkaXRSZXF1aXJlLFxuICAgICAgICAgICAgICBdKSxcbiAgICAgICAgICAgICAgYXVkaXRGdW5jdGlvbigodmFsdWVzKSA9PiB7XG4gICAgICAgICAgICAgICAgZGF0YS50eXBlID0gVmFsdWVUeXBlLlN0cmluZ0J5U3RyaW5nXG4gICAgICAgICAgICAgICAgcmV0dXJuIHZhbHVlc1xuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIF0sXG4gICAgICAgICAgKSxcbiAgICAgICAgICBhdWRpdFJlcXVpcmUsXG4gICAgICAgIClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIERhdGEgaXMgYSBOb2RlUGFyYW1ldGVyLlxuICAgICAgICBkYXRhLmNsYXNzID0gUGFyYW1ldGVyQ2xhc3MuTm9kZVxuXG4gICAgICAgIC8vIFJlcGFpciBkYXRhIGJlZm9yZSB2YWxpZGF0aW9uLlxuICAgICAgICAvLyBDcmVhdGUgYGNoaWxkcmVuYCBhdHRyaWJ1dGUgZnJvbSBjaGlsZHJlbiBrZXlzLlxuICAgICAgICBjb25zdCBjaGlsZHJlbjogeyBba2V5OiBzdHJpbmddOiB1bmtub3duIH0gPSB7fVxuICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiBjaGlsZHJlbktleXMpIHtcbiAgICAgICAgICBjaGlsZHJlbltrZXldID0gZGF0YVtrZXldXG4gICAgICAgICAgZGVsZXRlIGRhdGFba2V5XVxuICAgICAgICAgIHJlbWFpbmluZ0tleXMuZGVsZXRlKGtleSlcbiAgICAgICAgfVxuICAgICAgICBkYXRhLmNoaWxkcmVuID0gY2hpbGRyZW5cblxuICAgICAgICBhdWRpdC5hdHRyaWJ1dGUoXG4gICAgICAgICAgZGF0YSxcbiAgICAgICAgICBcImNoaWxkcmVuXCIsXG4gICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICBlcnJvcnMsXG4gICAgICAgICAgcmVtYWluaW5nS2V5cyxcbiAgICAgICAgICBhdWRpdEtleVZhbHVlRGljdGlvbmFyeShhdWRpdFN0cmluZywgW1xuICAgICAgICAgICAgYXVkaXRSYXdQYXJhbWV0ZXJUb0VkaXRhYmxlKHVuaXRzKSxcbiAgICAgICAgICAgIGF1ZGl0UmVxdWlyZSxcbiAgICAgICAgICBdKSxcbiAgICAgICAgICAvLyBhdWRpdFJlcXVpcmUsIC8vIEEgbm9kZSBtYXkgaGF2ZSBubyBjaGlsZCAoZXNwZWNpYWxseSB1bnByb2Nlc3NlZCBub2RlcykuXG4gICAgICAgIClcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGVycm9ycy5jaGlsZHJlbiAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAgICAgdHlwZW9mIGVycm9ycy5jaGlsZHJlbiA9PT0gXCJvYmplY3RcIlxuICAgICAgICApIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKGVycm9ycywgZXJyb3JzLmNoaWxkcmVuKVxuICAgICAgICAgIGRlb