@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
JavaScript
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