PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-coin-hbar/dist/src/lib
Просмотр файла: utils.js
"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.toUint8Array = exports.toHex = void 0;
exports.isValidAddress = isValidAddress;
exports.isValidTransactionId = isValidTransactionId;
exports.isValidPublicKey = isValidPublicKey;
exports.isNodeEnvironment = isNodeEnvironment;
exports.getCurrentTime = getCurrentTime;
exports.isValidTimeString = isValidTimeString;
exports.isValidAmount = isValidAmount;
exports.isValidRawTransactionFormat = isValidRawTransactionFormat;
exports.stringifyAccountId = stringifyAccountId;
exports.stringifyTokenId = stringifyTokenId;
exports.stringifyTxTime = stringifyTxTime;
exports.removePrefix = removePrefix;
exports.isValidMemo = isValidMemo;
exports.createRawKey = createRawKey;
exports.convertFromStellarPub = convertFromStellarPub;
exports.isSameBaseAddress = isSameBaseAddress;
exports.getBaseAddress = getBaseAddress;
exports.getAddressDetails = getAddressDetails;
exports.normalizeAddress = normalizeAddress;
exports.isValidAddressWithPaymentId = isValidAddressWithPaymentId;
exports.buildHederaTokenID = buildHederaTokenID;
exports.buildHederaAccountID = buildHederaAccountID;
exports.isValidHederaTokenID = isValidHederaTokenID;
exports.getHederaTokenIdFromName = getHederaTokenIdFromName;
exports.getHederaTokenNameFromId = getHederaTokenNameFromId;
exports.isTokenTransfer = isTokenTransfer;
exports.validateStartTime = validateStartTime;
exports.normalizeStarttime = normalizeStarttime;
exports.sleep = sleep;
exports.shouldBroadcastNow = shouldBroadcastNow;
const _ = __importStar(require("lodash"));
const sdk_1 = require("@hashgraph/sdk");
const proto_1 = require("@hashgraph/proto");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const stellar = __importStar(require("stellar-sdk"));
const url_1 = __importDefault(require("url"));
const sdk_core_1 = require("@bitgo/sdk-core");
Object.defineProperty(exports, "toHex", { enumerable: true, get: function () { return sdk_core_1.toHex; } });
Object.defineProperty(exports, "toUint8Array", { enumerable: true, get: function () { return sdk_core_1.toUint8Array; } });
const statics_1 = require("@bitgo/statics");
const MAX_TINYBARS_AMOUNT = new bignumber_js_1.default(2).pow(63).minus(1);
/**
* Returns whether the string is a valid Hedera account address
*
* In any form, `shard` and `realm` are assumed to be 0 if not provided.
*
* @param {string} address - The address to be validated
* @returns {boolean} - The validation result
*/
function isValidAddress(address) {
const addressArray = address.split('?memoId=');
if (_.isEmpty(address) ||
![1, 2].includes(addressArray.length) ||
!addressArray[0].match(/^\d+(?:(?=\.)(\.\d+){2}|(?!\.))$/) ||
(addressArray[1] && !isValidMemo(addressArray[1]))) {
return false;
}
try {
const acc = sdk_1.AccountId.fromString(addressArray[0]);
return !_.isNaN(acc.num);
}
catch (e) {
return false;
}
}
/**
* Returns whether the string is a valid Hedera transaction id
*
* @param {string} txId - The transaction id to be validated
* @returns {boolean} - The validation result
*/
function isValidTransactionId(txId) {
if (_.isEmpty(txId)) {
return false;
}
try {
const tx = sdk_1.TransactionId.fromString(txId);
if (_.isNil(tx.accountId)) {
return false;
}
return !_.isNaN(tx.accountId.num);
}
catch (e) {
return false;
}
}
/**
Returns whether the string is a valid Hedera public key
*
* @param {string} key - The public key to be validated
* @returns {boolean} - The validation result
*/
function isValidPublicKey(key) {
if (_.isEmpty(key)) {
return false;
}
try {
const pubKey = sdk_1.PublicKey.fromString(key.toLowerCase());
return !_.isNaN(pubKey.toString());
}
catch (e) {
return false;
}
}
/**
* Checks whether nodeJS.process exist and if a node version is defined to determine if this is an nodeJS environment
*
* @returns {boolean} - The validation result
*/
function isNodeEnvironment() {
return typeof process !== 'undefined' && typeof process.versions.node !== 'undefined';
}
/**
* Calculate the current time with nanoseconds precision
*
* @returns {string} - The current time in seconds
*/
function getCurrentTime() {
if (isNodeEnvironment()) {
const nanos = process.hrtime()[1];
const seconds = (Date.now() * 1000000 + nanos) / 1000000000;
return seconds.toFixed(9);
}
else {
return (performance.timeOrigin + performance.now()).toFixed(9);
}
}
/**
* Returns whether the string is a valid timestamp
*
* Nanoseconds are optional and can be passed after a dot, for example: 1595374723.356981689
*
* @param {string} time - The timestamp to be validated
* @returns {boolean} - The validation result
*/
function isValidTimeString(time) {
return /^\d+(\.\d+)?$/.test(time);
}
/**
* Returns whether the string is a valid amount number
*
* @param {string} amount - The string to validate
* @returns {boolean} - The validation result
*/
function isValidAmount(amount) {
const bigNumberAmount = new bignumber_js_1.default(amount);
return (bigNumberAmount.isInteger() &&
bigNumberAmount.isGreaterThanOrEqualTo(0) &&
bigNumberAmount.isLessThanOrEqualTo(MAX_TINYBARS_AMOUNT));
}
/**
* Returns whether the provided raw transaction accommodates to bitgo's preferred format
*
* @param {any} rawTransaction - The raw transaction to be checked
* @returns {boolean} - The validation result
*/
function isValidRawTransactionFormat(rawTransaction) {
const isAlphaNumeric = typeof rawTransaction === 'string' && /^[\da-fA-F]+$/.test(rawTransaction);
const isValidBuffer = Buffer.isBuffer(rawTransaction) && !!Uint8Array.from(rawTransaction);
return isAlphaNumeric || isValidBuffer;
}
/**
* Returns a string representation of an {proto.IAccountID} object
*
* @param {proto.IAccountID} accountId - Account id to be cast to string
* @returns {string} - The string representation of the {proto.IAccountID}
*/
function stringifyAccountId({ shardNum, realmNum, accountNum }) {
return `${shardNum || 0}.${realmNum || 0}.${accountNum}`;
}
/**
* Returns a string representation of an {proto.ITokenID} object
*
* @param {proto.ITokenID} - token id to be cast to string
* @returns {string} - the string representation of the {proto.ITokenID}
*/
function stringifyTokenId({ shardNum, realmNum, tokenNum }) {
return `${shardNum || 0}.${realmNum || 0}.${tokenNum}`;
}
/**
* Returns a string representation of an {proto.ITimestamp} object
*
* @param {proto.ITimestamp} timestamp - Timestamp to be cast to string
* @returns {string} - The string representation of the {proto.ITimestamp}
*/
function stringifyTxTime({ seconds, nanos }) {
return `${seconds}.${nanos}`;
}
/**
* Remove the specified prefix from a string only if it starts with that prefix
*
* @param {string} prefix - The prefix to be removed
* @param {string} key - The original string, usually a private or public key
* @returns {string} - The string without prefix
*/
function removePrefix(prefix, key) {
if (key.startsWith(prefix)) {
return key.slice(prefix.length);
}
return key;
}
/**
* Check if this is a valid memo
*
* @param {string} memo
* @returns {boolean}
*/
function isValidMemo(memo) {
return !(_.isEmpty(memo) || Buffer.from(memo).length > 100);
}
/**
* Uses the native hashgraph SDK function to get a raw key.
*
* @param {string} prv - Private key
* @returns {PrivateKey}
*/
function createRawKey(prv) {
return sdk_1.PrivateKey.fromString(prv);
}
/**
* Converts a stellar public key to ed25519 hex format
*
* @param {string} stellarPub
* @returns {string}
*/
function convertFromStellarPub(stellarPub) {
if (!stellar.StrKey.isValidEd25519PublicKey(stellarPub)) {
throw new Error('Not a valid stellar pub.');
}
const rawKey = stellar.StrKey.decodeEd25519PublicKey(stellarPub);
return rawKey.toString('hex');
}
/**
* Checks if two addresses have the same base address
*
* @param {String} address
* @param {String} baseAddress
* @returns {boolean}
*/
function isSameBaseAddress(address, baseAddress) {
if (!isValidAddressWithPaymentId(address)) {
throw new sdk_core_1.UtilsError(`invalid address: ${address}`);
}
return getBaseAddress(address) === getBaseAddress(baseAddress);
}
/**
* Returns the base address portion of an address
*
* @param {String} address
* @returns {String} - the base address
*/
function getBaseAddress(address) {
const addressDetails = getAddressDetails(address);
return addressDetails.address;
}
/**
* Process address into address and memo id
*
* @param {string} rawAddress
* @returns {AddressDetails} - object containing address and memo id
*/
function getAddressDetails(rawAddress) {
const addressDetails = url_1.default.parse(rawAddress);
const queryDetails = addressDetails.query ? new URLSearchParams(addressDetails.query) : undefined;
const baseAddress = addressDetails.pathname;
if (!isValidAddress(baseAddress)) {
throw new sdk_core_1.UtilsError(`invalid address: ${rawAddress}`);
}
// address doesn't have a memo id or memoId is empty
if (baseAddress === rawAddress) {
return {
address: rawAddress,
memoId: undefined,
};
}
if (!queryDetails || _.isNil(queryDetails.get('memoId'))) {
// if there are more properties, the query details need to contain the memo id property
throw new sdk_core_1.UtilsError(`invalid address with memo id: ${rawAddress}`);
}
const memoId = queryDetails.get('memoId');
if (!isValidMemo(memoId)) {
throw new sdk_core_1.UtilsError(`invalid address: '${rawAddress}', memoId is not valid`);
}
return {
address: baseAddress,
memoId,
};
}
/**
* Validate and return address with appended memo id
*
* @param {AddressDetails} addressDetails - Address which to append memo id
* @returns {string} - Address with appended memo id
*/
function normalizeAddress({ address, memoId }) {
if (memoId && isValidMemo(memoId)) {
return `${address}?memoId=${memoId}`;
}
return address;
}
/**
* Return boolean indicating whether input is a valid address with memo id
*
* @param {string} address - Address in the form <address>?memoId=<memoId>
* @returns {boolean} - True if input is a valid address
*/
function isValidAddressWithPaymentId(address) {
try {
const addressDetails = getAddressDetails(address);
return address === normalizeAddress(addressDetails);
}
catch (e) {
return false;
}
}
/**
* Build hedera {proto.TokenID} object from token ID string
*
* @param {string} tokenID - The token ID to build
* @returns {proto.TokenID} - The resulting proto TokenID object
*/
function buildHederaTokenID(tokenID) {
const tokenData = sdk_1.TokenId.fromString(tokenID);
return new proto_1.proto.TokenID({
tokenNum: tokenData.num,
realmNum: tokenData.realm,
shardNum: tokenData.shard,
});
}
/**
* Build hedera {proto.AccountID} object from account ID string
*
* @param {string} accountID - The account ID to build
* @returns {proto} - The resulting proto AccountID object
*/
function buildHederaAccountID(accountID) {
const accountId = sdk_1.AccountId.fromString(accountID);
return new proto_1.proto.AccountID({
shardNum: accountId.shard,
realmNum: accountId.realm,
accountNum: accountId.num,
});
}
/**
* Check if Hedera token ID is valid and supported
*
* @param {string} tokenId - The token ID to validate
* @returns {boolean} - True if tokenId is valid and supported
*/
function isValidHederaTokenID(tokenId) {
const isFormatValid = !_.isEmpty(tokenId) && !!tokenId.match(/^\d+(?:(?=\.)(\.\d+){2}|(?!\.))$/);
const isTokenSupported = getHederaTokenNameFromId(tokenId) !== undefined;
return isFormatValid && isTokenSupported;
}
/**
* Get the associated hedera token ID from token name, if supported
*
* @param {string} tokenName - The hedera token name
* @returns {boolean} - The associated token ID or undefined if not supported
*/
function getHederaTokenIdFromName(tokenName) {
if (statics_1.coins.has(tokenName)) {
const token = statics_1.coins.get(tokenName);
if (token.isToken && token instanceof statics_1.HederaToken) {
return token.tokenId;
}
}
return undefined;
}
/**
* Get the associated hedera token from token ID, if supported
*
* @param tokenId - The token address
* @returns {BaseCoin} - BaseCoin object for the matching token
*/
function getHederaTokenNameFromId(tokenId) {
const tokensArray = statics_1.coins
.filter((coin) => {
return coin instanceof statics_1.HederaToken && coin.tokenId === tokenId;
})
.map((token) => token); // flatten coin map to array
return tokensArray.length > 0 ? tokensArray[0] : undefined;
}
/**
* Return boolean indicating whether input is a valid token transfer transaction
*
* @param {proto.ICryptoTransferTransactionBody | null} transferTxBody is a transfer transaction body
* @returns {boolean} true is input is a valid token transfer transaction
*/
function isTokenTransfer(transferTxBody) {
return !!transferTxBody && !!transferTxBody.tokenTransfers && transferTxBody.tokenTransfers.length > 0;
}
/** validates a startTime string to be a valid timestamp and in the future
* @param {string} startTime - The startTime to be validated
* @throws {Error} - if startTime is not a valid timestamp or is in the past
* @returns {void}
* */
function validateStartTime(startTime) {
if (!isValidTimeString(startTime)) {
throw new Error('invalid startTime, got: ' + startTime);
}
const currentTime = getCurrentTime();
const startTimeFixed = normalizeStarttime(startTime);
const result = new bignumber_js_1.default(startTimeFixed).isLessThanOrEqualTo(currentTime);
if (result) {
throw new Error('startTime must be a future timestamp, got: ' + startTime);
}
}
function normalizeStarttime(startTime) {
return new bignumber_js_1.default(startTime).toFixed(9);
}
/**
* Await for a given amount of time in milliseconds
* @param ms - The amount of time to wait in milliseconds
* @returns {Promise<void>} - A promise that resolves after the given amount of time
*/
function sleep(ms) {
console.log(`sleeping for ${ms} ms`);
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Check if the startTime is within the broadcast window (5 seconds after and 175 seconds after the startTime)
*/
function shouldBroadcastNow(startTime) {
const startTimeFixed = normalizeStarttime(startTime);
const currentTime = getCurrentTime();
// startTime plus 5 seconds
const startingTimeWindow = new bignumber_js_1.default(startTimeFixed).plus(5).toFixed(9);
// startTime plus 170 seconds
const endingTimeWindow = new bignumber_js_1.default(startTimeFixed).plus(175).toFixed(9);
if (new bignumber_js_1.default(currentTime).isGreaterThan(endingTimeWindow)) {
throw new Error('startTime window expired, got: ' +
startTimeFixed +
' - currentTime: ' +
currentTime +
' - endingTimeWindow ' +
endingTimeWindow);
}
return (new bignumber_js_1.default(currentTime).isGreaterThanOrEqualTo(startingTimeWindow) &&
new bignumber_js_1.default(currentTime).isLessThanOrEqualTo(endingTimeWindow));
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3V0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXFCQSx3Q0FrQkM7QUFRRCxvREFhQztBQVFELDRDQVVDO0FBT0QsOENBRUM7QUFPRCx3Q0FRQztBQVVELDhDQUVDO0FBUUQsc0NBT0M7QUFRRCxrRUFLQztBQVFELGdEQUVDO0FBUUQsNENBRUM7QUFRRCwwQ0FFQztBQVNELG9DQUtDO0FBUUQsa0NBRUM7QUFRRCxvQ0FFQztBQVFELHNEQU9DO0FBU0QsOENBS0M7QUFRRCx3Q0FHQztBQVFELDhDQTZCQztBQVFELDRDQUtDO0FBUUQsa0VBT0M7QUFRRCxnREFPQztBQVFELG9EQU9DO0FBUUQsb0RBS0M7QUFRRCw0REFTQztBQVFELDREQVFDO0FBUUQsMENBRUM7QUFPRCw4Q0FVQztBQUVELGdEQUVDO0FBT0Qsc0JBR0M7QUFLRCxnREF1QkM7QUF4Y0QsMENBQTRCO0FBQzVCLHdDQUEwRjtBQUMxRiw0Q0FBeUM7QUFDekMsZ0VBQXFDO0FBQ3JDLHFEQUF1QztBQUV2Qyw4Q0FBc0I7QUFDdEIsOENBQWtFO0FBRXpELHNGQUZBLGdCQUFLLE9BRUE7QUFBRSw2RkFGQSx1QkFBWSxPQUVBO0FBRDVCLDRDQUE4RDtBQUc5RCxNQUFNLG1CQUFtQixHQUFHLElBQUksc0JBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTlEOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixjQUFjLENBQUMsT0FBZTtJQUM1QyxNQUFNLFlBQVksR0FBRyxPQUFPLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBRS9DLElBQ0UsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUM7UUFDbEIsQ0FBQyxDQUFDLENBQUMsRUFB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LFdBQVcsQ0FBQyxDQUFDLG1CQUFtQixDQUFDLGdCQUFnQixDQUFDLENBQ2pFLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IHsgQWNjb3VudElkLCBQcml2YXRlS2V5LCBQdWJsaWNLZXksIFRva2VuSWQsIFRyYW5zYWN0aW9uSWQgfSBmcm9tICdAaGFzaGdyYXBoL3Nkayc7XG5pbXBvcnQgeyBwcm90byB9IGZyb20gJ0BoYXNoZ3JhcGgvcHJvdG8nO1xuaW1wb3J0IEJpZ051bWJlciBmcm9tICdiaWdudW1iZXIuanMnO1xuaW1wb3J0ICogYXMgc3RlbGxhciBmcm9tICdzdGVsbGFyLXNkayc7XG5pbXBvcnQgeyBBZGRyZXNzRGV0YWlscyB9IGZyb20gJy4vaWZhY2UnO1xuaW1wb3J0IHVybCBmcm9tICd1cmwnO1xuaW1wb3J0IHsgdG9IZXgsIHRvVWludDhBcnJheSwgVXRpbHNFcnJvciB9IGZyb20gJ0BiaXRnby9zZGstY29yZSc7XG5pbXBvcnQgeyBCYXNlQ29pbiwgY29pbnMsIEhlZGVyYVRva2VuIH0gZnJvbSAnQGJpdGdvL3N0YXRpY3MnO1xuZXhwb3J0IHsgdG9IZXgsIHRvVWludDhBcnJheSB9O1xuXG5jb25zdCBNQVhfVElOWUJBUlNfQU1PVU5UID0gbmV3IEJpZ051bWJlcigyKS5wb3coNjMpLm1pbnVzKDEpO1xuXG4vKipcbiAqIFJldHVybnMgd2hldGhlciB0aGUgc3RyaW5nIGlzIGEgdmFsaWQgSGVkZXJhIGFjY291bnQgYWRkcmVzc1xuICpcbiAqIEluIGFueSBmb3JtLCBgc2hhcmRgIGFuZCBgcmVhbG1gIGFyZSBhc3N1bWVkIHRvIGJlIDAgaWYgbm90IHByb3ZpZGVkLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBhZGRyZXNzIC0gVGhlIGFkZHJlc3MgdG8gYmUgdmFsaWRhdGVkXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgdmFsaWRhdGlvbiByZXN1bHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRBZGRyZXNzKGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBhZGRyZXNzQXJyYXkgPSBhZGRyZXNzLnNwbGl0KCc/bWVtb0lkPScpO1xuXG4gIGlmIChcbiAgICBfLmlzRW1wdHkoYWRkcmVzcykgfHxcbiAgICAhWzEsIDJdLmluY2x1ZGVzKGFkZHJlc3NBcnJheS5sZW5ndGgpIHx8XG4gICAgIWFkZHJlc3NBcnJheVswXS5tYXRjaCgvXlxcZCsoPzooPz1cXC4pKFxcLlxcZCspezJ9fCg/IVxcLikpJC8pIHx8XG4gICAgKGFkZHJlc3NBcnJheVsxXSAmJiAhaXNWYWxpZE1lbW8oYWRkcmVzc0FycmF5WzFdKSlcbiAgKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG5cbiAgdHJ5IHtcbiAgICBjb25zdCBhY2MgPSBBY2NvdW50SWQuZnJvbVN0cmluZyhhZGRyZXNzQXJyYXlbMF0pO1xuICAgIHJldHVybiAhXy5pc05hTihhY2MubnVtKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybnMgd2hldGhlciB0aGUgc3RyaW5nIGlzIGEgdmFsaWQgSGVkZXJhIHRyYW5zYWN0aW9uIGlkXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHR4SWQgLSBUaGUgdHJhbnNhY3Rpb24gaWQgdG8gYmUgdmFsaWRhdGVkXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgdmFsaWRhdGlvbiByZXN1bHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRUcmFuc2FjdGlvbklkKHR4SWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBpZiAoXy5pc0VtcHR5KHR4SWQpKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHRyeSB7XG4gICAgY29uc3QgdHggPSBUcmFuc2FjdGlvbklkLmZyb21TdHJpbmcodHhJZCk7XG4gICAgaWYgKF8uaXNOaWwodHguYWNjb3VudElkKSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICByZXR1cm4gIV8uaXNOYU4odHguYWNjb3VudElkLm51bSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gUmV0dXJucyB3aGV0aGVyIHRoZSBzdHJpbmcgaXMgYSB2YWxpZCBIZWRlcmEgcHVibGljIGtleVxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgLSBUaGUgcHVibGljIGtleSB0byBiZSB2YWxpZGF0ZWRcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRoZSB2YWxpZGF0aW9uIHJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZFB1YmxpY0tleShrZXk6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBpZiAoXy5pc0VtcHR5KGtleSkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgdHJ5IHtcbiAgICBjb25zdCBwdWJLZXkgPSBQdWJsaWNLZXkuZnJvbVN0cmluZyhrZXkudG9Mb3dlckNhc2UoKSk7XG4gICAgcmV0dXJuICFfLmlzTmFOKHB1YktleS50b1N0cmluZygpKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrcyB3aGV0aGVyIG5vZGVKUy5wcm9jZXNzIGV4aXN0IGFuZCBpZiBhIG5vZGUgdmVyc2lvbiBpcyBkZWZpbmVkIHRvIGRldGVybWluZSBpZiB0aGlzIGlzIGFuIG5vZGVKUyBlbnZpcm9ubWVudFxuICpcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRoZSB2YWxpZGF0aW9uIHJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNOb2RlRW52aXJvbm1lbnQoKTogYm9vbGVhbiB7XG4gIHJldHVybiB0eXBlb2YgcHJvY2VzcyAhPT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIHByb2Nlc3MudmVyc2lvbnMubm9kZSAhPT0gJ3VuZGVmaW5lZCc7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlIHRoZSBjdXJyZW50IHRpbWUgd2l0aCBuYW5vc2Vjb25kcyBwcmVjaXNpb25cbiAqXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIFRoZSBjdXJyZW50IHRpbWUgaW4gc2Vjb25kc1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q3VycmVudFRpbWUoKTogc3RyaW5nIHtcbiAgaWYgKGlzTm9kZUVudmlyb25tZW50KCkpIHtcbiAgICBjb25zdCBuYW5vcyA9IHByb2Nlc3MuaHJ0aW1lKClbMV07XG4gICAgY29uc3Qgc2Vjb25kcyA9IChEYXRlLm5vdygpICogMTAwMDAwMCArIG5hbm9zKSAvIDEwMDAwMDAwMDA7XG4gICAgcmV0dXJuIHNlY29uZHMudG9GaXhlZCg5KTtcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gKHBlcmZvcm1hbmNlLnRpbWVPcmlnaW4gKyBwZXJmb3JtYW5jZS5ub3coKSkudG9GaXhlZCg5KTtcbiAgfVxufVxuXG4vKipcbiAqIFJldHVybnMgd2hldGhlciB0aGUgc3RyaW5nIGlzIGEgdmFsaWQgdGltZXN0YW1wXG4gKlxuICogTmFub3NlY29uZHMgYXJlIG9wdGlvbmFsIGFuZCBjYW4gYmUgcGFzc2VkIGFmdGVyIGEgZG90LCBmb3IgZXhhbXBsZTogMTU5NTM3NDcyMy4zNTY5ODE2ODlcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdGltZSAtIFRoZSB0aW1lc3RhbXAgdG8gYmUgdmFsaWRhdGVkXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUaGUgdmFsaWRhdGlvbiByZXN1bHRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRUaW1lU3RyaW5nKHRpbWU6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gL15cXGQrKFxcLlxcZCspPyQvLnRlc3QodGltZSk7XG59XG5cbi8qKlxuICogUmV0dXJucyB3aGV0aGVyIHRoZSBzdHJpbmcgaXMgYSB2YWxpZCBhbW91bnQgbnVtYmVyXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IGFtb3VudCAtIFRoZSBzdHJpbmcgdG8gdmFsaWRhdGVcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRoZSB2YWxpZGF0aW9uIHJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZEFtb3VudChhbW91bnQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBiaWdOdW1iZXJBbW91bnQgPSBuZXcgQmlnTnVtYmVyKGFtb3VudCk7XG4gIHJldHVybiAoXG4gICAgYmlnTnVtYmVyQW1vdW50LmlzSW50ZWdlcigpICYmXG4gICAgYmlnTnVtYmVyQW1vdW50LmlzR3JlYXRlclRoYW5PckVxdWFsVG8oMCkgJiZcbiAgICBiaWdOdW1iZXJBbW91bnQuaXNMZXNzVGhhbk9yRXF1YWxUbyhNQVhfVElOWUJBUlNfQU1PVU5UKVxuICApO1xufVxuXG4vKipcbiAqIFJldHVybnMgd2hldGhlciB0aGUgcHJvdmlkZWQgcmF3IHRyYW5zYWN0aW9uIGFjY29tbW9kYXRlcyB0byBiaXRnbydzIHByZWZlcnJlZCBmb3JtYXRcbiAqXG4gKiBAcGFyYW0ge2FueX0gcmF3VHJhbnNhY3Rpb24gLSBUaGUgcmF3IHRyYW5zYWN0aW9uIHRvIGJlIGNoZWNrZWRcbiAqIEByZXR1cm5zIHtib29sZWFufSAtIFRoZSB2YWxpZGF0aW9uIHJlc3VsdFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZFJhd1RyYW5zYWN0aW9uRm9ybWF0KHJhd1RyYW5zYWN0aW9uOiBhbnkpOiBib29sZWFuIHtcbiAgY29uc3QgaXNBbHBoYU51bWVyaWMgPSB0eXBlb2YgcmF3VHJhbnNhY3Rpb24gPT09ICdzdHJpbmcnICYmIC9eW1xcZGEtZkEtRl0rJC8udGVzdChyYXdUcmFuc2FjdGlvbik7XG4gIGNvbnN0IGlzVmFsaWRCdWZmZXIgPSBCdWZmZXIuaXNCdWZmZXIocmF3VHJhbnNhY3Rpb24pICYmICEhVWludDhBcnJheS5mcm9tKHJhd1RyYW5zYWN0aW9uKTtcblxuICByZXR1cm4gaXNBbHBoYU51bWVyaWMgfHwgaXNWYWxpZEJ1ZmZlcjtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGEgc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIGFuIHtwcm90by5JQWNjb3VudElEfSBvYmplY3RcbiAqXG4gKiBAcGFyYW0ge3Byb3RvLklBY2NvdW50SUR9IGFjY291bnRJZCAtIEFjY291bnQgaWQgdG8gYmUgY2FzdCB0byBzdHJpbmdcbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gVGhlIHN0cmluZyByZXByZXNlbnRhdGlvbiBvZiB0aGUge3Byb3RvLklBY2NvdW50SUR9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzdHJpbmdpZnlBY2NvdW50SWQoeyBzaGFyZE51bSwgcmVhbG1OdW0sIGFjY291bnROdW0gfTogcHJvdG8uSUFjY291bnRJRCk6IHN0cmluZyB7XG4gIHJldHVybiBgJHtzaGFyZE51bSB8fCAwfS4ke3JlYWxtTnVtIHx8IDB9LiR7YWNjb3VudE51bX1gO1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgYW4ge3Byb3RvLklUb2tlbklEfSBvYmplY3RcbiAqXG4gKiBAcGFyYW0ge3Byb3RvLklUb2tlbklEfSAtIHRva2VuIGlkIHRvIGJlIGNhc3QgdG8gc3RyaW5nXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIHRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIHtwcm90by5JVG9rZW5JRH1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0cmluZ2lmeVRva2VuSWQoeyBzaGFyZE51bSwgcmVhbG1OdW0sIHRva2VuTnVtIH06IHByb3RvLklUb2tlbklEKTogc3RyaW5nIHtcbiAgcmV0dXJuIGAke3NoYXJkTnVtIHx8IDB9LiR7cmVhbG1OdW0gfHwgMH0uJHt0b2tlbk51bX1gO1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgYW4ge3Byb3RvLklUaW1lc3RhbXB9IG9iamVjdFxuICpcbiAqIEBwYXJhbSB7cHJvdG8uSVRpbWVzdGFtcH0gdGltZXN0YW1wIC0gVGltZXN0YW1wIHRvIGJlIGNhc3QgdG8gc3RyaW5nXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIFRoZSBzdHJpbmcgcmVwcmVzZW50YXRpb24gb2YgdGhlIHtwcm90by5JVGltZXN0YW1wfVxuICovXG5leHBvcnQgZnVuY3Rpb24gc3RyaW5naWZ5VHhUaW1lKHsgc2Vjb25kcywgbmFub3MgfTogcHJvdG8uSVRpbWVzdGFtcCk6IHN0cmluZyB7XG4gIHJldHVybiBgJHtzZWNvbmRzfS4ke25hbm9zfWA7XG59XG5cbi8qKlxuICogUmVtb3ZlIHRoZSBzcGVjaWZpZWQgcHJlZml4IGZyb20gYSBzdHJpbmcgb25seSBpZiBpdCBzdGFydHMgd2l0aCB0aGF0IHByZWZpeFxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcmVmaXggLSBUaGUgcHJlZml4IHRvIGJlIHJlbW92ZWRcbiAqIEBwYXJhbSB7c3RyaW5nfSBrZXkgLSBUaGUgb3JpZ2luYWwgc3RyaW5nLCB1c3VhbGx5IGEgcHJpdmF0ZSBvciBwdWJsaWMga2V5XG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIFRoZSBzdHJpbmcgd2l0aG91dCBwcmVmaXhcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZVByZWZpeChwcmVmaXg6IHN0cmluZywga2V5OiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoa2V5LnN0YXJ0c1dpdGgocHJlZml4KSkge1xuICAgIHJldHVybiBrZXkuc2xpY2UocHJlZml4Lmxlbmd0aCk7XG4gIH1cbiAgcmV0dXJuIGtleTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiB0aGlzIGlzIGEgdmFsaWQgbWVtb1xuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBtZW1vXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVmFsaWRNZW1vKG1lbW86IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gIShfLmlzRW1wdHkobWVtbykgfHwgQnVmZmVyLmZyb20obWVtbykubGVuZ3RoID4gMTAwKTtcbn1cblxuLyoqXG4gKiBVc2VzIHRoZSBuYXRpdmUgaGFzaGdyYXBoIFNESyBmdW5jdGlvbiB0byBnZXQgYSByYXcga2V5LlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBwcnYgLSBQcml2YXRlIGtleVxuICogQHJldHVybnMge1ByaXZhdGVLZXl9XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSYXdLZXkocHJ2OiBzdHJpbmcpOiBQcml2YXRlS2V5IHtcbiAgcmV0dXJuIFByaXZhdGVLZXkuZnJvbVN0cmluZyhwcnYpO1xufVxuXG4vKipcbiAqIENvbnZlcnRzIGEgc3RlbGxhciBwdWJsaWMga2V5IHRvIGVkMjU1MTkgaGV4IGZvcm1hdFxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBzdGVsbGFyUHViXG4gKiBAcmV0dXJucyB7c3RyaW5nfVxuICovXG5leHBvcnQgZnVuY3Rpb24gY29udmVydEZyb21TdGVsbGFyUHViKHN0ZWxsYXJQdWI6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmICghc3RlbGxhci5TdHJLZXkuaXNWYWxpZEVkMjU1MTlQdWJsaWNLZXkoc3RlbGxhclB1YikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ05vdCBhIHZhbGlkIHN0ZWxsYXIgcHViLicpO1xuICB9XG5cbiAgY29uc3QgcmF3S2V5OiBCdWZmZXIgPSBzdGVsbGFyLlN0cktleS5kZWNvZGVFZDI1NTE5UHVibGljS2V5KHN0ZWxsYXJQdWIpO1xuICByZXR1cm4gcmF3S2V5LnRvU3RyaW5nKCdoZXgnKTtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgdHdvIGFkZHJlc3NlcyBoYXZlIHRoZSBzYW1lIGJhc2UgYWRkcmVzc1xuICpcbiAqIEBwYXJhbSB7U3RyaW5nfSBhZGRyZXNzXG4gKiBAcGFyYW0ge1N0cmluZ30gYmFzZUFkZHJlc3NcbiAqIEByZXR1cm5zIHtib29sZWFufVxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNTYW1lQmFzZUFkZHJlc3MoYWRkcmVzczogc3RyaW5nLCBiYXNlQWRkcmVzczogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGlmICghaXNWYWxpZEFkZHJlc3NXaXRoUGF5bWVudElkKGFkZHJlc3MpKSB7XG4gICAgdGhyb3cgbmV3IFV0aWxzRXJyb3IoYGludmFsaWQgYWRkcmVzczogJHthZGRyZXNzfWApO1xuICB9XG4gIHJldHVybiBnZXRCYXNlQWRkcmVzcyhhZGRyZXNzKSA9PT0gZ2V0QmFzZUFkZHJlc3MoYmFzZUFkZHJlc3MpO1xufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIGJhc2UgYWRkcmVzcyBwb3J0aW9uIG9mIGFuIGFkZHJlc3NcbiAqXG4gKiBAcGFyYW0ge1N0cmluZ30gYWRkcmVzc1xuICogQHJldHVybnMge1N0cmluZ30gLSB0aGUgYmFzZSBhZGRyZXNzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRCYXNlQWRkcmVzcyhhZGRyZXNzOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBhZGRyZXNzRGV0YWlscyA9IGdldEFkZHJlc3NEZXRhaWxzKGFkZHJlc3MpO1xuICByZXR1cm4gYWRkcmVzc0RldGFpbHMuYWRkcmVzcztcbn1cblxuLyoqXG4gKiBQcm9jZXNzIGFkZHJlc3MgaW50byBhZGRyZXNzIGFuZCBtZW1vIGlkXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHJhd0FkZHJlc3NcbiAqIEByZXR1cm5zIHtBZGRyZXNzRGV0YWlsc30gLSBvYmplY3QgY29udGFpbmluZyBhZGRyZXNzIGFuZCBtZW1vIGlkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRBZGRyZXNzRGV0YWlscyhyYXdBZGRyZXNzOiBzdHJpbmcpOiBBZGRyZXNzRGV0YWlscyB7XG4gIGNvbnN0IGFkZHJlc3NEZXRhaWxzID0gdXJsLnBhcnNlKHJhd0FkZHJlc3MpO1xuICBjb25zdCBxdWVyeURldGFpbHMgPSBhZGRyZXNzRGV0YWlscy5xdWVyeSA/IG5ldyBVUkxTZWFyY2hQYXJhbXMoYWRkcmVzc0RldGFpbHMucXVlcnkpIDogdW5kZWZpbmVkO1xuICBjb25zdCBiYXNlQWRkcmVzcyA9IGFkZHJlc3NEZXRhaWxzLnBhdGhuYW1lIGFzIHN0cmluZztcbiAgaWYgKCFpc1ZhbGlkQWRkcmVzcyhiYXNlQWRkcmVzcykpIHtcbiAgICB0aHJvdyBuZXcgVXRpbHNFcnJvcihgaW52YWxpZCBhZGRyZXNzOiAke3Jhd0FkZHJlc3N9YCk7XG4gIH1cblxuICAvLyBhZGRyZXNzIGRvZXNuJ3QgaGF2ZSBhIG1lbW8gaWQgb3IgbWVtb0lkIGlzIGVtcHR5XG4gIGlmIChiYXNlQWRkcmVzcyA9PT0gcmF3QWRkcmVzcykge1xuICAgIHJldHVybiB7XG4gICAgICBhZGRyZXNzOiByYXdBZGRyZXNzLFxuICAgICAgbWVtb0lkOiB1bmRlZmluZWQsXG4gICAgfTtcbiAgfVxuXG4gIGlmICghcXVlcnlEZXRhaWxzIHx8IF8uaXNOaWwocXVlcnlEZXRhaWxzLmdldCgnbWVtb0lkJykpKSB7XG4gICAgLy8gaWYgdGhlcmUgYXJlIG1vcmUgcHJvcGVydGllcywgdGhlIHF1ZXJ5IGRldGFpbHMgbmVlZCB0byBjb250YWluIHRoZSBtZW1vIGlkIHByb3BlcnR5XG4gICAgdGhyb3cgbmV3IFV0aWxzRXJyb3IoYGludmFsaWQgYWRkcmVzcyB3aXRoIG1lbW8gaWQ6ICR7cmF3QWRkcmVzc31gKTtcbiAgfVxuICBjb25zdCBtZW1vSWQgPSBxdWVyeURldGFpbHMuZ2V0KCdtZW1vSWQnKSBhcyBzdHJpbmc7XG4gIGlmICghaXNWYWxpZE1lbW8obWVtb0lkKSkge1xuICAgIHRocm93IG5ldyBVdGlsc0Vycm9yKGBpbnZhbGlkIGFkZHJlc3M6ICcke3Jhd0FkZHJlc3N9JywgbWVtb0lkIGlzIG5vdCB2YWxpZGApO1xuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBhZGRyZXNzOiBiYXNlQWRkcmVzcyxcbiAgICBtZW1vSWQsXG4gIH07XG59XG5cbi8qKlxuICogVmFsaWRhdGUgYW5kIHJldHVybiBhZGRyZXNzIHdpdGggYXBwZW5kZWQgbWVtbyBpZFxuICpcbiAqIEBwYXJhbSB7QWRkcmVzc0RldGFpbHN9IGFkZHJlc3NEZXRhaWxzIC0gQWRkcmVzcyB3aGljaCB0byBhcHBlbmQgbWVtbyBpZFxuICogQHJldHVybnMge3N0cmluZ30gLSBBZGRyZXNzIHdpdGggYXBwZW5kZWQgbWVtbyBpZFxuICovXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplQWRkcmVzcyh7IGFkZHJlc3MsIG1lbW9JZCB9OiBBZGRyZXNzRGV0YWlscyk6IHN0cmluZyB7XG4gIGlmIChtZW1vSWQgJiYgaXNWYWxpZE1lbW8obWVtb0lkKSkge1xuICAgIHJldHVybiBgJHthZGRyZXNzfT9tZW1vSWQ9JHttZW1vSWR9YDtcbiAgfVxuICByZXR1cm4gYWRkcmVzcztcbn1cblxuLyoqXG4gKiBSZXR1cm4gYm9vbGVhbiBpbmRpY2F0aW5nIHdoZXRoZXIgaW5wdXQgaXMgYSB2YWxpZCBhZGRyZXNzIHdpdGggbWVtbyBpZFxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBhZGRyZXNzIC0gQWRkcmVzcyBpbiB0aGUgZm9ybSA8YWRkcmVzcz4/bWVtb0lkPTxtZW1vSWQ+XG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIGlucHV0IGlzIGEgdmFsaWQgYWRkcmVzc1xuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZEFkZHJlc3NXaXRoUGF5bWVudElkKGFkZHJlc3M6IHN0cmluZyk6IGJvb2xlYW4ge1xuICB0cnkge1xuICAgIGNvbnN0IGFkZHJlc3NEZXRhaWxzID0gZ2V0QWRkcmVzc0RldGFpbHMoYWRkcmVzcyk7XG4gICAgcmV0dXJuIGFkZHJlc3MgPT09IG5vcm1hbGl6ZUFkZHJlc3MoYWRkcmVzc0RldGFpbHMpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbi8qKlxuICogQnVpbGQgaGVkZXJhIHtwcm90by5Ub2tlbklEfSBvYmplY3QgZnJvbSB0b2tlbiBJRCBzdHJpbmdcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gdG9rZW5JRCAtIFRoZSB0b2tlbiBJRCB0byBidWlsZFxuICogQHJldHVybnMge3Byb3RvLlRva2VuSUR9IC0gVGhlIHJlc3VsdGluZyBwcm90byBUb2tlbklEIG9iamVjdFxuICovXG5leHBvcnQgZnVuY3Rpb24gYnVpbGRIZWRlcmFUb2tlbklEKHRva2VuSUQ6IHN0cmluZyk6IHByb3RvLlRva2VuSUQge1xuICBjb25zdCB0b2tlbkRhdGEgPSBUb2tlbklkLmZyb21TdHJpbmcodG9rZW5JRCk7XG4gIHJldHVybiBuZXcgcHJvdG8uVG9rZW5JRCh7XG4gICAgdG9rZW5OdW06IHRva2VuRGF0YS5udW0sXG4gICAgcmVhbG1OdW06IHRva2VuRGF0YS5yZWFsbSxcbiAgICBzaGFyZE51bTogdG9rZW5EYXRhLnNoYXJkLFxuICB9KTtcbn1cblxuLyoqXG4gKiBCdWlsZCBoZWRlcmEge3Byb3RvLkFjY291bnRJRH0gb2JqZWN0IGZyb20gYWNjb3VudCBJRCBzdHJpbmdcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElEIC0gVGhlIGFjY291bnQgSUQgdG8gYnVpbGRcbiAqIEByZXR1cm5zIHtwcm90b30gLSBUaGUgcmVzdWx0aW5nIHByb3RvIEFjY291bnRJRCBvYmplY3RcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkSGVkZXJhQWNjb3VudElEKGFjY291bnRJRDogc3RyaW5nKTogcHJvdG8uQWNjb3VudElEIHtcbiAgY29uc3QgYWNjb3VudElkID0gQWNjb3VudElkLmZyb21TdHJpbmcoYWNjb3VudElEKTtcbiAgcmV0dXJuIG5ldyBwcm90by5BY2NvdW50SUQoe1xuICAgIHNoYXJkTnVtOiBhY2NvdW50SWQuc2hhcmQsXG4gICAgcmVhbG1OdW06IGFjY291bnRJZC5yZWFsbSxcbiAgICBhY2NvdW50TnVtOiBhY2NvdW50SWQubnVtLFxuICB9KTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiBIZWRlcmEgdG9rZW4gSUQgaXMgdmFsaWQgYW5kIHN1cHBvcnRlZFxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSB0b2tlbklkIC0gVGhlIHRva2VuIElEIHRvIHZhbGlkYXRlXG4gKiBAcmV0dXJucyB7Ym9vbGVhbn0gLSBUcnVlIGlmIHRva2VuSWQgaXMgdmFsaWQgYW5kIHN1cHBvcnRlZFxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNWYWxpZEhlZGVyYVRva2VuSUQodG9rZW5JZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IGlzRm9ybWF0VmFsaWQgPSAhXy5pc0VtcHR5KHRva2VuSWQpICYmICEhdG9rZW5JZC5tYXRjaCgvXlxcZCsoPzooPz1cXC4pKFxcLlxcZCspezJ9fCg/IVxcLikpJC8pO1xuICBjb25zdCBpc1Rva2VuU3VwcG9ydGVkID0gZ2V0SGVkZXJhVG9rZW5OYW1lRnJvbUlkKHRva2VuSWQpICE9PSB1bmRlZmluZWQ7XG5cbiAgcmV0dXJuIGlzRm9ybWF0VmFsaWQgJiYgaXNUb2tlblN1cHBvcnRlZDtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIGFzc29jaWF0ZWQgaGVkZXJhIHRva2VuIElEIGZyb20gdG9rZW4gbmFtZSwgaWYgc3VwcG9ydGVkXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHRva2VuTmFtZSAtIFRoZSBoZWRlcmEgdG9rZW4gbmFtZVxuICogQHJldHVybnMge2Jvb2xlYW59IC0gVGhlIGFzc29jaWF0ZWQgdG9rZW4gSUQgb3IgdW5kZWZpbmVkIGlmIG5vdCBzdXBwb3J0ZWRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEhlZGVyYVRva2VuSWRGcm9tTmFtZSh0b2tlbk5hbWU6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmIChjb2lucy5oYXModG9rZW5OYW1lKSkge1xuICAgIGNvbnN0IHRva2VuID0gY29pbnMuZ2V0KHRva2VuTmFtZSk7XG4gICAgaWYgKHRva2VuLmlzVG9rZW4gJiYgdG9rZW4gaW5zdGFuY2VvZiBIZWRlcmFUb2tlbikge1xuICAgICAgcmV0dXJuIHRva2VuLnRva2VuSWQ7XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHVuZGVmaW5lZDtcbn1cblxuLyoqXG4gKiBHZXQgdGhlIGFzc29jaWF0ZWQgaGVkZXJhIHRva2VuIGZyb20gdG9rZW4gSUQsIGlmIHN1cHBvcnRlZFxuICpcbiAqIEBwYXJhbSB0b2tlbklkIC0gVGhlIHRva2VuIGFkZHJlc3NcbiAqIEByZXR1cm5zIHtCYXNlQ29pbn0gLSBCYXNlQ29pbiBvYmplY3QgZm9yIHRoZSBtYXRjaGluZyB0b2tlblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0SGVkZXJhVG9rZW5OYW1lRnJvbUlkKHRva2VuSWQ6IHN0cmluZyk6IFJlYWRvbmx5PEJhc2VDb2luPiB8IHVuZGVmaW5lZCB7XG4gIGNvbnN0IHRva2Vuc0FycmF5ID0gY29pbnNcbiAgICAuZmlsdGVyKChjb2luKSA9PiB7XG4gICAgICByZXR1cm4gY29pbiBpbnN0YW5jZW9mIEhlZGVyYVRva2VuICYmIGNvaW4udG9rZW5JZCA9PT0gdG9rZW5JZDtcbiAgICB9KVxuICAgIC5tYXAoKHRva2VuKSA9PiB0b2tlbik7IC8vIGZsYXR0ZW4gY29pbiBtYXAgdG8gYXJyYXlcblxuICByZXR1cm4gdG9rZW5zQXJyYXkubGVuZ3RoID4gMCA/IHRva2Vuc0FycmF5WzBdIDogdW5kZWZpbmVkO1xufVxuXG4vKipcbiAqIFJldHVybiBib29sZWFuIGluZGljYXRpbmcgd2hldGhlciBpbnB1dCBpcyBhIHZhbGlkIHRva2VuIHRyYW5zZmVyIHRyYW5zYWN0aW9uXG4gKlxuICogQHBhcmFtIHtwcm90by5JQ3J5cHRvVHJhbnNmZXJUcmFuc2FjdGlvbkJvZHkgfCBudWxsfSB0cmFuc2ZlclR4Qm9keSBpcyBhIHRyYW5zZmVyIHRyYW5zYWN0aW9uIGJvZHlcbiAqIEByZXR1cm5zIHtib29sZWFufSB0cnVlIGlzIGlucHV0IGlzIGEgdmFsaWQgdG9rZW4gdHJhbnNmZXIgdHJhbnNhY3Rpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzVG9rZW5UcmFuc2Zlcih0cmFuc2ZlclR4Qm9keTogcHJvdG8uSUNyeXB0b1RyYW5zZmVyVHJhbnNhY3Rpb25Cb2R5IHwgbnVsbCk6IGJvb2xlYW4ge1xuICByZXR1cm4gISF0cmFuc2ZlclR4Qm9keSAmJiAhIXRyYW5zZmVyVHhCb2R5LnRva2VuVHJhbnNmZXJzICYmIHRyYW5zZmVyVHhCb2R5LnRva2VuVHJhbnNmZXJzLmxlbmd0aCA+IDA7XG59XG5cbi8qKiB2YWxpZGF0ZXMgYSBzdGFydFRpbWUgc3RyaW5nIHRvIGJlIGEgdmFsaWQgdGltZXN0YW1wIGFuZCBpbiB0aGUgZnV0dXJlXG4gKiBAcGFyYW0ge3N0cmluZ30gc3RhcnRUaW1lIC0gVGhlIHN0YXJ0VGltZSB0byBiZSB2YWxpZGF0ZWRcbiAqIEB0aHJvd3Mge0Vycm9yfSAtIGlmIHN0YXJ0VGltZSBpcyBub3QgYSB2YWxpZCB0aW1lc3RhbXAgb3IgaXMgaW4gdGhlIHBhc3RcbiAqIEByZXR1cm5zIHt2b2lkfVxuICogKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZVN0YXJ0VGltZShzdGFydFRpbWU6IHN0cmluZyk6IHZvaWQge1xuICBpZiAoIWlzVmFsaWRUaW1lU3RyaW5nKHN0YXJ0VGltZSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ2ludmFsaWQgc3RhcnRUaW1lLCBnb3Q6ICcgKyBzdGFydFRpbWUpO1xuICB9XG4gIGNvbnN0IGN1cnJlbnRUaW1lID0gZ2V0Q3VycmVudFRpbWUoKTtcbiAgY29uc3Qgc3RhcnRUaW1lRml4ZWQgPSBub3JtYWxpemVTdGFydHRpbWUoc3RhcnRUaW1lKTtcbiAgY29uc3QgcmVzdWx0ID0gbmV3IEJpZ051bWJlcihzdGFydFRpbWVGaXhlZCkuaXNMZXNzVGhhbk9yRXF1YWxUbyhjdXJyZW50VGltZSk7XG4gIGlmIChyZXN1bHQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3N0YXJ0VGltZSBtdXN0IGJlIGEgZnV0dXJlIHRpbWVzdGFtcCwgZ290OiAnICsgc3RhcnRUaW1lKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbm9ybWFsaXplU3RhcnR0aW1lKHN0YXJ0VGltZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgcmV0dXJuIG5ldyBCaWdOdW1iZXIoc3RhcnRUaW1lKS50b0ZpeGVkKDkpO1xufVxuXG4vKipcbiAqIEF3YWl0IGZvciBhIGdpdmVuIGFtb3VudCBvZiB0aW1lIGluIG1pbGxpc2Vjb25kc1xuICogQHBhcmFtIG1zIC0gVGhlIGFtb3VudCBvZiB0aW1lIHRvIHdhaXQgaW4gbWlsbGlzZWNvbmRzXG4gKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gLSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBhZnRlciB0aGUgZ2l2ZW4gYW1vdW50IG9mIHRpbWVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNsZWVwKG1zOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc29sZS5sb2coYHNsZWVwaW5nIGZvciAke21zfSBtc2ApO1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgbXMpKTtcbn1cblxuLyoqXG4gKiBDaGVjayBpZiB0aGUgc3RhcnRUaW1lIGlzIHdpdGhpbiB0aGUgYnJvYWRjYXN0IHdpbmRvdyAoNSBzZWNvbmRzIGFmdGVyIGFuZCAxNzUgc2Vjb25kcyBhZnRlciB0aGUgc3RhcnRUaW1lKVxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkQnJvYWRjYXN0Tm93KHN0YXJ0VGltZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gIGNvbnN0IHN0YXJ0VGltZUZpeGVkID0gbm9ybWFsaXplU3RhcnR0aW1lKHN0YXJ0VGltZSk7XG4gIGNvbnN0IGN1cnJlbnRUaW1lID0gZ2V0Q3VycmVudFRpbWUoKTtcbiAgLy8gc3RhcnRUaW1lIHBsdXMgNSBzZWNvbmRzXG4gIGNvbnN0IHN0YXJ0aW5nVGltZVdpbmRvdyA9IG5ldyBCaWdOdW1iZXIoc3RhcnRUaW1lRml4ZWQpLnBsdXMoNSkudG9GaXhlZCg5KTtcbiAgLy8gc3RhcnRUaW1lIHBsdXMgMTcwIHNlY29uZHNcbiAgY29uc3QgZW5kaW5nVGltZVdpbmRvdyA9IG5ldyBCaWdOdW1iZXIoc3RhcnRUaW1lRml4ZWQpLnBsdXMoMTc1KS50b0ZpeGVkKDkpO1xuXG4gIGlmIChuZXcgQmlnTnVtYmVyKGN1cnJlbnRUaW1lKS5pc0dyZWF0ZXJUaGFuKGVuZGluZ1RpbWVXaW5kb3cpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgJ3N0YXJ0VGltZSB3aW5kb3cgZXhwaXJlZCwgZ290OiAnICtcbiAgICAgICAgc3RhcnRUaW1lRml4ZWQgK1xuICAgICAgICAnIC0gY3VycmVudFRpbWU6ICcgK1xuICAgICAgICBjdXJyZW50VGltZSArXG4gICAgICAgICcgLSBlbmRpbmdUaW1lV2luZG93ICcgK1xuICAgICAgICBlbmRpbmdUaW1lV2luZG93XG4gICAgKTtcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgbmV3IEJpZ051bWJlcihjdXJyZW50VGltZSkuaXNHcmVhdGVyVGhhbk9yRXF1YWxUbyhzdGFydGluZ1RpbWVXaW5kb3cpICYmXG4gICAgbmV3IEJpZ051bWJlcihjdXJyZW50VGltZSkuaXNMZXNzVGhhbk9yRXF1YWxUbyhlbmRpbmdUaW1lV2luZG93KVxuICApO1xufVxuIl19Выполнить команду
Для локальной разработки. Не используйте в интернете!