@rudderstack/integrations-lib
Version:
A comprehensive TypeScript library providing shared utilities, SDKs, and tools for RudderStack integrations and destinations.
453 lines • 56.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.deepFreezeProperty = exports.deepFreeze = exports.lockProperty = exports.flattenQueryParams = exports.generateRandomString = exports.uuidv5 = exports.convertToString = exports.generateUUID = exports.hashToSha256 = exports.formatTimeStamp = exports.getOffsetInSec = exports.flattenJson = exports.getValueFromPropertiesOrTraits = exports.getHashFromArrayWithValueAsObject = exports.getHashFromArrayWithDuplicate = exports.getHashFromArray = exports.removeUndefinedNullEmptyExclBoolInt = exports.isDefinedNotNullNotEmpty = exports.isEmptyObject = exports.isEmpty = exports.isObject = exports.getType = exports.isPrimitive = exports.stripTrailingSlash = exports.isValidUrl = exports.base64Convertor = exports.flattenMap = exports.removeUndefinedAndNullAndEmptyValues = exports.removeUndefinedAndNullValues = exports.removeNullValues = exports.removeUndefinedValues = exports.isBlank = exports.isDefinedAndNotNullAndNotEmpty = exports.isDefinedAndNotNull = exports.isNotNull = exports.isNotEmpty = exports.isDefined = exports.set = exports.get = void 0;
const lodash_1 = __importDefault(require("lodash"));
const get_value_1 = __importDefault(require("get-value"));
exports.get = get_value_1.default;
const set_value_1 = __importDefault(require("set-value"));
exports.set = set_value_1.default;
const moment_timezone_1 = __importDefault(require("moment-timezone"));
const sha256_1 = __importDefault(require("sha256"));
const crypto_1 = __importDefault(require("crypto"));
const uuid_1 = require("uuid");
const logger = __importStar(require("../logger"));
const isDefined = (x) => !lodash_1.default.isUndefined(x);
exports.isDefined = isDefined;
const isNotEmpty = (x) => !lodash_1.default.isEmpty(x);
exports.isNotEmpty = isNotEmpty;
const isNotNull = (x) => x != null;
exports.isNotNull = isNotNull;
const isDefinedAndNotNull = (x) => (0, exports.isDefined)(x) && (0, exports.isNotNull)(x);
exports.isDefinedAndNotNull = isDefinedAndNotNull;
const isDefinedAndNotNullAndNotEmpty = (x) => (0, exports.isDefined)(x) && (0, exports.isNotNull)(x) && (0, exports.isNotEmpty)(x);
exports.isDefinedAndNotNullAndNotEmpty = isDefinedAndNotNullAndNotEmpty;
const isBlank = (value) => lodash_1.default.isEmpty(lodash_1.default.toString(value));
exports.isBlank = isBlank;
// ========================================================================
const removeUndefinedValues = (obj) => {
if (!lodash_1.default.isObject(obj))
return obj;
return lodash_1.default.pickBy(obj, exports.isDefined);
};
exports.removeUndefinedValues = removeUndefinedValues;
const removeNullValues = (obj) => {
if (!lodash_1.default.isObject(obj))
return obj;
return lodash_1.default.pickBy(obj, exports.isNotNull);
};
exports.removeNullValues = removeNullValues;
const removeUndefinedAndNullValues = (obj) => {
if (!lodash_1.default.isObject(obj))
return obj;
return lodash_1.default.pickBy(obj, exports.isDefinedAndNotNull);
};
exports.removeUndefinedAndNullValues = removeUndefinedAndNullValues;
const removeUndefinedAndNullAndEmptyValues = (obj) => {
if (!lodash_1.default.isObject(obj))
return obj;
return lodash_1.default.pickBy(obj, exports.isDefinedAndNotNullAndNotEmpty);
};
exports.removeUndefinedAndNullAndEmptyValues = removeUndefinedAndNullAndEmptyValues;
/**
* The flattenMap function takes a collection as input and returns a flattened version of the collection. If the input is not an array or an object, it is returned as is. Otherwise, the function uses the flatMap function from the lodash library to flatten the collection.
* @param collection
* @returns
*/
const flattenMap = (collection) => {
if (!lodash_1.default.isArray(collection) && !lodash_1.default.isObject(collection))
return collection;
return lodash_1.default.flatMap(collection, (x) => x);
};
exports.flattenMap = flattenMap;
// ========================================================================
// GENERIC UTLITY
// ========================================================================
const base64Convertor = (string) => Buffer.from(string).toString('base64');
exports.base64Convertor = base64Convertor;
// return a valid URL object if correct else null
const isValidUrl = (url) => {
try {
return new URL(url);
}
catch (err) {
return null;
}
};
exports.isValidUrl = isValidUrl;
const stripTrailingSlash = (str) => str && str.endsWith('/') ? str.slice(0, -1) : str;
exports.stripTrailingSlash = stripTrailingSlash;
const isPrimitive = (arg) => {
const type = typeof arg;
return arg == null || (type !== 'object' && type !== 'function');
};
exports.isPrimitive = isPrimitive;
/**
*
* @param {*} arg
* @returns {type}
*
* Returns type of passed arg
* for null argss returns "NULL" insted of "object"
*
*/
const getType = (arg) => {
const type = typeof arg;
if (arg == null) {
return 'NULL';
}
if (Array.isArray(arg)) {
return 'array';
}
return type;
};
exports.getType = getType;
const isObject = (value) => {
const type = typeof value;
return value != null && (type === 'object' || type === 'function') && !Array.isArray(value);
};
exports.isObject = isObject;
const isEmpty = (input) => lodash_1.default.isEmpty(lodash_1.default.toString(input).trim());
exports.isEmpty = isEmpty;
/**
* Returns true for empty object {}
* @param {*} obj
* @returns
*/
const isEmptyObject = (obj) => {
if (!obj) {
logger.warn('input is undefined or null');
return true;
}
return Object.keys(obj).length === 0;
};
exports.isEmptyObject = isEmptyObject;
/**
* Function to check if value is Defined, Not null and Not Empty.
* Create this function, Because existing isDefinedAndNotNullAndNotEmpty(123) is returning false due to lodash _.isEmpty function.
* _.isEmpty is used to detect empty collections/objects and it will return true for Integer, Boolean values.
* ref: https://github.com/lodash/lodash/issues/496
* @param {*} value 123
* @returns yes
*/
const isDefinedNotNullNotEmpty = (value) => !(value === undefined ||
value === null ||
Number.isNaN(value) ||
(typeof value === 'object' && Object.keys(value).length === 0) ||
(typeof value === 'string' && value.trim().length === 0));
exports.isDefinedNotNullNotEmpty = isDefinedNotNullNotEmpty;
const removeUndefinedNullEmptyExclBoolInt = (obj) => {
if (!lodash_1.default.isObject(obj))
return obj;
return lodash_1.default.pickBy(obj, exports.isDefinedNotNullNotEmpty);
};
exports.removeUndefinedNullEmptyExclBoolInt = removeUndefinedNullEmptyExclBoolInt;
// Format the destination.Config.dynamicMap arrays to hashMap
const getHashFromArray = (arrays, fromKey = 'from', toKey = 'to', isLowerCase = true) => {
const hashMap = {};
if (Array.isArray(arrays)) {
arrays.forEach((array) => {
if ((0, exports.isEmpty)(array[fromKey]))
return;
hashMap[isLowerCase ? array[fromKey].toString().toLowerCase() : array[fromKey]] =
array[toKey];
});
}
return hashMap;
};
exports.getHashFromArray = getHashFromArray;
/**
* Format the destination.Config.dynamicMap arrays to hashMap
* where value is an array
* @param {} arrays [{"from":"prop1","to":"val1"},{"from":"prop1","to":"val2"},{"from":"prop2","to":"val2"}]
* @param {} fromKey="from"
* @param {} toKey="to"
* @param {} isLowerCase=true
* @param {} return hashmap {"prop1":["val1","val2"],"prop2":["val2"]}
*/
const getHashFromArrayWithDuplicate = (arrays, fromKey = 'from', toKey = 'to', isLowerCase = true) => {
const hashMap = {};
if (Array.isArray(arrays)) {
arrays.forEach((array) => {
if ((0, exports.isEmpty)(array[fromKey]))
return;
const key = isLowerCase
? array[fromKey].toString().toLowerCase().trim()
: array[fromKey].toString().trim();
if (hashMap[key]) {
hashMap[key].add(array[toKey]);
}
else {
hashMap[key] = new Set();
hashMap[key].add(array[toKey]);
}
});
}
return hashMap;
};
exports.getHashFromArrayWithDuplicate = getHashFromArrayWithDuplicate;
/**
* Format the arrays to hashMap with key as `fromKey` and value as Object
* @param {*} arrays [{"id":"a0b8efe1-c828-4c63-8850-0d0742888f9d","name":"Email","type":"email","type_config":{},"date_created":"1662225840284","hide_from_guests":false,"required":false}]
* @param {*} fromKey name
* @param {*} isLowerCase false
* @returns // {"Email":{"id":"a0b8efe1-c828-4c63-8850-0d0742888f9d","name":"Email","type":"email","type_config":{},"date_created":"1662225840284","hide_from_guests":false,"required":false}}
*/
const getHashFromArrayWithValueAsObject = (arrays, fromKey = 'from', isLowerCase = true) => {
const hashMap = {};
if (Array.isArray(arrays)) {
arrays.forEach((array) => {
if ((0, exports.isEmpty)(array[fromKey]))
return;
hashMap[isLowerCase ? array[fromKey].toLowerCase() : array[fromKey]] = array;
});
}
return hashMap;
};
exports.getHashFromArrayWithValueAsObject = getHashFromArrayWithValueAsObject;
/**
* Retrieves a value from a message object based on a given key. It searches for the key in the properties, traits, and context.traits of the message object in that order.
*
* @param {Object} options - The options object.
* @param {Object} options.message - The message object containing properties, traits, and context.traits.
* @param {string} options.key - The key to search for in the message object.
* @returns {*} The value found in the message object for the given key, or null if the key is not found or the value is null.
*/
const getValueFromPropertiesOrTraits = ({ message, key }) => {
const keySet = ['properties', 'traits', 'context.traits'];
const val = lodash_1.default.find(lodash_1.default.map(keySet, (k) => (0, get_value_1.default)(message, `${k}.${key}`)), (v) => !lodash_1.default.isNil(v));
return !lodash_1.default.isNil(val) ? val : null;
};
exports.getValueFromPropertiesOrTraits = getValueFromPropertiesOrTraits;
/**
* Flattens a JSON object into a single-level object.
*
* @param data - The JSON object to be flattened.
* @param separator - The character used to separate the keys in the flattened object. Default is '.'.
* @param mode - The mode used to flatten arrays. If set to 'strict', the array indices will be included in the flattened keys. Default is 'normal'.
* @returns The flattened JSON object.
*/
const flattenJson = (data, separator = '.', mode = 'normal') => {
const result = {};
// a recursive function to loop through the array of the data
const recurse = (cur, prop) => {
let i;
if (Object(cur) !== cur) {
result[prop] = cur;
}
else if (Array.isArray(cur)) {
for (i = 0; i < cur.length; i += 1) {
if (mode === 'strict') {
recurse(cur[i], `${prop}${separator}${i}`);
}
else {
recurse(cur[i], `${prop}[${i}]`);
}
}
if (cur.length === 0) {
result[prop] = [];
}
}
else {
let isEmptyFlag = true;
Object.keys(cur).forEach((key) => {
isEmptyFlag = false;
recurse(cur[key], prop ? `${prop}${separator}${key}` : key);
});
if (isEmptyFlag && prop)
result[prop] = {};
}
};
recurse(data, '');
return result;
};
exports.flattenJson = flattenJson;
/**
* Get the offset value in seconds for a given timezone.
*
* @param value - The timezone value for which the offset needs to be calculated.
* @returns The offset value in seconds for the given timezone. If the timezone is invalid or not found, it returns `undefined`.
*
* @example
* const offset = getOffsetInSec('Asia/Calcutta');
* console.log(offset); // Output: 19800
*/
const getOffsetInSec = (value) => {
const name = moment_timezone_1.default.tz.zone(value);
if (name) {
const x = (0, moment_timezone_1.default)().tz(value).format('Z');
const split = x.split(':');
const hour = Number(split[0]);
const min = Number(split[1]);
let sec = 0;
sec = hour < 0 ? -1 * (hour * -60 * 60 + min * 60) : hour * 60 * 60 + min * 60;
return sec;
}
return undefined;
};
exports.getOffsetInSec = getOffsetInSec;
/**
* Converts a date string into a formatted timestamp.
* If a format string is provided, it uses the moment library to format the date.
* If no format string is provided, it returns the timestamp of the date.
* @param dateStr - The date string to be formatted.
* @param format - (Optional) The format string to be used for formatting the date.
* @returns The formatted date as a string if a format string is provided,
* otherwise the timestamp of the date as a number.
*/
const formatTimeStamp = (dateStr, format) => {
const date = new Date(dateStr);
// moment format is passed. format accordingly
if (format) {
return moment_timezone_1.default.utc(date).format(format);
}
// return default format
return date.getTime();
};
exports.formatTimeStamp = formatTimeStamp;
const hashToSha256 = (value) => (0, sha256_1.default)(value);
exports.hashToSha256 = hashToSha256;
/**
* Generates a Universally Unique Identifier (UUID) using the `crypto.randomUUID()` method.
* The `disableEntropyCache` option is set to `true` to prevent caching of generated UUIDs.
*
* @returns {string} A randomly generated UUID.
*
* @see {@link https://nodejs.org/api/crypto.html#cryptorandomuuidoptions:~:text=options%20%3CObject%3E-,disableEntropyCache,-%3Cboolean%3E%20By}
*/
const generateUUID = () => crypto_1.default.randomUUID({ disableEntropyCache: true });
exports.generateUUID = generateUUID;
const convertToString = (value) => {
if (typeof value === 'string') {
return value;
}
return JSON.stringify(value);
};
exports.convertToString = convertToString;
const uuid_namespace = 'e76c6e51-06f9-4222-9636-5aba7e3a84b6';
const uuidv5 = (val) => (0, uuid_1.v5)(val, uuid_namespace);
exports.uuidv5 = uuidv5;
const generateRandomString = (length = 10) => crypto_1.default.randomBytes(length).toString('hex').slice(0, length);
exports.generateRandomString = generateRandomString;
/**
*
* @param {Record<string, any | any[]} qParams - Object containing parameters where values are arrays
* @returns {Record<string, any>} Flattened object where array values of length one are replaced with their first element
* @description
* Flattens an object by replacing array values of length one with their first element,
* while keeping non-array values or arrays of length more than one unchanged.
* In case of empty array, the key is removed from the output
*/
const flattenQueryParams = (qParams) => {
if (!qParams)
return {}; // Early return for nullish values
const formattedOutput = {};
Object.entries(qParams).forEach(([key, value]) => {
const finalValue = Array.isArray(value) && value.length <= 1 ? value[0] : value;
if (finalValue !== undefined && finalValue !== null) {
formattedOutput[key] = finalValue;
}
});
return formattedOutput;
};
exports.flattenQueryParams = flattenQueryParams;
/**
* Locks a property of an object, making it immutable.
* @param obj - The object to lock the property of.
* @param key - The key of the property to lock.
* @returns The object with the property locked.
*/
const lockProperty = (obj, key) => Object.defineProperty(obj, key, {
value: obj[key],
writable: false,
configurable: false,
});
exports.lockProperty = lockProperty;
/**
* Deep freezes an object, making it immutable.
* @param obj - The object to deep freeze.
* @returns {T | Readonly<T>} - If `propKey` is provided, returns the original object `T` with `obj[propKey]` deep frozen and `propKey` locked. Otherwise, returns a `Readonly<T>` version of the entire object.
*
* @example
* const config = deepFreeze({ api: { key: 'secret', timeout: 1000 } });
* // Attempting to modify config.api.key will throw an error in strict mode
*/
const deepFreeze = (obj) => {
// If the object is null, undefined, or not an object, return it as is
if (!obj || typeof obj !== 'object')
return obj;
const freezeIfObject = (value) => {
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
(0, exports.deepFreeze)(value);
}
};
// Freeze all properties of the object
Object.getOwnPropertyNames(obj).forEach((key) => {
// Access property using string key, which is allowed by Record<string, unknown>
freezeIfObject(obj[key]);
});
// Freeze the object itself and return it
return Object.freeze(obj);
};
exports.deepFreeze = deepFreeze;
/**
* Deep freezes a specific property of an object and locks that property from reassignment.
* @template T - The type of the object.
* @param {T} obj - The object containing the property to freeze.
* @param {keyof T} propKey - The key of the property to deep freeze and lock.
* @returns {T} - The original object `T` with `obj[propKey]` deep frozen and `propKey` locked.
*
* @example
* const myObject = { data: { value: 123 } };
* deepFreezeProperty(myObject, 'data');
* // myObject.data is now immutable, and myObject.data cannot be reassigned.
*/
const deepFreezeProperty = (obj, propKey) => {
// If the object is null, undefined, or not an object, or propKey is not found, return it as is
if (!obj || typeof obj !== 'object' || !Object.prototype.hasOwnProperty.call(obj, propKey))
return obj;
const valueToFreeze = obj[propKey];
if (valueToFreeze && typeof valueToFreeze === 'object' && !Object.isFrozen(valueToFreeze)) {
(0, exports.deepFreeze)(valueToFreeze);
}
// Lock the property from reassignment
(0, exports.lockProperty)(obj, propKey);
return obj;
};
exports.deepFreezeProperty = deepFreezeProperty;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlzYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9taXNjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLG9EQUF1QjtBQUN2QiwwREFBNEI7QUFlbkIsY0FmRixtQkFBRyxDQWVFO0FBZFosMERBQTRCO0FBY2QsY0FkUCxtQkFBRyxDQWNPO0FBYmpCLHNFQUFxQztBQUNyQyxvREFBNEI7QUFDNUIsb0RBQTRCO0FBQzVCLCtCQUEwQjtBQUMxQixrREFBb0M7QUFVN0IsTUFBTSxTQUFTLEdBQUcsQ0FBQyxDQUFVLEVBQUUsRUFBRSxDQUFDLENBQUMsZ0JBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFBOUMsUUFBQSxTQUFTLGFBQXFDO0FBQ3BELE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBVSxFQUFFLEVBQUUsQ0FBQyxDQUFDLGdCQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQTNDLFFBQUEsVUFBVSxjQUFpQztBQUNqRCxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQVUsRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQztBQUF0QyxRQUFBLFNBQVMsYUFBNkI7QUFDNUMsTUFBTSxtQkFBbUIsR0FBRyxDQUFDLENBQVUsRUFBRSxFQUFFLENBQUMsSUFBQSxpQkFBUyxFQUFDLENBQUMsQ0FBQyxJQUFJLElBQUEsaUJBQVMsRUFBQyxDQUFDLENBQUMsQ0FBQztBQUFuRSxRQUFBLG1CQUFtQix1QkFBZ0Q7QUFDekUsTUFBTSw4QkFBOEIsR0FBRyxDQUFDLENBQVUsRUFBRSxFQUFFLENBQzNELElBQUEsaUJBQVMsRUFBQyxDQUFDLENBQUMsSUFBSSxJQUFBLGlCQUFTLEVBQUMsQ0FBQyxDQUFDLElBQUksSUFBQSxrQkFBVSxFQUFDLENBQUMsQ0FBQyxDQUFDO0FBRG5DLFFBQUEsOEJBQThCLGtDQUNLO0FB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QUMsQ0FBQztJQUN2RCxDQUFDO0lBQ0Qsc0NBQXNDO0lBQ3RDLElBQUEsb0JBQVksRUFBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDM0IsT0FBTyxHQUFHLENBQUM7QUFDYixDQUFDLENBQUM7QUFmVyxRQUFBLGtCQUFrQixzQkFlN0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IGdldCBmcm9tICdnZXQtdmFsdWUnO1xuaW1wb3J0IHNldCBmcm9tICdzZXQtdmFsdWUnO1xuaW1wb3J0IG1vbWVudCBmcm9tICdtb21lbnQtdGltZXpvbmUnO1xuaW1wb3J0IHNoYTI1NiBmcm9tICdzaGEyNTYnO1xuaW1wb3J0IGNyeXB0byBmcm9tICdjcnlwdG8nO1xuaW1wb3J0IHsgdjUgfSBmcm9tICd1dWlkJztcbmltcG9ydCAqIGFzIGxvZ2dlciBmcm9tICcuLi9sb2dnZXInO1xuXG5pbnRlcmZhY2UgRGVzdGluYXRpb25NYXAge1xuICBba2V5OiBzdHJpbmddOiBzdHJpbmc7XG59XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gSU5MSU5FUlNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuZXhwb3J0IHsgZ2V0LCBzZXQgfTtcbmV4cG9ydCBjb25zdCBpc0RlZmluZWQgPSAoeDogdW5rbm93bikgPT4gIV8uaXNVbmRlZmluZWQoeCk7XG5leHBvcnQgY29uc3QgaXNOb3RFbXB0eSA9ICh4OiB1bmtub3duKSA9PiAhXy5pc0VtcHR5KHgpO1xuZXhwb3J0IGNvbnN0IGlzTm90TnVsbCA9ICh4OiB1bmtub3duKSA9PiB4ICE9IG51bGw7XG5leHBvcnQgY29uc3QgaXNEZWZpbmVkQW5kTm90TnVsbCA9ICh4OiB1bmtub3duKSA9PiBpc0RlZmluZWQoeCkgJiYgaXNOb3ROdWxsKHgpO1xuZXhwb3J0IGNvbnN0IGlzRGVmaW5lZEFuZE5vdE51bGxBbmROb3RFbXB0eSA9ICh4OiB1bmtub3duKSA9PlxuICBpc0RlZmluZWQoeCkgJiYgaXNOb3ROdWxsKHgpICYmIGlzTm90RW1wdHkoeCk7XG5leHBvcnQgY29uc3QgaXNCbGFuayA9ICh2YWx1ZTogdW5rbm93bikgPT4gXy5pc0VtcHR5KF8udG9TdHJpbmcodmFsdWUpKTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5leHBvcnQgY29uc3QgcmVtb3ZlVW5kZWZpbmVkVmFsdWVzID0gKG9iajogdW5rbm93bikgPT4ge1xuICBpZiAoIV8uaXNPYmplY3Qob2JqKSkgcmV0dXJuIG9iajtcbiAgcmV0dXJuIF8ucGlja0J5KG9iaiwgaXNEZWZpbmVkKTtcbn07XG5cbmV4cG9ydCBjb25zdCByZW1vdmVOdWxsVmFsdWVzID0gKG9iajogdW5rbm93bikgPT4ge1xuICBpZiAoIV8uaXNPYmplY3Qob2JqKSkgcmV0dXJuIG9iajtcbiAgcmV0dXJuIF8ucGlja0J5KG9iaiwgaXNOb3ROdWxsKTtcbn07XG5leHBvcnQgY29uc3QgcmVtb3ZlVW5kZWZpbmVkQW5kTnVsbFZhbHVlcyA9IChvYmo6IHVua25vd24pID0+IHtcbiAgaWYgKCFfLmlzT2JqZWN0KG9iaikpIHJldHVybiBvYmo7XG4gIHJldHVybiBfLnBpY2tCeShvYmosIGlzRGVmaW5lZEFuZE5vdE51bGwpO1xufTtcbmV4cG9ydCBjb25zdCByZW1vdmVVbmRlZmluZWRBbmROdWxsQW5kRW1wdHlWYWx1ZXMgPSAob2JqOiB1bmtub3duKSA9PiB7XG4gIGlmICghXy5pc09iamVjdChvYmopKSByZXR1cm4gb2JqO1xuICByZXR1cm4gXy5waWNrQnkob2JqLCBpc0RlZmluZWRBbmROb3ROdWxsQW5kTm90RW1wdHkpO1xufTtcblxuLyoqXG4gKiBUaGUgZmxhdHRlbk1hcCBmdW5jdGlvbiB0YWtlcyBhIGNvbGxlY3Rpb24gYXMgaW5wdXQgYW5kIHJldHVybnMgYSBmbGF0dGVuZWQgdmVyc2lvbiBvZiB0aGUgY29sbGVjdGlvbi4gSWYgdGhlIGlucHV0IGlzIG5vdCBhbiBhcnJheSBvciBhbiBvYmplY3QsIGl0IGlzIHJldHVybmVkIGFzIGlzLiBPdGhlcndpc2UsIHRoZSBmdW5jdGlvbiB1c2VzIHRoZSBmbGF0TWFwIGZ1bmN0aW9uIGZyb20gdGhlIGxvZGFzaCBsaWJyYXJ5IHRvIGZsYXR0ZW4gdGhlIGNvbGxlY3Rpb24uXG4gKiBAcGFyYW0gY29sbGVjdGlvblxuICogQHJldHVybnNcbiAqL1xuZXhwb3J0IGNvbnN0IGZsYXR0ZW5NYXAgPSAoY29sbGVjdGlvbjogdW5rbm93bikgPT4ge1xuICBpZiAoIV8uaXNBcnJheShjb2xsZWN0aW9uKSAmJiAhXy5pc09iamVjdChjb2xsZWN0aW9uKSkgcmV0dXJuIGNvbGxlY3Rpb247XG4gIHJldHVybiBfLmZsYXRNYXAoY29sbGVjdGlvbiwgKHgpID0+IHgpO1xufTtcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBHRU5FUklDIFVUTElUWVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCBjb25zdCBiYXNlNjRDb252ZXJ0b3IgPSAoc3RyaW5nOiBzdHJpbmcpID0+IEJ1ZmZlci5mcm9tKHN0cmluZykudG9TdHJpbmcoJ2Jhc2U2NCcpO1xuXG4vLyByZXR1cm4gYSB2YWxpZCBVUkwgb2JqZWN0IGlmIGNvcnJlY3QgZWxzZSBudWxsXG5leHBvcnQgY29uc3QgaXNWYWxpZFVybCA9ICh1cmw6IHN0cmluZyB8IFVSTCkgPT4ge1xuICB0cnkge1xuICAgIHJldHVybiBuZXcgVVJMKHVybCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHJldHVybiBudWxsO1xuICB9XG59O1xuXG5leHBvcnQgY29uc3Qgc3RyaXBUcmFpbGluZ1NsYXNoID0gKHN0cjogc3RyaW5nKSA9PlxuICBzdHIgJiYgc3RyLmVuZHNXaXRoKCcvJykgPyBzdHIuc2xpY2UoMCwgLTEpIDogc3RyO1xuXG5leHBvcnQgY29uc3QgaXNQcmltaXRpdmUgPSAoYXJnOiB1bmtub3duKSA9PiB7XG4gIGNvbnN0IHR5cGUgPSB0eXBlb2YgYXJnO1xuICByZXR1cm4gYXJnID09IG51bGwgfHwgKHR5cGUgIT09ICdvYmplY3QnICYmIHR5cGUgIT09ICdmdW5jdGlvbicpO1xufTtcblxuLyoqXG4gKlxuICogQHBhcmFtIHsqfSBhcmdcbiAqIEByZXR1cm5zIHt0eXBlfVxuICpcbiAqIFJldHVybnMgdHlwZSBvZiBwYXNzZWQgYXJnXG4gKiBmb3IgbnVsbCBhcmdzcyByZXR1cm5zIFwiTlVMTFwiIGluc3RlZCBvZiBcIm9iamVjdFwiXG4gKlxuICovXG5leHBvcnQgY29uc3QgZ2V0VHlwZSA9IChhcmc6IHVua25vd24pID0+IHtcbiAgY29uc3QgdHlwZSA9IHR5cGVvZiBhcmc7XG4gIGlmIChhcmcgPT0gbnVsbCkge1xuICAgIHJldHVybiAnTlVMTCc7XG4gIH1cbiAgaWYgKEFycmF5LmlzQXJyYXkoYXJnKSkge1xuICAgIHJldHVybiAnYXJyYXknO1xuICB9XG4gIHJldHVybiB0eXBlO1xufTtcblxuZXhwb3J0IGNvbnN0IGlzT2JqZWN0ID0gKHZhbHVlOiB1bmtub3duKSA9PiB7XG4gIGNvbnN0IHR5cGUgPSB0eXBlb2YgdmFsdWU7XG4gIHJldHVybiB2YWx1ZSAhPSBudWxsICYmICh0eXBlID09PSAnb2JqZWN0JyB8fCB0eXBlID09PSAnZnVuY3Rpb24nKSAmJiAhQXJyYXkuaXNBcnJheSh2YWx1ZSk7XG59O1xuXG5leHBvcnQgY29uc3QgaXNFbXB0eSA9IChpbnB1dDogdW5rbm93bikgPT4gXy5pc0VtcHR5KF8udG9TdHJpbmcoaW5wdXQpLnRyaW0oKSk7XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGZvciBlbXB0eSBvYmplY3Qge31cbiAqIEBwYXJhbSB7Kn0gb2JqXG4gKiBAcmV0dXJuc1xuICovXG5leHBvcnQgY29uc3QgaXNFbXB0eU9iamVjdCA9IChvYmo6IGFueSkgPT4ge1xuICBpZiAoIW9iaikge1xuICAgIGxvZ2dlci53YXJuKCdpbnB1dCBpcyB1bmRlZmluZWQgb3IgbnVsbCcpO1xuICAgIHJldHVybiB0cnVlO1xuICB9XG4gIHJldHVybiBPYmplY3Qua2V5cyhvYmopLmxlbmd0aCA9PT0gMDtcbn07XG5cbi8qKlxuICogRnVuY3Rpb24gdG8gY2hlY2sgaWYgdmFsdWUgaXMgRGVmaW5lZCwgTm90IG51bGwgYW5kIE5vdCBFbXB0eS5cbiAqIENyZWF0ZSB0aGlzIGZ1bmN0aW9uLCBCZWNhdXNlIGV4aXN0aW5nIGlzRGVmaW5lZEFuZE5vdE51bGxBbmROb3RFbXB0eSgxMjMpIGlzIHJldHVybmluZyBmYWxzZSBkdWUgdG8gbG9kYXNoIF8uaXNFbXB0eSBmdW5jdGlvbi5cbiAqIF8uaXNFbXB0eSBpcyB1c2VkIHRvIGRldGVjdCBlbXB0eSBjb2xsZWN0aW9ucy9vYmplY3RzIGFuZCBpdCB3aWxsIHJldHVybiB0cnVlIGZvciBJbnRlZ2VyLCBCb29sZWFuIHZhbHVlcy5cbiAqIHJlZjogaHR0cHM6Ly9naXRodWIuY29tL2xvZGFzaC9sb2Rhc2gvaXNzdWVzLzQ5NlxuICogQHBhcmFtIHsqfSB2YWx1ZSAxMjNcbiAqIEByZXR1cm5zIHllc1xuICovXG5leHBvcnQgY29uc3QgaXNEZWZpbmVkTm90TnVsbE5vdEVtcHR5ID0gKHZhbHVlOiB1bmtub3duKSA9PlxuICAhKFxuICAgIHZhbHVlID09PSB1bmRlZmluZWQgfHxcbiAgICB2YWx1ZSA9PT0gbnVsbCB8fFxuICAgIE51bWJlci5pc05hTih2YWx1ZSkgfHxcbiAgICAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiBPYmplY3Qua2V5cyh2YWx1ZSkubGVuZ3RoID09PSAwKSB8fFxuICAgICh0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnICYmIHZhbHVlLnRyaW0oKS5sZW5ndGggPT09IDApXG4gICk7XG5cbmV4cG9ydCBjb25zdCByZW1vdmVVbmRlZmluZWROdWxsRW1wdHlFeGNsQm9vbEludCA9IChvYmo6IHVua25vd24pID0+IHtcbiAgaWYgKCFfLmlzT2JqZWN0KG9iaikpIHJldHVybiBvYmo7XG4gIHJldHVybiBfLnBpY2tCeShvYmosIGlzRGVmaW5lZE5vdE51bGxOb3RFbXB0eSk7XG59O1xuXG4vLyBGb3JtYXQgdGhlIGRlc3RpbmF0aW9uLkNvbmZpZy5keW5hbWljTWFwIGFycmF5cyB0byBoYXNoTWFwXG5leHBvcnQgY29uc3QgZ2V0SGFzaEZyb21BcnJheSA9IChcbiAgYXJyYXlzOiBBcnJheTxEZXN0aW5hdGlvbk1hcD4sXG4gIGZyb21LZXk6IHN0cmluZyA9ICdmcm9tJyxcbiAgdG9LZXk6IHN0cmluZyA9ICd0bycsXG4gIGlzTG93ZXJDYXNlID0gdHJ1ZSxcbikgPT4ge1xuICBjb25zdCBoYXNoTWFwOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG4gIGlmIChBcnJheS5pc0FycmF5KGFycmF5cykpIHtcbiAgICBhcnJheXMuZm9yRWFjaCgoYXJyYXkpID0+IHtcbiAgICAgIGlmIChpc0VtcHR5KGFycmF5W2Zyb21LZXldKSkgcmV0dXJuO1xuICAgICAgaGFzaE1hcFtpc0xvd2VyQ2FzZSA/IGFycmF5W2Zyb21LZXldLnRvU3RyaW5nKCkudG9Mb3dlckNhc2UoKSA6IGFycmF5W2Zyb21LZXldXSA9XG4gICAgICAgIGFycmF5W3RvS2V5XTtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gaGFzaE1hcDtcbn07XG5cbi8qKlxuICogRm9ybWF0IHRoZSBkZXN0aW5hdGlvbi5Db25maWcuZHluYW1pY01hcCBhcnJheXMgdG8gaGFzaE1hcFxuICogd2hlcmUgdmFsdWUgaXMgYW4gYXJyYXlcbiAqIEBwYXJhbSAge30gYXJyYXlzIFt7XCJmcm9tXCI6XCJwcm9wMVwiLFwidG9cIjpcInZhbDFcIn0se1wiZnJvbVwiOlwicHJvcDFcIixcInRvXCI6XCJ2YWwyXCJ9LHtcImZyb21cIjpcInByb3AyXCIsXCJ0b1wiOlwidmFsMlwifV1cbiAqIEBwYXJhbSAge30gZnJvbUtleT1cImZyb21cIlxuICogQHBhcmFtICB7fSB0b0tleT1cInRvXCJcbiAqIEBwYXJhbSAge30gaXNMb3dlckNhc2U9dHJ1ZVxuICogQHBhcmFtICB7fSByZXR1cm4gaGFzaG1hcCB7XCJwcm9wMVwiOltcInZhbDFcIixcInZhbDJcIl0sXCJwcm9wMlwiOltcInZhbDJcIl19XG4gKi9cbmV4cG9ydCBjb25zdCBnZXRIYXNoRnJvbUFycmF5V2l0aER1cGxpY2F0ZSA9IChcbiAgYXJyYXlzOiBBcnJheTxEZXN0aW5hdGlvbk1hcD4sXG4gIGZyb21LZXkgPSAnZnJvbScgYXMgc3RyaW5nLFxuICB0b0tleSA9ICd0bycsXG4gIGlzTG93ZXJDYXNlID0gdHJ1ZSxcbikgPT4ge1xuICBjb25zdCBoYXNoTWFwOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG4gIGlmIChBcnJheS5pc0FycmF5KGFycmF5cykpIHtcbiAgICBhcnJheXMuZm9yRWFjaCgoYXJyYXkpID0+IHtcbiAgICAgIGlmIChpc0VtcHR5KGFycmF5W2Zyb21LZXldKSkgcmV0dXJuO1xuICAgICAgY29uc3Qga2V5ID0gaXNMb3dlckNhc2VcbiAgICAgICAgPyBhcnJheVtmcm9tS2V5XS50b1N0cmluZygpLnRvTG93ZXJDYXNlKCkudHJpbSgpXG4gICAgICAgIDogYXJyYXlbZnJvbUtleV0udG9TdHJpbmcoKS50cmltKCk7XG5cbiAgICAgIGlmIChoYXNoTWFwW2tleV0pIHtcbiAgICAgICAgaGFzaE1hcFtrZXldLmFkZChhcnJheVt0b0tleV0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaGFzaE1hcFtrZXldID0gbmV3IFNldCgpO1xuICAgICAgICBoYXNoTWFwW2tleV0uYWRkKGFycmF5W3RvS2V5XSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGhhc2hNYXA7XG59O1xuXG4vKipcbiAqIEZvcm1hdCB0aGUgYXJyYXlzIHRvIGhhc2hNYXAgd2l0aCBrZXkgYXMgYGZyb21LZXlgIGFuZCB2YWx1ZSBhcyBPYmplY3RcbiAqIEBwYXJhbSB7Kn0gYXJyYXlzIFt7XCJpZFwiOlwiYTBiOGVmZTEtYzgyOC00YzYzLTg4NTAtMGQwNzQyODg4ZjlkXCIsXCJuYW1lXCI6XCJFbWFpbFwiLFwidHlwZVwiOlwiZW1haWxcIixcInR5cGVfY29uZmlnXCI6e30sXCJkYXRlX2NyZWF0ZWRcIjpcIjE2NjIyMjU4NDAyODRcIixcImhpZGVfZnJvbV9ndWVzdHNcIjpmYWxzZSxcInJlcXVpcmVkXCI6ZmFsc2V9XVxuICogQHBhcmFtIHsqfSBmcm9tS2V5IG5hbWVcbiAqIEBwYXJhbSB7Kn0gaXNMb3dlckNhc2UgZmFsc2VcbiAqIEByZXR1cm5zIC8vIHtcIkVtYWlsXCI6e1wiaWRcIjpcImEwYjhlZmUxLWM4MjgtNGM2My04ODUwLTBkMDc0Mjg4OGY5ZFwiLFwibmFtZVwiOlwiRW1haWxcIixcInR5cGVcIjpcImVtYWlsXCIsXCJ0eXBlX2NvbmZpZ1wiOnt9LFwiZGF0ZV9jcmVhdGVkXCI6XCIxNjYyMjI1ODQwMjg0XCIsXCJoaWRlX2Zyb21fZ3Vlc3RzXCI6ZmFsc2UsXCJyZXF1aXJlZFwiOmZhbHNlfX1cbiAqL1xuZXhwb3J0IGNvbnN0IGdldEhhc2hGcm9tQXJyYXlXaXRoVmFsdWVBc09iamVjdCA9IChcbiAgYXJyYXlzOiBBcnJheTxEZXN0aW5hdGlvbk1hcD4sXG4gIGZyb21LZXkgPSAnZnJvbScsXG4gIGlzTG93ZXJDYXNlID0gdHJ1ZSxcbikgPT4ge1xuICBjb25zdCBoYXNoTWFwOiBSZWNvcmQ8c3RyaW5nLCBhbnk+ID0ge307XG4gIGlmIChBcnJheS5pc0FycmF5KGFycmF5cykpIHtcbiAgICBhcnJheXMuZm9yRWFjaCgoYXJyYXkpID0+IHtcbiAgICAgIGlmIChpc0VtcHR5KGFycmF5W2Zyb21LZXldKSkgcmV0dXJuO1xuICAgICAgaGFzaE1hcFtpc0xvd2VyQ2FzZSA/IGFycmF5W2Zyb21LZXldLnRvTG93ZXJDYXNlKCkgOiBhcnJheVtmcm9tS2V5XV0gPSBhcnJheTtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gaGFzaE1hcDtcbn07XG5cbi8qKlxuICogUmV0cmlldmVzIGEgdmFsdWUgZnJvbSBhIG1lc3NhZ2Ugb2JqZWN0IGJhc2VkIG9uIGEgZ2l2ZW4ga2V5LiBJdCBzZWFyY2hlcyBmb3IgdGhlIGtleSBpbiB0aGUgcHJvcGVydGllcywgdHJhaXRzLCBhbmQgY29udGV4dC50cmFpdHMgb2YgdGhlIG1lc3NhZ2Ugb2JqZWN0IGluIHRoYXQgb3JkZXIuXG4gKlxuICogQHBhcmFtIHtPYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucyBvYmplY3QuXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucy5tZXNzYWdlIC0gVGhlIG1lc3NhZ2Ugb2JqZWN0IGNvbnRhaW5pbmcgcHJvcGVydGllcywgdHJhaXRzLCBhbmQgY29udGV4dC50cmFpdHMuXG4gKiBAcGFyYW0ge3N0cmluZ30gb3B0aW9ucy5rZXkgLSBUaGUga2V5IHRvIHNlYXJjaCBmb3IgaW4gdGhlIG1lc3NhZ2Ugb2JqZWN0LlxuICogQHJldHVybnMgeyp9IFRoZSB2YWx1ZSBmb3VuZCBpbiB0aGUgbWVzc2FnZSBvYmplY3QgZm9yIHRoZSBnaXZlbiBrZXksIG9yIG51bGwgaWYgdGhlIGtleSBpcyBub3QgZm91bmQgb3IgdGhlIHZhbHVlIGlzIG51bGwuXG4gKi9cbmV4cG9ydCBjb25zdCBnZXRWYWx1ZUZyb21Qcm9wZXJ0aWVzT3JUcmFpdHMgPSAoeyBtZXNzYWdlLCBrZXkgfTogeyBtZXNzYWdlOiBhbnk7IGtleTogYW55IH0pID0+IHtcbiAgY29uc3Qga2V5U2V0ID0gWydwcm9wZXJ0aWVzJywgJ3RyYWl0cycsICdjb250ZXh0LnRyYWl0cyddO1xuXG4gIGNvbnN0IHZhbCA9IF8uZmluZChcbiAgICBfLm1hcChrZXlTZXQsIChrKSA9PiBnZXQobWVzc2FnZSwgYCR7a30uJHtrZXl9YCkpLFxuICAgICh2KSA9PiAhXy5pc05pbCh2KSxcbiAgKTtcbiAgcmV0dXJuICFfLmlzTmlsKHZhbCkgPyB2YWwgOiBudWxsO1xufTtcblxuLyoqXG4gKiBGbGF0dGVucyBhIEpTT04gb2JqZWN0IGludG8gYSBzaW5nbGUtbGV2ZWwgb2JqZWN0LlxuICpcbiAqIEBwYXJhbSBkYXRhIC0gVGhlIEpTT04gb2JqZWN0IHRvIGJlIGZsYXR0ZW5lZC5cbiAqIEBwYXJhbSBzZXBhcmF0b3IgLSBUaGUgY2hhcmFjdGVyIHVzZWQgdG8gc2VwYXJhdGUgdGhlIGtleXMgaW4gdGhlIGZsYXR0ZW5lZCBvYmplY3QuIERlZmF1bHQgaXMgJy4nLlxuICogQHBhcmFtIG1vZGUgLSBUaGUgbW9kZSB1c2VkIHRvIGZsYXR0ZW4gYXJyYXlzLiBJZiBzZXQgdG8gJ3N0cmljdCcsIHRoZSBhcnJheSBpbmRpY2VzIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIGZsYXR0ZW5lZCBrZXlzLiBEZWZhdWx0IGlzICdub3JtYWwnLlxuICogQHJldHVybnMgVGhlIGZsYXR0ZW5lZCBKU09OIG9iamVjdC5cbiAqL1xuZXhwb3J0IGNvbnN0IGZsYXR0ZW5Kc29uID0gKGRhdGE6IHVua25vd24sIHNlcGFyYXRvciA9ICcuJywgbW9kZSA9ICdub3JtYWwnKSA9PiB7XG4gIGNvbnN0IHJlc3VsdDogUmVjb3JkPGFueSwgYW55PiA9IHt9O1xuXG4gIC8vIGEgcmVjdXJzaXZlIGZ1bmN0aW9uIHRvIGxvb3AgdGhyb3VnaCB0aGUgYXJyYXkgb2YgdGhlIGRhdGFcbiAgY29uc3QgcmVjdXJzZSA9IChjdXI6IGFueSwgcHJvcDogYW55KSA9PiB7XG4gICAgbGV0IGk7XG4gICAgaWYgKE9iamVjdChjdXIpICE9PSBjdXIpIHtcbiAgICAgIHJlc3VsdFtwcm9wXSA9IGN1cjtcbiAgICB9IGVsc2UgaWYgKEFycmF5LmlzQXJyYXkoY3VyKSkge1xuICAgICAgZm9yIChpID0gMDsgaSA8IGN1ci5sZW5ndGg7IGkgKz0gMSkge1xuICAgICAgICBpZiAobW9kZSA9PT0gJ3N0cmljdCcpIHtcbiAgICAgICAgICByZWN1cnNlKGN1cltpXSwgYCR7cHJvcH0ke3NlcGFyYXRvcn0ke2l9YCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmVjdXJzZShjdXJbaV0sIGAke3Byb3B9WyR7aX1dYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmIChjdXIubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHJlc3VsdFtwcm9wXSA9IFtdO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgaXNFbXB0eUZsYWcgPSB0cnVlO1xuICAgICAgT2JqZWN0LmtleXMoY3VyKS5mb3JFYWNoKChrZXkpID0+IHtcbiAgICAgICAgaXNFbXB0eUZsYWcgPSBmYWxzZTtcbiAgICAgICAgcmVjdXJzZShjdXJba2V5XSwgcHJvcCA/IGAke3Byb3B9JHtzZXBhcmF0b3J9JHtrZXl9YCA6IGtleSk7XG4gICAgICB9KTtcbiAgICAgIGlmIChpc0VtcHR5RmxhZyAmJiBwcm9wKSByZXN1bHRbcHJvcF0gPSB7fTtcbiAgICB9XG4gIH07XG5cbiAgcmVjdXJzZShkYXRhLCAnJyk7XG4gIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIEdldCB0aGUgb2Zmc2V0IHZhbHVlIGluIHNlY29uZHMgZm9yIGEgZ2l2ZW4gdGltZXpvbmUuXG4gKlxuICogQHBhcmFtIHZhbHVlIC0gVGhlIHRpbWV6b25lIHZhbHVlIGZvciB3aGljaCB0aGUgb2Zmc2V0IG5lZWRzIHRvIGJlIGNhbGN1bGF0ZWQuXG4gKiBAcmV0dXJucyBUaGUgb2Zmc2V0IHZhbHVlIGluIHNlY29uZHMgZm9yIHRoZSBnaXZlbiB0aW1lem9uZS4gSWYgdGhlIHRpbWV6b25lIGlzIGludmFsaWQgb3Igbm90IGZvdW5kLCBpdCByZXR1cm5zIGB1bmRlZmluZWRgLlxuICpcbiAqIEBleGFtcGxlXG4gKiBjb25zdCBvZmZzZXQgPSBnZXRPZmZzZXRJblNlYygnQXNpYS9DYWxjdXR0YScpO1xuICogY29uc29sZS5sb2cob2Zmc2V0KTsgLy8gT3V0cHV0OiAxOTgwMFxuICovXG5leHBvcnQgY29uc3QgZ2V0T2Zmc2V0SW5TZWMgPSAodmFsdWU6IHN0cmluZykgPT4ge1xuICBjb25zdCBuYW1lID0gbW9tZW50LnR6LnpvbmUodmFsdWUpO1xuICBpZiAobmFtZSkge1xuICAgIGNvbnN0IHggPSBtb21lbnQoKS50eih2YWx1ZSkuZm9ybWF0KCdaJyk7XG4gICAgY29uc3Qgc3BsaXQgPSB4LnNwbGl0KCc6Jyk7XG4gICAgY29uc3QgaG91ciA9IE51bWJlcihzcGxpdFswXSk7XG4gICAgY29uc3QgbWluID0gTnVtYmVyKHNwbGl0WzFdKTtcbiAgICBsZXQgc2VjID0gMDtcbiAgICBzZWMgPSBob3VyIDwgMCA/IC0xICogKGhvdXIgKiAtNjAgKiA2MCArIG1pbiAqIDYwKSA6IGhvdXIgKiA2MCAqIDYwICsgbWluICogNjA7XG4gICAgcmV0dXJuIHNlYztcbiAgfVxuICByZXR1cm4gdW5kZWZpbmVkO1xufTtcblxuLyoqXG4gKiBDb252ZXJ0cyBhIGRhdGUgc3RyaW5nIGludG8gYSBmb3JtYXR0ZWQgdGltZXN0YW1wLlxuICogSWYgYSBmb3JtYXQgc3RyaW5nIGlzIHByb3ZpZGVkLCBpdCB1c2VzIHRoZSBtb21lbnQgbGlicmFyeSB0byBmb3JtYXQgdGhlIGRhdGUuXG4gKiBJZiBubyBmb3JtYXQgc3RyaW5nIGlzIHByb3ZpZGVkLCBpdCByZXR1cm5zIHRoZSB0aW1lc3RhbXAgb2YgdGhlIGRhdGUuXG4gKiBAcGFyYW0gZGF0ZVN0ciAtIFRoZSBkYXRlIHN0cmluZyB0byBiZSBmb3JtYXR0ZWQuXG4gKiBAcGFyYW0gZm9ybWF0IC0gKE9wdGlvbmFsKSBUaGUgZm9ybWF0IHN0cmluZyB0byBiZSB1c2VkIGZvciBmb3JtYXR0aW5nIHRoZSBkYXRlLlxuICogQHJldHVybnMgVGhlIGZvcm1hdHRlZCBkYXRlIGFzIGEgc3RyaW5nIGlmIGEgZm9ybWF0IHN0cmluZyBpcyBwcm92aWRlZCxcbiAqICAgICAgICAgIG90aGVyd2lzZSB0aGUgdGltZXN0YW1wIG9mIHRoZSBkYXRlIGFzIGEgbnVtYmVyLlxuICovXG5leHBvcnQgY29uc3QgZm9ybWF0VGltZVN0YW1wID0gKGRhdGVTdHI6IHN0cmluZywgZm9ybWF0Pzogc3RyaW5nKSA9PiB7XG4gIGNvbn