ngs-json-utils
Version:
A set of Angular utilities for deep cloning, serialization, and JSON manipulation.
412 lines • 50.5 kB
JavaScript
import { Injectable } from '@angular/core';
import * as i0 from "@angular/core";
/**
* A utility service for performing common JSON-related operations safely and effectively.
*/
export class NgsJsonUtilsService {
/**
* Safely converts a JavaScript value to a JSON string.
* If an error occurs during serialization, returns `undefined` instead of throwing an error.
*
* @param data - The value to serialize into a JSON string.
* @param replacer - Optional function to filter or transform properties.
* @param space - Optional number of spaces for formatting the output JSON.
* @returns A JSON string representation of the data, or `undefined` if serialization fails.
*/
stringify(data, replacer, space) {
try {
return JSON.stringify(data, replacer, space);
}
catch (error) {
console.error('Error during JSON.stringify:', error);
return undefined;
}
}
/**
* Safely parses a JSON string into a JavaScript object or value.
* If parsing fails, returns the provided default value (or `null` if not provided).
*
* @param json - The JSON string to parse.
* @param defaultValue - A fallback value to return if parsing fails (defaults to `null`).
* @returns The parsed object/value, or the default value if parsing fails.
*/
parse(json, defaultValue = null) {
try {
return JSON.parse(json);
}
catch (error) {
console.error('Error during JSON.parse:', error);
return defaultValue;
}
}
/**
* Creates a deep copy of a given value using JSON serialization.
* It relies on the JSON.stringify and JSON.parse methods to safely clone an object.
* This approach doesn't handle cyclic references and non-serializable properties (like functions).
*
* @param data - The value to create a deep copy of.
* @returns A deep copy of the input value, or undefined if cloning fails.
*/
deepCopy(data) {
try {
// Serializing and immediately deserializing the object to create a deep copy
const jsonString = JSON.stringify(data);
return JSON.parse(jsonString);
}
catch (error) {
console.error('Error during deep copy (JSON serialization)', error);
return undefined;
}
}
/**
* Safely validates if a given string is in valid JSON format.
* If the string is invalid, it logs the error and returns `false`.
*
* @param jsonString - The string to validate.
* @returns `true` if the string is valid JSON; otherwise, `false`.
*/
isValidJSON(jsonString) {
if (!jsonString) {
console.error('Invalid JSON string: empty or undefined input');
return false;
}
try {
JSON.parse(jsonString);
return true;
}
catch (error) {
console.error('Invalid JSON string:', error);
return false;
}
}
/**
* Compares two JSON-compatible values for equality using serialization.
* Logs an error to the console if serialization fails.
*
* @param obj1 - The first JSON-compatible value to compare.
* @param obj2 - The second JSON-compatible value to compare.
* @returns `true` if the two values are equal, `false` otherwise.
*/
equalJSON(obj1, obj2) {
try {
return JSON.stringify(obj1) === JSON.stringify(obj2);
}
catch (error) {
console.error('Failed to serialize objects for comparison', {
obj1,
obj2,
error,
});
return false;
}
}
/**
* Safely compares two values for deep equality, adhering strictly to JSON-compatible types.
* It does not handle cyclic references or non-JSON-serializable objects.
*
* @param obj1 - The first value to compare.
* @param obj2 - The second value to compare.
* @returns `true` if the two values are deeply equal, `false` otherwise.
*/
deepEqualJSON(obj1, obj2) {
const isObject = (val) => typeof val === 'object' && val !== null;
const deepEqualHelper = (a, b) => {
if (a === b)
return true;
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length)
return false;
return a.every((item, index) => deepEqualHelper(item, b[index]));
}
if (isObject(a) && isObject(b)) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length)
return false;
return keysA.every((key) => keysB.includes(key) && deepEqualHelper(a[key], b[key]));
}
return false;
};
return deepEqualHelper(obj1, obj2);
}
/**
* Safely performs a deep merge of two objects using JSON serialization.
* Handles cyclic references by catching errors gracefully.
* Source properties will overwrite corresponding target properties.
*
* @param target - The target object to be updated.
* @param source - The source object providing updates.
* @returns A new object with merged properties, or the original target if an error occurs.
*/
deepMerge(target, source) {
try {
const targetClone = JSON.stringify(target);
const sourceClone = JSON.stringify(source);
const merged = JSON.parse(`{${targetClone.slice(1, -1)},${sourceClone.slice(1, -1)}}`);
return merged;
}
catch (error) {
console.error('Error during safe deep merge:', error);
return target;
}
}
/**
* Safely filters an object's properties, keeping only those specified in the allowed keys.
* Handles cyclic references and non-serializable objects gracefully.
* Logs errors if any occur.
*
* @param obj - The object to filter.
* @param allowedKeys - An array of keys to retain in the filtered object.
* @returns A new object containing only the allowed keys, or an empty object if an error occurs.
*/
filterKeys(obj, allowedKeys) {
try {
const serializedObj = JSON.stringify(obj, (key, value) => {
if (typeof value === 'function') {
return undefined;
}
return value;
});
const parsedObj = JSON.parse(serializedObj);
return allowedKeys.reduce((acc, key) => {
if (key in parsedObj) {
acc[key] = parsedObj[key];
}
return acc;
}, {});
}
catch (error) {
console.error('Error during filtering object:', error);
return {};
}
}
/**
* Safely recursively updates the target object with values from the updates object.
* Handles serialization of objects and logs errors if any occur (e.g., with non-serializable data).
* If an error occurs during the update, the target remains unchanged.
*
* @param target - The object to be updated.
* @param updates - The object containing updates to apply.
* @returns A new object with updated values, or the original target if an error occurs.
*/
deepUpdate(target, updates) {
try {
const serializedUpdates = JSON.stringify(updates, (key, value) => {
if (typeof value === 'function' || value === undefined) {
return undefined;
}
return value;
});
const parsedUpdates = JSON.parse(serializedUpdates);
return Object.keys(parsedUpdates).reduce((acc, key) => {
const value = parsedUpdates[key];
if (value && typeof value === 'object' && !Array.isArray(value)) {
acc[key] = this.deepUpdate(acc[key] || {}, value);
}
else {
acc[key] = value;
}
return acc;
}, { ...target });
}
catch (error) {
console.error('Error during deep update:', error);
return target;
}
}
/**
* Searches for a value in a nested object by its key.
*
* @param obj - The object to search within.
* @param key - The key to look for.
* @returns The value associated with the key, or `undefined` if not found.
*/
findValueByKey(obj, key) {
if (key in obj) {
return obj[key];
}
for (const k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
const result = this.findValueByKey(obj[k], key);
if (result !== undefined) {
return result;
}
}
}
return undefined;
}
/**
* Safely searches for a value in a nested object by its key.
* If an error occurs during the search, returns `undefined` instead of throwing.
*
* @param obj - The object to search within.
* @param key - The key to look for.
* @returns The value associated with the key, or `undefined` if not found or an error occurs.
*/
safeFindValueByKey(obj, key) {
try {
if (key in obj) {
return obj[key];
}
for (const k in obj) {
if (typeof obj[k] === 'object' && obj[k] !== null) {
const result = this.safeFindValueByKey(obj[k], key);
if (result !== undefined) {
return result;
}
}
}
return undefined;
}
catch (error) {
console.error('Error during object search:', error);
return undefined;
}
}
/**
* Safely removes all `undefined` values from an object.
* If an error occurs during the cleaning process, returns the original object.
*
* @param obj - The object to clean.
* @returns A new object without `undefined` values, or the original object if an error occurs.
*/
removeUndefined(obj) {
try {
return JSON.parse(JSON.stringify(obj, (_, value) => value === undefined ? undefined : value));
}
catch (error) {
console.error('Error during object cleaning:', error);
return obj;
}
}
/**
* Safely converts a Map object to a plain JSON object.
* If an error occurs during the conversion, returns an empty object.
*
* @param map - The Map to convert.
* @returns A JSON object with the same key-value pairs as the Map, or an empty object if an error occurs.
*/
mapToJSON(map) {
try {
const obj = {};
map.forEach((value, key) => {
obj[String(key)] = value;
});
return obj;
}
catch (error) {
console.error('Error during Map to JSON conversion:', error);
return {};
}
}
/**
* Safely converts a plain JSON object to a Map object.
* If an error occurs during the conversion, returns an empty Map.
*
* @param json - The JSON object to convert.
* @returns A Map containing the key-value pairs from the JSON object, or an empty Map if an error occurs.
*/
jsonToMap(json) {
try {
if (json && typeof json === 'object' && !Array.isArray(json)) {
return new Map(Object.entries(json));
}
return new Map();
}
catch (error) {
console.error('Error during JSON to Map conversion:', error);
return new Map();
}
}
/**
* Safely merges multiple arrays of objects, keeping only unique objects based on a specified key.
* If invalid input is provided (non-array or non-object values), returns an empty array.
*
* @param arrays - An array of arrays of objects to merge.
* @param key - The key used to determine uniqueness (objects with the same key value are considered duplicates).
* @returns An array of unique objects based on the specified key, or an empty array if the input is invalid.
*/
mergeUniqueByKey(arrays, key) {
// Проверяем, что входные данные являются массивом и ключ - строкой
if (!Array.isArray(arrays) || typeof key !== 'string')
return [];
return arrays.flat().reduce((acc, obj) => {
// Проверяем, что каждый объект содержит заданный ключ
if (obj &&
obj.hasOwnProperty(key) &&
!acc.some((item) => item[key] === obj[key])) {
acc.push(obj);
}
return acc;
}, []);
}
/**
* Safely removes all keys from an object that have null or empty values.
* If the input is not an object, returns an empty object.
*
* @param obj - The object from which to remove empty values.
* @returns A new object with only non-empty values, or an empty object if the input is invalid.
*/
removeEmptyValues(obj) {
if (typeof obj !== 'object' || obj === null)
return {};
return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null && v !== ''));
}
/**
* Safely retrieves the value from an object based on a specified path (array of keys).
* Returns undefined if the object or the path is invalid.
*
* @param obj - The object to retrieve the value from.
* @param path - An array of keys representing the path to the value.
* @returns The value at the specified path, or undefined if the object or path is invalid.
*/
getByPath(obj, path) {
if (typeof obj !== 'object' || obj === null || !Array.isArray(path))
return undefined;
return path.reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj);
}
/**
* Safely extracts unique values from an array of objects based on a specific key.
* If the input is invalid (non-array or non-object values), returns an empty array.
*
* @param arr - An array of objects from which to extract unique values.
* @param key - The key used to extract unique values.
* @returns An array of unique values based on the specified key, or an empty array if the input is invalid.
*/
uniqueValuesByKey(arr, key) {
if (!Array.isArray(arr) || typeof key !== 'string')
return [];
return Array.from(new Set(arr.map((item) => (item && key in item ? item[key] : undefined))));
}
/**
* Safely flattens a nested object into a single-level object, using dot notation for nested keys.
* If the input is invalid (non-object or null), returns an empty object.
*
* @param obj - The object to flatten.
* @param prefix - The prefix to prepend to each key (used for nested objects).
* @returns A flattened object with dot notation keys, or an empty object if the input is invalid.
*/
flattenObject(obj, prefix = '') {
if (typeof obj !== 'object' || obj === null)
return {};
return Object.entries(obj).reduce((acc, [key, value]) => {
const newKey = prefix ? `${prefix}.${key}` : key;
if (typeof value === 'object' && value !== null) {
Object.assign(acc, this.flattenObject(value, newKey));
}
else {
acc[newKey] = value;
}
return acc;
}, {});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NgsJsonUtilsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NgsJsonUtilsService, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: NgsJsonUtilsService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root',
}]
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmdzLWpzb24tdXRpbHMuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL25ncy1qc29uLXV0aWxzL3NyYy9saWIvbmdzLWpzb24tdXRpbHMuc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQUUzQzs7R0FFRztBQUlILE1BQU0sT0FBTyxtQkFBbUI7SUFDOUI7Ozs7Ozs7O09BUUc7SUFDSCxTQUFTLENBQ1AsSUFBYSxFQUNiLFFBQTJDLEVBQzNDLEtBQWM7UUFFZCxJQUFJLENBQUM7WUFDSCxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFLFFBQVEsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUMvQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsOEJBQThCLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDckQsT0FBTyxTQUFTLENBQUM7UUFDbkIsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsS0FBSyxDQUFJLElBQVksRUFBRSxlQUF5QixJQUFJO1FBQ2xELElBQUksQ0FBQztZQUNILE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQU0sQ0FBQztRQUMvQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsMEJBQTBCLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDakQsT0FBTyxZQUFZLENBQUM7UUFDdEIsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0gsUUFBUSxDQUFJLElBQU87UUFDakIsSUFBSSxDQUFDO1lBQ0gsNkVBQTZFO1lBQzdFLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDeEMsT0FBTyxJQUFJLENBQUMs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QkFBTSxDQUFDO2dCQUNOLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxLQUFLLENBQUM7WUFDdEIsQ0FBQztZQUNELE9BQU8sR0FBRyxDQUFDO1FBQ2IsQ0FBQyxFQUNELEVBQUUsQ0FDSCxDQUFDO0lBQ0osQ0FBQzt3R0FyZFUsbUJBQW1COzRHQUFuQixtQkFBbUIsY0FGbEIsTUFBTTs7NEZBRVAsbUJBQW1CO2tCQUgvQixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuLyoqXG4gKiBBIHV0aWxpdHkgc2VydmljZSBmb3IgcGVyZm9ybWluZyBjb21tb24gSlNPTi1yZWxhdGVkIG9wZXJhdGlvbnMgc2FmZWx5IGFuZCBlZmZlY3RpdmVseS5cbiAqL1xuQEluamVjdGFibGUoe1xuICBwcm92aWRlZEluOiAncm9vdCcsXG59KVxuZXhwb3J0IGNsYXNzIE5nc0pzb25VdGlsc1NlcnZpY2Uge1xuICAvKipcbiAgICogU2FmZWx5IGNvbnZlcnRzIGEgSmF2YVNjcmlwdCB2YWx1ZSB0byBhIEpTT04gc3RyaW5nLlxuICAgKiBJZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nIHNlcmlhbGl6YXRpb24sIHJldHVybnMgYHVuZGVmaW5lZGAgaW5zdGVhZCBvZiB0aHJvd2luZyBhbiBlcnJvci5cbiAgICpcbiAgICogQHBhcmFtIGRhdGEgLSBUaGUgdmFsdWUgdG8gc2VyaWFsaXplIGludG8gYSBKU09OIHN0cmluZy5cbiAgICogQHBhcmFtIHJlcGxhY2VyIC0gT3B0aW9uYWwgZnVuY3Rpb24gdG8gZmlsdGVyIG9yIHRyYW5zZm9ybSBwcm9wZXJ0aWVzLlxuICAgKiBAcGFyYW0gc3BhY2UgLSBPcHRpb25hbCBudW1iZXIgb2Ygc3BhY2VzIGZvciBmb3JtYXR0aW5nIHRoZSBvdXRwdXQgSlNPTi5cbiAgICogQHJldHVybnMgQSBKU09OIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGF0YSwgb3IgYHVuZGVmaW5lZGAgaWYgc2VyaWFsaXphdGlvbiBmYWlscy5cbiAgICovXG4gIHN0cmluZ2lmeShcbiAgICBkYXRhOiB1bmtub3duLFxuICAgIHJlcGxhY2VyPzogKGtleTogc3RyaW5nLCB2YWx1ZTogYW55KSA9PiBhbnksXG4gICAgc3BhY2U/OiBudW1iZXIsXG4gICk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShkYXRhLCByZXBsYWNlciwgc3BhY2UpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBkdXJpbmcgSlNPTi5zdHJpbmdpZnk6JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IHBhcnNlcyBhIEpTT04gc3RyaW5nIGludG8gYSBKYXZhU2NyaXB0IG9iamVjdCBvciB2YWx1ZS5cbiAgICogSWYgcGFyc2luZyBmYWlscywgcmV0dXJucyB0aGUgcHJvdmlkZWQgZGVmYXVsdCB2YWx1ZSAob3IgYG51bGxgIGlmIG5vdCBwcm92aWRlZCkuXG4gICAqXG4gICAqIEBwYXJhbSBqc29uIC0gVGhlIEpTT04gc3RyaW5nIHRvIHBhcnNlLlxuICAgKiBAcGFyYW0gZGVmYXVsdFZhbHVlIC0gQSBmYWxsYmFjayB2YWx1ZSB0byByZXR1cm4gaWYgcGFyc2luZyBmYWlscyAoZGVmYXVsdHMgdG8gYG51bGxgKS5cbiAgICogQHJldHVybnMgVGhlIHBhcnNlZCBvYmplY3QvdmFsdWUsIG9yIHRoZSBkZWZhdWx0IHZhbHVlIGlmIHBhcnNpbmcgZmFpbHMuXG4gICAqL1xuICBwYXJzZTxUPihqc29uOiBzdHJpbmcsIGRlZmF1bHRWYWx1ZTogVCB8IG51bGwgPSBudWxsKTogVCB8IG51bGwge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gSlNPTi5wYXJzZShqc29uKSBhcyBUO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdFcnJvciBkdXJpbmcgSlNPTi5wYXJzZTonLCBlcnJvcik7XG4gICAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgZGVlcCBjb3B5IG9mIGEgZ2l2ZW4gdmFsdWUgdXNpbmcgSlNPTiBzZXJpYWxpemF0aW9uLlxuICAgKiBJdCByZWxpZXMgb24gdGhlIEpTT04uc3RyaW5naWZ5IGFuZCBKU09OLnBhcnNlIG1ldGhvZHMgdG8gc2FmZWx5IGNsb25lIGFuIG9iamVjdC5cbiAgICogVGhpcyBhcHByb2FjaCBkb2Vzbid0IGhhbmRsZSBjeWNsaWMgcmVmZXJlbmNlcyBhbmQgbm9uLXNlcmlhbGl6YWJsZSBwcm9wZXJ0aWVzIChsaWtlIGZ1bmN0aW9ucykuXG4gICAqXG4gICAqIEBwYXJhbSBkYXRhIC0gVGhlIHZhbHVlIHRvIGNyZWF0ZSBhIGRlZXAgY29weSBvZi5cbiAgICogQHJldHVybnMgQSBkZWVwIGNvcHkgb2YgdGhlIGlucHV0IHZhbHVlLCBvciB1bmRlZmluZWQgaWYgY2xvbmluZyBmYWlscy5cbiAgICovXG4gIGRlZXBDb3B5PFQ+KGRhdGE6IFQpOiBUIHwgdW5kZWZpbmVkIHtcbiAgICB0cnkge1xuICAgICAgLy8gU2VyaWFsaXppbmcgYW5kIGltbWVkaWF0ZWx5IGRlc2VyaWFsaXppbmcgdGhlIG9iamVjdCB0byBjcmVhdGUgYSBkZWVwIGNvcHlcbiAgICAgIGNvbnN0IGpzb25TdHJpbmcgPSBKU09OLnN0cmluZ2lmeShkYXRhKTtcbiAgICAgIHJldHVybiBKU09OLnBhcnNlKGpzb25TdHJpbmcpIGFzIFQ7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGR1cmluZyBkZWVwIGNvcHkgKEpTT04gc2VyaWFsaXphdGlvbiknLCBlcnJvcik7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgdmFsaWRhdGVzIGlmIGEgZ2l2ZW4gc3RyaW5nIGlzIGluIHZhbGlkIEpTT04gZm9ybWF0LlxuICAgKiBJZiB0aGUgc3RyaW5nIGlzIGludmFsaWQsIGl0IGxvZ3MgdGhlIGVycm9yIGFuZCByZXR1cm5zIGBmYWxzZWAuXG4gICAqXG4gICAqIEBwYXJhbSBqc29uU3RyaW5nIC0gVGhlIHN0cmluZyB0byB2YWxpZGF0ZS5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBzdHJpbmcgaXMgdmFsaWQgSlNPTjsgb3RoZXJ3aXNlLCBgZmFsc2VgLlxuICAgKi9cbiAgaXNWYWxpZEpTT04oanNvblN0cmluZzogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgaWYgKCFqc29uU3RyaW5nKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdJbnZhbGlkIEpTT04gc3RyaW5nOiBlbXB0eSBvciB1bmRlZmluZWQgaW5wdXQnKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgSlNPTi5wYXJzZShqc29uU3RyaW5nKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdJbnZhbGlkIEpTT04gc3RyaW5nOicsIGVycm9yKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ29tcGFyZXMgdHdvIEpTT04tY29tcGF0aWJsZSB2YWx1ZXMgZm9yIGVxdWFsaXR5IHVzaW5nIHNlcmlhbGl6YXRpb24uXG4gICAqIExvZ3MgYW4gZXJyb3IgdG8gdGhlIGNvbnNvbGUgaWYgc2VyaWFsaXphdGlvbiBmYWlscy5cbiAgICpcbiAgICogQHBhcmFtIG9iajEgLSBUaGUgZmlyc3QgSlNPTi1jb21wYXRpYmxlIHZhbHVlIHRvIGNvbXBhcmUuXG4gICAqIEBwYXJhbSBvYmoyIC0gVGhlIHNlY29uZCBKU09OLWNvbXBhdGlibGUgdmFsdWUgdG8gY29tcGFyZS5cbiAgICogQHJldHVybnMgYHRydWVgIGlmIHRoZSB0d28gdmFsdWVzIGFyZSBlcXVhbCwgYGZhbHNlYCBvdGhlcndpc2UuXG4gICAqL1xuICBlcXVhbEpTT048VD4ob2JqMTogVCwgb2JqMjogVCk6IGJvb2xlYW4ge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob2JqMSkgPT09IEpTT04uc3RyaW5naWZ5KG9iajIpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdGYWlsZWQgdG8gc2VyaWFsaXplIG9iamVjdHMgZm9yIGNvbXBhcmlzb24nLCB7XG4gICAgICAgIG9iajEsXG4gICAgICAgIG9iajIsXG4gICAgICAgIGVycm9yLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNhZmVseSBjb21wYXJlcyB0d28gdmFsdWVzIGZvciBkZWVwIGVxdWFsaXR5LCBhZGhlcmluZyBzdHJpY3RseSB0byBKU09OLWNvbXBhdGlibGUgdHlwZXMuXG4gICAqIEl0IGRvZXMgbm90IGhhbmRsZSBjeWNsaWMgcmVmZXJlbmNlcyBvciBub24tSlNPTi1zZXJpYWxpemFibGUgb2JqZWN0cy5cbiAgICpcbiAgICogQHBhcmFtIG9iajEgLSBUaGUgZmlyc3QgdmFsdWUgdG8gY29tcGFyZS5cbiAgICogQHBhcmFtIG9iajIgLSBUaGUgc2Vjb25kIHZhbHVlIHRvIGNvbXBhcmUuXG4gICAqIEByZXR1cm5zIGB0cnVlYCBpZiB0aGUgdHdvIHZhbHVlcyBhcmUgZGVlcGx5IGVxdWFsLCBgZmFsc2VgIG90aGVyd2lzZS5cbiAgICovXG4gIGRlZXBFcXVhbEpTT048VD4ob2JqMTogVCwgb2JqMjogVCk6IGJvb2xlYW4ge1xuICAgIGNvbnN0IGlzT2JqZWN0ID0gKHZhbDogdW5rbm93bik6IHZhbCBpcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9PlxuICAgICAgdHlwZW9mIHZhbCA9PT0gJ29iamVjdCcgJiYgdmFsICE9PSBudWxsO1xuXG4gICAgY29uc3QgZGVlcEVxdWFsSGVscGVyID0gKGE6IHVua25vd24sIGI6IHVua25vd24pOiBib29sZWFuID0+IHtcbiAgICAgIGlmIChhID09PSBiKSByZXR1cm4gdHJ1ZTtcblxuICAgICAgaWYgKEFycmF5LmlzQXJyYXkoYSkgJiYgQXJyYXkuaXNBcnJheShiKSkge1xuICAgICAgICBpZiAoYS5sZW5ndGggIT09IGIubGVuZ3RoKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHJldHVybiBhLmV2ZXJ5KChpdGVtLCBpbmRleCkgPT4gZGVlcEVxdWFsSGVscGVyKGl0ZW0sIGJbaW5kZXhdKSk7XG4gICAgICB9XG5cbiAgICAgIGlmIChpc09iamVjdChhKSAmJiBpc09iamVjdChiKSkge1xuICAgICAgICBjb25zdCBrZXlzQSA9IE9iamVjdC5rZXlzKGEpO1xuICAgICAgICBjb25zdCBrZXlzQiA9IE9iamVjdC5rZXlzKGIpO1xuXG4gICAgICAgIGlmIChrZXlzQS5sZW5ndGggIT09IGtleXNCLmxlbmd0aCkgcmV0dXJuIGZhbHNlO1xuXG4gICAgICAgIHJldHVybiBrZXlzQS5ldmVyeShcbiAgICAgICAgICAoa2V5KSA9PiBrZXlzQi5pbmNsdWRlcyhrZXkpICYmIGRlZXBFcXVhbEhlbHBlcihhW2tleV0sIGJba2V5XSksXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIGRlZXBFcXVhbEhlbHBlcihvYmoxLCBvYmoyKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgcGVyZm9ybXMgYSBkZWVwIG1lcmdlIG9mIHR3byBvYmplY3RzIHVzaW5nIEpTT04gc2VyaWFsaXphdGlvbi5cbiAgICogSGFuZGxlcyBjeWNsaWMgcmVmZXJlbmNlcyBieSBjYXRjaGluZyBlcnJvcnMgZ3JhY2VmdWxseS5cbiAgICogU291cmNlIHByb3BlcnRpZXMgd2lsbCBvdmVyd3JpdGUgY29ycmVzcG9uZGluZyB0YXJnZXQgcHJvcGVydGllcy5cbiAgICpcbiAgICogQHBhcmFtIHRhcmdldCAtIFRoZSB0YXJnZXQgb2JqZWN0IHRvIGJlIHVwZGF0ZWQuXG4gICAqIEBwYXJhbSBzb3VyY2UgLSBUaGUgc291cmNlIG9iamVjdCBwcm92aWRpbmcgdXBkYXRlcy5cbiAgICogQHJldHVybnMgQSBuZXcgb2JqZWN0IHdpdGggbWVyZ2VkIHByb3BlcnRpZXMsIG9yIHRoZSBvcmlnaW5hbCB0YXJnZXQgaWYgYW4gZXJyb3Igb2NjdXJzLlxuICAgKi9cbiAgZGVlcE1lcmdlPFQ+KHRhcmdldDogVCwgc291cmNlOiBQYXJ0aWFsPFQ+KTogVCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHRhcmdldENsb25lID0gSlNPTi5zdHJpbmdpZnkodGFyZ2V0KTtcbiAgICAgIGNvbnN0IHNvdXJjZUNsb25lID0gSlNPTi5zdHJpbmdpZnkoc291cmNlKTtcblxuICAgICAgY29uc3QgbWVyZ2VkID0gSlNPTi5wYXJzZShcbiAgICAgICAgYHske3RhcmdldENsb25lLnNsaWNlKDEsIC0xKX0sJHtzb3VyY2VDbG9uZS5zbGljZSgxLCAtMSl9fWAsXG4gICAgICApO1xuXG4gICAgICByZXR1cm4gbWVyZ2VkIGFzIFQ7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGR1cmluZyBzYWZlIGRlZXAgbWVyZ2U6JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIHRhcmdldDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IGZpbHRlcnMgYW4gb2JqZWN0J3MgcHJvcGVydGllcywga2VlcGluZyBvbmx5IHRob3NlIHNwZWNpZmllZCBpbiB0aGUgYWxsb3dlZCBrZXlzLlxuICAgKiBIYW5kbGVzIGN5Y2xpYyByZWZlcmVuY2VzIGFuZCBub24tc2VyaWFsaXphYmxlIG9iamVjdHMgZ3JhY2VmdWxseS5cbiAgICogTG9ncyBlcnJvcnMgaWYgYW55IG9jY3VyLlxuICAgKlxuICAgKiBAcGFyYW0gb2JqIC0gVGhlIG9iamVjdCB0byBmaWx0ZXIuXG4gICAqIEBwYXJhbSBhbGxvd2VkS2V5cyAtIEFuIGFycmF5IG9mIGtleXMgdG8gcmV0YWluIGluIHRoZSBmaWx0ZXJlZCBvYmplY3QuXG4gICAqIEByZXR1cm5zIEEgbmV3IG9iamVjdCBjb250YWluaW5nIG9ubHkgdGhlIGFsbG93ZWQga2V5cywgb3IgYW4gZW1wdHkgb2JqZWN0IGlmIGFuIGVycm9yIG9jY3Vycy5cbiAgICovXG4gIGZpbHRlcktleXM8VCBleHRlbmRzIG9iamVjdCwgSyBleHRlbmRzIGtleW9mIFQ+KFxuICAgIG9iajogVCxcbiAgICBhbGxvd2VkS2V5czogS1tdLFxuICApOiBQYXJ0aWFsPFBpY2s8VCwgSz4+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3Qgc2VyaWFsaXplZE9iaiA9IEpTT04uc3RyaW5naWZ5KG9iaiwgKGtleSwgdmFsdWUpID0+IHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHZhbHVlO1xuICAgICAgfSk7XG4gICAgICBjb25zdCBwYXJzZWRPYmogPSBKU09OLnBhcnNlKHNlcmlhbGl6ZWRPYmopO1xuXG4gICAgICByZXR1cm4gYWxsb3dlZEtleXMucmVkdWNlKFxuICAgICAgICAoYWNjLCBrZXkpID0+IHtcbiAgICAgICAgICBpZiAoa2V5IGluIHBhcnNlZE9iaikge1xuICAgICAgICAgICAgYWNjW2tleV0gPSBwYXJzZWRPYmpba2V5XTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIGFjYztcbiAgICAgICAgfSxcbiAgICAgICAge30gYXMgUGFydGlhbDxQaWNrPFQsIEs+PixcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGR1cmluZyBmaWx0ZXJpbmcgb2JqZWN0OicsIGVycm9yKTtcbiAgICAgIHJldHVybiB7fSBhcyBQYXJ0aWFsPFBpY2s8VCwgSz4+O1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgcmVjdXJzaXZlbHkgdXBkYXRlcyB0aGUgdGFyZ2V0IG9iamVjdCB3aXRoIHZhbHVlcyBmcm9tIHRoZSB1cGRhdGVzIG9iamVjdC5cbiAgICogSGFuZGxlcyBzZXJpYWxpemF0aW9uIG9mIG9iamVjdHMgYW5kIGxvZ3MgZXJyb3JzIGlmIGFueSBvY2N1ciAoZS5nLiwgd2l0aCBub24tc2VyaWFsaXphYmxlIGRhdGEpLlxuICAgKiBJZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nIHRoZSB1cGRhdGUsIHRoZSB0YXJnZXQgcmVtYWlucyB1bmNoYW5nZWQuXG4gICAqXG4gICAqIEBwYXJhbSB0YXJnZXQgLSBUaGUgb2JqZWN0IHRvIGJlIHVwZGF0ZWQuXG4gICAqIEBwYXJhbSB1cGRhdGVzIC0gVGhlIG9iamVjdCBjb250YWluaW5nIHVwZGF0ZXMgdG8gYXBwbHkuXG4gICAqIEByZXR1cm5zIEEgbmV3IG9iamVjdCB3aXRoIHVwZGF0ZWQgdmFsdWVzLCBvciB0aGUgb3JpZ2luYWwgdGFyZ2V0IGlmIGFuIGVycm9yIG9jY3Vycy5cbiAgICovXG4gIGRlZXBVcGRhdGU8VD4odGFyZ2V0OiBULCB1cGRhdGVzOiBQYXJ0aWFsPFQ+KTogVCB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHNlcmlhbGl6ZWRVcGRhdGVzID0gSlNPTi5zdHJpbmdpZnkodXBkYXRlcywgKGtleSwgdmFsdWUpID0+IHtcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ2Z1bmN0aW9uJyB8fCB2YWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdmFsdWU7XG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHBhcnNlZFVwZGF0ZXMgPSBKU09OLnBhcnNlKHNlcmlhbGl6ZWRVcGRhdGVzKTtcblxuICAgICAgcmV0dXJuIE9iamVjdC5rZXlzKHBhcnNlZFVwZGF0ZXMpLnJlZHVjZShcbiAgICAgICAgKGFjYywga2V5KSA9PiB7XG4gICAgICAgICAgY29uc3QgdmFsdWUgPSBwYXJzZWRVcGRhdGVzW2tleSBhcyBrZXlvZiBUXTtcbiAgICAgICAgICBpZiAodmFsdWUgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheSh2YWx1ZSkpIHtcbiAgICAgICAgICAgIGFjY1trZXkgYXMga2V5b2YgVF0gPSB0aGlzLmRlZXBVcGRhdGUoXG4gICAgICAgICAgICAgIGFjY1trZXkgYXMga2V5b2YgVF0gfHwgKHt9IGFzIFQpLFxuICAgICAgICAgICAgICB2YWx1ZSxcbiAgICAgICAgICAgICkgYXMgYW55O1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBhY2Nba2V5IGFzIGtleW9mIFRdID0gdmFsdWUgYXMgYW55O1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgICB9LFxuICAgICAgICB7IC4uLnRhcmdldCB9LFxuICAgICAgKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgZHVyaW5nIGRlZXAgdXBkYXRlOicsIGVycm9yKTtcbiAgICAgIHJldHVybiB0YXJnZXQ7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNlYXJjaGVzIGZvciBhIHZhbHVlIGluIGEgbmVzdGVkIG9iamVjdCBieSBpdHMga2V5LlxuICAgKlxuICAgKiBAcGFyYW0gb2JqIC0gVGhlIG9iamVjdCB0byBzZWFyY2ggd2l0aGluLlxuICAgKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB0byBsb29rIGZvci5cbiAgICogQHJldHVybnMgVGhlIHZhbHVlIGFzc29jaWF0ZWQgd2l0aCB0aGUga2V5LCBvciBgdW5kZWZpbmVkYCBpZiBub3QgZm91bmQuXG4gICAqL1xuICBmaW5kVmFsdWVCeUtleTxUIGV4dGVuZHMgb2JqZWN0LCBLIGV4dGVuZHMga2V5b2YgVD4oXG4gICAgb2JqOiBULFxuICAgIGtleTogSyxcbiAgKTogVFtLXSB8IHVuZGVmaW5lZCB7XG4gICAgaWYgKGtleSBpbiBvYmopIHtcbiAgICAgIHJldHVybiBvYmpba2V5XTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGsgaW4gb2JqKSB7XG4gICAgICBpZiAodHlwZW9mIG9ialtrXSA9PT0gJ29iamVjdCcgJiYgb2JqW2tdICE9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuZmluZFZhbHVlQnlLZXkob2JqW2tdIGFzIFQsIGtleSk7XG4gICAgICAgIGlmIChyZXN1bHQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHJldHVybiByZXN1bHQ7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhZmVseSBzZWFyY2hlcyBmb3IgYSB2YWx1ZSBpbiBhIG5lc3RlZCBvYmplY3QgYnkgaXRzIGtleS5cbiAgICogSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgc2VhcmNoLCByZXR1cm5zIGB1bmRlZmluZWRgIGluc3RlYWQgb2YgdGhyb3dpbmcuXG4gICAqXG4gICAqIEBwYXJhbSBvYmogLSBUaGUgb2JqZWN0IHRvIHNlYXJjaCB3aXRoaW4uXG4gICAqIEBwYXJhbSBrZXkgLSBUaGUga2V5IHRvIGxvb2sgZm9yLlxuICAgKiBAcmV0dXJucyBUaGUgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoZSBrZXksIG9yIGB1bmRlZmluZWRgIGlmIG5vdCBmb3VuZCBvciBhbiBlcnJvciBvY2N1cnMuXG4gICAqL1xuICBzYWZlRmluZFZhbHVlQnlLZXk8VCBleHRlbmRzIG9iamVjdCwgSyBleHRlbmRzIGtleW9mIFQ+KFxuICAgIG9iajogVCxcbiAgICBrZXk6IEssXG4gICk6IFRbS10gfCB1bmRlZmluZWQge1xuICAgIHRyeSB7XG4gICAgICBpZiAoa2V5IGluIG9iaikge1xuICAgICAgICByZXR1cm4gb2JqW2tleV07XG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3QgayBpbiBvYmopIHtcbiAgICAgICAgaWYgKHR5cGVvZiBvYmpba10gPT09ICdvYmplY3QnICYmIG9ialtrXSAhPT0gbnVsbCkge1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMuc2FmZUZpbmRWYWx1ZUJ5S2V5KG9ialtrXSBhcyBULCBrZXkpO1xuICAgICAgICAgIGlmIChyZXN1bHQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgZHVyaW5nIG9iamVjdCBzZWFyY2g6JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IHJlbW92ZXMgYWxsIGB1bmRlZmluZWRgIHZhbHVlcyBmcm9tIGFuIG9iamVjdC5cbiAgICogSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyB0aGUgY2xlYW5pbmcgcHJvY2VzcywgcmV0dXJucyB0aGUgb3JpZ2luYWwgb2JqZWN0LlxuICAgKlxuICAgKiBAcGFyYW0gb2JqIC0gVGhlIG9iamVjdCB0byBjbGVhbi5cbiAgICogQHJldHVybnMgQSBuZXcgb2JqZWN0IHdpdGhvdXQgYHVuZGVmaW5lZGAgdmFsdWVzLCBvciB0aGUgb3JpZ2luYWwgb2JqZWN0IGlmIGFuIGVycm9yIG9jY3Vycy5cbiAgICovXG4gIHJlbW92ZVVuZGVmaW5lZDxUPihvYmo6IFQpOiBUIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIEpTT04ucGFyc2UoXG4gICAgICAgIEpTT04uc3RyaW5naWZ5KG9iaiwgKF8sIHZhbHVlKSA9PlxuICAgICAgICAgIHZhbHVlID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiB2YWx1ZSxcbiAgICAgICAgKSxcbiAgICAgICk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGR1cmluZyBvYmplY3QgY2xlYW5pbmc6JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIG9iajtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IGNvbnZlcnRzIGEgTWFwIG9iamVjdCB0byBhIHBsYWluIEpTT04gb2JqZWN0LlxuICAgKiBJZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nIHRoZSBjb252ZXJzaW9uLCByZXR1cm5zIGFuIGVtcHR5IG9iamVjdC5cbiAgICpcbiAgICogQHBhcmFtIG1hcCAtIFRoZSBNYXAgdG8gY29udmVydC5cbiAgICogQHJldHVybnMgQSBKU09OIG9iamVjdCB3aXRoIHRoZSBzYW1lIGtleS12YWx1ZSBwYWlycyBhcyB0aGUgTWFwLCBvciBhbiBlbXB0eSBvYmplY3QgaWYgYW4gZXJyb3Igb2NjdXJzLlxuICAgKi9cbiAgbWFwVG9KU09OPEssIFY+KG1hcDogTWFwPEssIFY+KTogUmVjb3JkPHN0cmluZywgVj4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBvYmo6IFJlY29yZDxzdHJpbmcsIFY+ID0ge307XG4gICAgICBtYXAuZm9yRWFjaCgodmFsdWUsIGtleSkgPT4ge1xuICAgICAgICBvYmpbU3RyaW5nKGtleSldID0gdmFsdWU7XG4gICAgICB9KTtcbiAgICAgIHJldHVybiBvYmo7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGR1cmluZyBNYXAgdG8gSlNPTiBjb252ZXJzaW9uOicsIGVycm9yKTtcbiAgICAgIHJldHVybiB7fTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IGNvbnZlcnRzIGEgcGxhaW4gSlNPTiBvYmplY3QgdG8gYSBNYXAgb2JqZWN0LlxuICAgKiBJZiBhbiBlcnJvciBvY2N1cnMgZHVyaW5nIHRoZSBjb252ZXJzaW9uLCByZXR1cm5zIGFuIGVtcHR5IE1hcC5cbiAgICpcbiAgICogQHBhcmFtIGpzb24gLSBUaGUgSlNPTiBvYmplY3QgdG8gY29udmVydC5cbiAgICogQHJldHVybnMgQSBNYXAgY29udGFpbmluZyB0aGUga2V5LXZhbHVlIHBhaXJzIGZyb20gdGhlIEpTT04gb2JqZWN0LCBvciBhbiBlbXB0eSBNYXAgaWYgYW4gZXJyb3Igb2NjdXJzLlxuICAgKi9cbiAganNvblRvTWFwPFY+KGpzb246IFJlY29yZDxzdHJpbmcsIFY+KTogTWFwPHN0cmluZywgVj4ge1xuICAgIHRyeSB7XG4gICAgICBpZiAoanNvbiAmJiB0eXBlb2YganNvbiA9PT0gJ29iamVjdCcgJiYgIUFycmF5LmlzQXJyYXkoanNvbikpIHtcbiAgICAgICAgcmV0dXJuIG5ldyBNYXAoT2JqZWN0LmVudHJpZXMoanNvbikpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG5ldyBNYXAoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgY29uc29sZS5lcnJvcignRXJyb3IgZHVyaW5nIEpTT04gdG8gTWFwIGNvbnZlcnNpb246JywgZXJyb3IpO1xuICAgICAgcmV0dXJuIG5ldyBNYXAoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogU2FmZWx5IG1lcmdlcyBtdWx0aXBsZSBhcnJheXMgb2Ygb2JqZWN0cywga2VlcGluZyBvbmx5IHVuaXF1ZSBvYmplY3RzIGJhc2VkIG9uIGEgc3BlY2lmaWVkIGtleS5cbiAgICogSWYgaW52YWxpZCBpbnB1dCBpcyBwcm92aWRlZCAobm9uLWFycmF5IG9yIG5vbi1vYmplY3QgdmFsdWVzKSwgcmV0dXJucyBhbiBlbXB0eSBhcnJheS5cbiAgICpcbiAgICogQHBhcmFtIGFycmF5cyAtIEFuIGFycmF5IG9mIGFycmF5cyBvZiBvYmplY3RzIHRvIG1lcmdlLlxuICAgKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB1c2VkIHRvIGRldGVybWluZSB1bmlxdWVuZXNzIChvYmplY3RzIHdpdGggdGhlIHNhbWUga2V5IHZhbHVlIGFyZSBjb25zaWRlcmVkIGR1cGxpY2F0ZXMpLlxuICAgKiBAcmV0dXJucyBBbiBhcnJheSBvZiB1bmlxdWUgb2JqZWN0cyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIGtleSwgb3IgYW4gZW1wdHkgYXJyYXkgaWYgdGhlIGlucHV0IGlzIGludmFsaWQuXG4gICAqL1xuICBtZXJnZVVuaXF1ZUJ5S2V5PFQgZXh0ZW5kcyBSZWNvcmQ8c3RyaW5nLCBhbnk+PihcbiAgICBhcnJheXM6IFRbXSxcbiAgICBrZXk6IHN0cmluZyxcbiAgKTogVFtdIHtcbiAgICAvLyDQn9GA0L7QstC10YDRj9C10LwsINGH0YLQviDQstGF0L7QtNC90YvQtSDQtNCw0L3QvdGL0LUg0Y/QstC70Y/RjtGC0YHRjyDQvNCw0YHRgdC40LLQvtC8INC4INC60LvRjtGHIC0g0YHRgtGA0L7QutC+0LlcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkoYXJyYXlzKSB8fCB0eXBlb2Yga2V5ICE9PSAnc3RyaW5nJykgcmV0dXJuIFtdO1xuXG4gICAgcmV0dXJuIGFycmF5cy5mbGF0KCkucmVkdWNlKChhY2M6IFRbXSwgb2JqOiBUKSA9PiB7XG4gICAgICAvLyDQn9GA0L7QstC10YDRj9C10LwsINGH0YLQviDQutCw0LbQtNGL0Lkg0L7QsdGK0LXQutGCINGB0L7QtNC10YDQttC40YIg0LfQsNC00LDQvdC90YvQuSDQutC70Y7Rh1xuICAgICAgaWYgKFxuICAgICAgICBvYmogJiZcbiAgICAgICAgb2JqLmhhc093blByb3BlcnR5KGtleSkgJiZcbiAgICAgICAgIWFjYy5zb21lKChpdGVtKSA9PiBpdGVtW2tleV0gPT09IG9ialtrZXldKVxuICAgICAgKSB7XG4gICAgICAgIGFjYy5wdXNoKG9iaik7XG4gICAgICB9XG4gICAgICByZXR1cm4gYWNjO1xuICAgIH0sIFtdKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgcmVtb3ZlcyBhbGwga2V5cyBmcm9tIGFuIG9iamVjdCB0aGF0IGhhdmUgbnVsbCBvciBlbXB0eSB2YWx1ZXMuXG4gICAqIElmIHRoZSBpbnB1dCBpcyBub3QgYW4gb2JqZWN0LCByZXR1cm5zIGFuIGVtcHR5IG9iamVjdC5cbiAgICpcbiAgICogQHBhcmFtIG9iaiAtIFRoZSBvYmplY3QgZnJvbSB3aGljaCB0byByZW1vdmUgZW1wdHkgdmFsdWVzLlxuICAgKiBAcmV0dXJucyBBIG5ldyBvYmplY3Qgd2l0aCBvbmx5IG5vbi1lbXB0eSB2YWx1ZXMsIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgaW5wdXQgaXMgaW52YWxpZC5cbiAgICovXG4gIHJlbW92ZUVtcHR5VmFsdWVzKG9iajogYW55KTogYW55IHtcbiAgICBpZiAodHlwZW9mIG9iaiAhPT0gJ29iamVjdCcgfHwgb2JqID09PSBudWxsKSByZXR1cm4ge307XG5cbiAgICByZXR1cm4gT2JqZWN0LmZyb21FbnRyaWVzKFxuICAgICAgT2JqZWN0LmVudHJpZXMob2JqKS5maWx0ZXIoKFtfLCB2XSkgPT4gdiAhPSBudWxsICYmIHYgIT09ICcnKSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhZmVseSByZXRyaWV2ZXMgdGhlIHZhbHVlIGZyb20gYW4gb2JqZWN0IGJhc2VkIG9uIGEgc3BlY2lmaWVkIHBhdGggKGFycmF5IG9mIGtleXMpLlxuICAgKiBSZXR1cm5zIHVuZGVmaW5lZCBpZiB0aGUgb2JqZWN0IG9yIHRoZSBwYXRoIGlzIGludmFsaWQuXG4gICAqXG4gICAqIEBwYXJhbSBvYmogLSBUaGUgb2JqZWN0IHRvIHJldHJpZXZlIHRoZSB2YWx1ZSBmcm9tLlxuICAgKiBAcGFyYW0gcGF0aCAtIEFuIGFycmF5IG9mIGtleXMgcmVwcmVzZW50aW5nIHRoZSBwYXRoIHRvIHRoZSB2YWx1ZS5cbiAgICogQHJldHVybnMgVGhlIHZhbHVlIGF0IHRoZSBzcGVjaWZpZWQgcGF0aCwgb3IgdW5kZWZpbmVkIGlmIHRoZSBvYmplY3Qgb3IgcGF0aCBpcyBpbnZhbGlkLlxuICAgKi9cbiAgZ2V0QnlQYXRoKG9iajogYW55LCBwYXRoOiBzdHJpbmdbXSk6IGFueSB7XG4gICAgaWYgKHR5cGVvZiBvYmogIT09ICdvYmplY3QnIHx8IG9iaiA9PT0gbnVsbCB8fCAhQXJyYXkuaXNBcnJheShwYXRoKSlcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG5cbiAgICByZXR1cm4gcGF0aC5yZWR1Y2UoXG4gICAgICAoYWNjLCBrZXkpID0+IChhY2MgJiYgYWNjW2tleV0gIT09IHVuZGVmaW5lZCA/IGFjY1trZXldIDogdW5kZWZpbmVkKSxcbiAgICAgIG9iaixcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFNhZmVseSBleHRyYWN0cyB1bmlxdWUgdmFsdWVzIGZyb20gYW4gYXJyYXkgb2Ygb2JqZWN0cyBiYXNlZCBvbiBhIHNwZWNpZmljIGtleS5cbiAgICogSWYgdGhlIGlucHV0IGlzIGludmFsaWQgKG5vbi1hcnJheSBvciBub24tb2JqZWN0IHZhbHVlcyksIHJldHVybnMgYW4gZW1wdHkgYXJyYXkuXG4gICAqXG4gICAqIEBwYXJhbSBhcnIgLSBBbiBhcnJheSBvZiBvYmplY3RzIGZyb20gd2hpY2ggdG8gZXh0cmFjdCB1bmlxdWUgdmFsdWVzLlxuICAgKiBAcGFyYW0ga2V5IC0gVGhlIGtleSB1c2VkIHRvIGV4dHJhY3QgdW5pcXVlIHZhbHVlcy5cbiAgICogQHJldHVybnMgQW4gYXJyYXkgb2YgdW5pcXVlIHZhbHVlcyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIGtleSwgb3IgYW4gZW1wdHkgYXJyYXkgaWYgdGhlIGlucHV0IGlzIGludmFsaWQuXG4gICAqL1xuICB1bmlxdWVWYWx1ZXNCeUtleTxUIGV4dGVuZHMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4+KFxuICAgIGFycjogVFtdLFxuICAgIGtleTogc3RyaW5nLFxuICApOiB1bmtub3duW10ge1xuICAgIGlmICghQXJyYXkuaXNBcnJheShhcnIpIHx8IHR5cGVvZiBrZXkgIT09ICdzdHJpbmcnKSByZXR1cm4gW107XG5cbiAgICByZXR1cm4gQXJyYXkuZnJvbShcbiAgICAgIG5ldyBTZXQoYXJyLm1hcCgoaXRlbSkgPT4gKGl0ZW0gJiYga2V5IGluIGl0ZW0gPyBpdGVtW2tleV0gOiB1bmRlZmluZWQpKSksXG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTYWZlbHkgZmxhdHRlbnMgYSBuZXN0ZWQgb2JqZWN0IGludG8gYSBzaW5nbGUtbGV2ZWwgb2JqZWN0LCB1c2luZyBkb3Qgbm90YXRpb24gZm9yIG5lc3RlZCBrZXlzLlxuICAgKiBJZiB0aGUgaW5wdXQgaXMgaW52YWxpZCAobm9uLW9iamVjdCBvciBudWxsKSwgcmV0dXJucyBhbiBlbXB0eSBvYmplY3QuXG4gICAqXG4gICAqIEBwYXJhbSBvYmogLSBUaGUgb2JqZWN0IHRvIGZsYXR0ZW4uXG4gICAqIEBwYXJhbSBwcmVmaXggLSBUaGUgcHJlZml4IHRvIHByZXBlbmQgdG8gZWFjaCBrZXkgKHVzZWQgZm9yIG5lc3RlZCBvYmplY3RzKS5cbiAgICogQHJldHVybnMgQSBmbGF0dGVuZWQgb2JqZWN0IHdpdGggZG90IG5vdGF0aW9uIGtleXMsIG9yIGFuIGVtcHR5IG9iamVjdCBpZiB0aGUgaW5wdXQgaXMgaW52YWxpZC5cbiAgICovXG4gIGZsYXR0ZW5PYmplY3Q8VCBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+PihcbiAgICBvYmo6IFQsXG4gICAgcHJlZml4OiBzdHJpbmcgPSAnJyxcbiAgKTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4ge1xuICAgIGlmICh0eXBlb2Ygb2JqICE9PSAnb2JqZWN0JyB8fCBvYmogPT09IG51bGwpIHJldHVybiB7fTtcblxuICAgIHJldHVybiBPYmplY3QuZW50cmllcyhvYmopLnJlZHVjZShcbiAgICAgIChhY2M6IFJlY29yZDxzdHJpbmcsIHVua25vd24+LCBba2V5LCB2YWx1ZV0pID0+IHtcbiAgICAgICAgY29uc3QgbmV3S2V5ID0gcHJlZml4ID8gYCR7cHJlZml4fS4ke2tleX1gIDoga2V5O1xuIC