PHP WebShell
Текущая директория: /opt/BitGoJS/modules/express/dist/lightning
Просмотр файла: lightningSignerRoutes.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleInitLightningWallet = handleInitLightningWallet;
exports.handleCreateSignerMacaroon = handleCreateSignerMacaroon;
exports.handleGetLightningWalletState = handleGetLightningWalletState;
exports.handleUnlockLightningWallet = handleUnlockLightningWallet;
const net_1 = require("net");
const sdk_core_1 = require("@bitgo/sdk-core");
const abstract_lightning_1 = require("@bitgo/abstract-lightning");
const utxolib = require("@bitgo/utxo-lib");
const buffer_1 = require("buffer");
const codecs_1 = require("./codecs");
const lndSignerClient_1 = require("./lndSignerClient");
const errors_1 = require("../errors");
async function createSignerMacaroon(lndSignerClient, header, watchOnlyIp) {
const { macaroon } = await lndSignerClient.bakeMacaroon({ permissions: abstract_lightning_1.signerMacaroonPermissions }, header);
const macaroonBase64 = watchOnlyIp
? (0, abstract_lightning_1.addIPCaveatToMacaroon)(buffer_1.Buffer.from(macaroon, 'hex').toString('base64'), watchOnlyIp)
: undefined;
return macaroonBase64 ? buffer_1.Buffer.from(macaroonBase64, 'base64').toString('hex') : macaroon;
}
function getSignerRootKey(passphrase, userMainnetEncryptedPrv, network, decrypt) {
const userMainnetPrv = decrypt({
password: passphrase,
input: userMainnetEncryptedPrv,
});
return utxolib.bitgo.keyutil.convertExtendedKeyNetwork(userMainnetPrv, utxolib.networks.bitcoin, network);
}
function getMacaroonRootKey(passphrase, nodeAuthEncryptedPrv, decrypt) {
const hdNode = utxolib.bip32.fromBase58(decrypt({ password: passphrase, input: nodeAuthEncryptedPrv }));
if (!hdNode.privateKey) {
throw new Error('nodeAuthEncryptedPrv is not a private key');
}
return hdNode.privateKey.toString('base64');
}
/**
* Handle the request to initialise remote signer LND for a wallet.
*/
async function handleInitLightningWallet(req) {
const bitgo = req.bitgo;
const coinName = req.params.coin;
if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
throw new errors_1.ApiResponseError(`Invalid coin ${coinName}. This is not a lightning coin.`, 400);
}
const coin = bitgo.coin(coinName);
const walletId = req.params.id;
if (typeof walletId !== 'string') {
throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
}
const { passphrase, expressHost } = (0, sdk_core_1.decodeOrElse)(codecs_1.InitLightningWalletRequest.name, codecs_1.InitLightningWalletRequest, req.body, (_) => {
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
throw new errors_1.ApiResponseError('Invalid request body to initialize lightning wallet', 400);
});
const wallet = await coin.wallets().get({ id: walletId, includeBalance: false });
if (wallet.subType() !== 'lightningSelfCustody') {
throw new errors_1.ApiResponseError(`not a self custodial lighting wallet ${walletId}`, 400);
}
const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
const userKey = await (0, abstract_lightning_1.getLightningKeychain)(wallet);
const userKeyEncryptedPrv = userKey.encryptedPrv;
if (!userKeyEncryptedPrv) {
throw new errors_1.ApiResponseError('Missing encryptedPrv in user keychain', 400);
}
const { nodeAuthKey } = await (0, abstract_lightning_1.getLightningAuthKeychains)(wallet);
const nodeAuthKeyEncryptedPrv = nodeAuthKey.encryptedPrv;
if (!nodeAuthKeyEncryptedPrv) {
throw new errors_1.ApiResponseError('Missing encryptedPrv in node auth keychain', 400);
}
const network = (0, abstract_lightning_1.getUtxolibNetwork)(coin.getChain());
const signerRootKey = getSignerRootKey(passphrase, userKeyEncryptedPrv, network, bitgo.decrypt);
const macaroonRootKey = getMacaroonRootKey(passphrase, nodeAuthKeyEncryptedPrv, bitgo.decrypt);
const { admin_macaroon: adminMacaroon } = await lndSignerClient.initWallet({
// The passphrase at LND can only accommodate a base64 character set
// For more information, see BTC-1851
wallet_password: buffer_1.Buffer.from(passphrase).toString('base64'),
extended_master_key: signerRootKey,
macaroon_root_key: macaroonRootKey,
});
return await (0, abstract_lightning_1.updateWalletCoinSpecific)(wallet, {
signerAdminMacaroon: expressHost && !!(0, net_1.isIP)(expressHost) ? (0, abstract_lightning_1.addIPCaveatToMacaroon)(adminMacaroon, expressHost) : adminMacaroon,
watchOnlyAccounts: (0, abstract_lightning_1.createWatchOnly)(signerRootKey, network),
passphrase,
});
}
/**
* Handle the request to create a signer macaroon from remote signer LND for a wallet.
*/
async function handleCreateSignerMacaroon(req) {
const bitgo = req.bitgo;
const coinName = req.params.coin;
if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
throw new errors_1.ApiResponseError(`Invalid coin to create signer macaroon: ${coinName}. Must be a lightning coin.`, 400);
}
const coin = bitgo.coin(coinName);
const walletId = req.params.id;
if (typeof walletId !== 'string') {
throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
}
const { passphrase, addIpCaveatToMacaroon } = (0, sdk_core_1.decodeOrElse)(codecs_1.CreateSignerMacaroonRequest.name, codecs_1.CreateSignerMacaroonRequest, req.body, (_) => {
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
throw new errors_1.ApiResponseError('Invalid request body to create signer macaroon', 400);
});
const wallet = await coin.wallets().get({ id: walletId, includeBalance: false });
if (wallet.subType() !== 'lightningSelfCustody') {
throw new errors_1.ApiResponseError(`not a self custodial lighting wallet ${walletId}`, 400);
}
const watchOnlyIp = wallet.coinSpecific()?.watchOnlyExternalIp;
if (!watchOnlyIp && addIpCaveatToMacaroon) {
throw new errors_1.ApiResponseError('Cannot create signer macaroon because the external IP is not set. This can take some time. Contact support@bitgo.com if longer than 24 hours.', 400);
}
if (watchOnlyIp && !(0, net_1.isIP)(watchOnlyIp)) {
throw new errors_1.ApiResponseError(`Invalid IP address: ${watchOnlyIp}. Contact support@bitgo.com`, 500);
}
const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
const encryptedSignerAdminMacaroon = wallet.coinSpecific()?.encryptedSignerAdminMacaroon;
if (!encryptedSignerAdminMacaroon) {
throw new errors_1.ApiResponseError('Missing encryptedSignerAdminMacaroon in wallet', 400);
}
const adminMacaroon = bitgo.decrypt({
password: passphrase,
input: encryptedSignerAdminMacaroon,
});
const signerMacaroon = await createSignerMacaroon(lndSignerClient, { adminMacaroonHex: buffer_1.Buffer.from(adminMacaroon, 'base64').toString('hex') }, addIpCaveatToMacaroon ? watchOnlyIp : null);
return await (0, abstract_lightning_1.updateWalletCoinSpecific)(wallet, {
signerMacaroon,
passphrase,
});
}
/**
* Handle the request to get the state of a wallet from the signer.
*/
async function handleGetLightningWalletState(req) {
const coinName = req.params.coin;
if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
throw new errors_1.ApiResponseError(`Invalid coin to get lightning wallet state: ${coinName}`, 400);
}
const walletId = req.params.id;
if (typeof walletId !== 'string') {
throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
}
const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
return await lndSignerClient.getWalletState();
}
/**
* Handle the request to unlock a wallet in the signer.
*/
async function handleUnlockLightningWallet(req) {
const coinName = req.params.coin;
if (!(0, abstract_lightning_1.isLightningCoinName)(coinName)) {
throw new errors_1.ApiResponseError(`Invalid coin to unlock lightning wallet: ${coinName}`, 400);
}
const walletId = req.params.id;
if (typeof walletId !== 'string') {
throw new errors_1.ApiResponseError(`Invalid wallet id: ${walletId}`, 400);
}
const { passphrase } = (0, sdk_core_1.decodeOrElse)(codecs_1.UnlockLightningWalletRequest.name, codecs_1.UnlockLightningWalletRequest, req.body, (_) => {
// DON'T throw errors from decodeOrElse. It could leak sensitive information.
throw new errors_1.ApiResponseError('Invalid request body to unlock lightning wallet', 400);
});
const lndSignerClient = await lndSignerClient_1.LndSignerClient.create(walletId, req.config);
// The passphrase at LND can only accommodate a base64 character set
// For more information, see BTC-1851
await lndSignerClient.unlockWallet({
wallet_password: buffer_1.Buffer.from(passphrase).toString('base64'),
});
return { message: 'ok' };
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlnaHRuaW5nU2lnbmVyUm91dGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xpZ2h0bmluZy9saWdodG5pbmdTaWduZXJSb3V0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUErREEsOERBeURDO0FBS0QsZ0VBMkRDO0FBS0Qsc0VBWUM7QUFLRCxrRUEyQkM7QUF6T0QsNkJBQTJCO0FBRTNCLDhDQUErQztBQUMvQyxrRUFTbUM7QUFDbkMsMkNBQTJDO0FBQzNDLG1DQUFnQztBQUVoQyxxQ0FLa0I7QUFDbEIsdURBQW9EO0FBQ3BELHNDQUE2QztBQUk3QyxLQUFLLFVBQVUsb0JBQW9CLENBQ2pDLGVBQWdDLEVBQ2hDLE1BQW9DLEVBQ3BDLFdBQXNDO0lBRXRDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxNQUFNLGVBQWUsQ0FBQyxZQUFZLENBQUMsRUFBRSxXQUFXLEVBQUUsOENBQXlCLEVBQUUsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUM1RyxNQUFNLGNBQWMsR0FBRyxXQUFXO1FBQ2hDLENBQUMsQ0FBQyxJQUFBLDBDQUFxQixFQUFDLGVBQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsRUFBRSxXQUFXLENBQUM7UUFDckYsQ0FBQyxDQUFDLFNBQVMsQ0FBQztJQUNkLE9BQU8sY0FBYyxDQUFDLENBQUMsQ0FBQyxlQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztBQUMzRixDQUFDO0FBRUQsU0FBUyxnQkFBZ0IsQ0FDdkIsVUFBa0IsRUFDbEIsdUJBQStCLEVBQy9CLE9BQXdCLEVBQ3hCLE9BQWdCO0lBRWhCLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQztRQUM3QixRQUFRLEVBQUUsVUFBVTtRQUNwQixLQUFLLEVBQUUsdUJBQXVCO0tBQy9CLENBQUMsQ0FBQztJQUNILE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMseUJBQXlCLENBQUMsY0FBYyxFQUFFLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQzVHLENBQUM7QUFFRCxTQUFTLGtCQUFrQixDQUFDLFVBQWtCLEVBQUUsb0JBQTRCLEVBQUUsT0FBZ0I7SUFDNUYsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsQ0FBQyxDQUFDLENBQUM7SUFDeEcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUN2QixNQUFNLElBQUksS0FBSyxDQUFDLDJDQUEyQyxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7QUFDOUMsQ0FBQztBQUVEOztHQUVHO0FBQ0ksS0FBSyxVQUFVLHlCQUF5QixDQUFDLEdBQW9CO0lBQ2xFLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUM7SUFDeEIsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7SUFDakMsSUFBSSxDQUFDLElBQUEsd0NBQW1CLEVBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUkseUJBQWdCLENBQUMsZ0JBQWdCLFFBQVEsaUNBQWlDLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDN0YsQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFFbEMsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7SUFDL0IsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUNqQyxNQUFNLElBQUkseUJBQWdCLENBQUMsc0JBQXNCLFFBQVEsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFFRCxNQUFNLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxHQUFHLElBQUEsdUJBQVksRUFDOUMsbUNBQTBCLENBQUMsSUFBSSxFQUMvQixtQ0FBMEIsRUFDMUIsR0FBRyxDQUFDLElBQUksRUFDUixDQUFDLENBQUMsRUFBRSxFQUFFO1FBQ0osNkVBQTZFO1FBQzdFLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyxxREFBcUQsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUN6RixDQUFDLENBQ0YsQ0FBQztJQUVGLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRSxRQUFRLEVBQUUsY0FBYyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDakYsSUFBSSxNQUFNLENBQUMsT0FBTyxFQUFFLEtBQUssc0JBQXNCLEVBQUUsQ0FBQztRQUNoRCxNQUFNLElBQUkseUJBQWdCLENBQUMsd0NBQXdDLFFBQVEsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFDRCxNQUFNLGVBQWUsR0FBRyxNQUFNLGlDQUFlLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFFM0UsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFBLHlDQUFvQixFQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ25ELE1BQU0sbUJBQW1CLEdBQUcsT0FBTyxDQUFDLFlBQVksQ0FBQztJQUNqRCxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUN6QixNQUFNLElBQUkseUJBQWdCLENBQUMsdUNBQXVDLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUNELE1BQU0sRUFBRSxXQUFXLEVBQUUsR0FBRyxNQUFNLElBQUEsOENBQXlCLEVBQUMsTUFBTSxDQUFDLENBQUM7SUFDaEUsTUFBTSx1QkFBdUIsR0FBRyxXQUFXLENBQUMsWUFBWSxDQUFDO0lBQ3pELElBQUksQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1FBQzdCLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyw0Q0FBNEMsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNoRixDQUFDO0lBQ0QsTUFBTSxPQUFPLEdBQUcsSUFBQSxzQ0FBaUIsRUFBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUNuRCxNQUFNLGFBQWEsR0FBRyxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNoRyxNQUFNLGVBQWUsR0FBRyxrQkFBa0IsQ0FBQyxVQUFVLEVBQUUsdUJBQXVCLEVBQUUsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRS9GLE1BQU0sRUFBRSxjQUFjLEVBQUUsYUFBYSxFQUFFLEdBQUcsTUFBTSxlQUFlLENBQUMsVUFBVSxDQUFDO1FBQ3pFLG9FQUFvRTtRQUNwRSxxQ0FBcUM7UUFDckMsZUFBZSxFQUFFLGVBQU0sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQztRQUMzRCxtQkFBbUIsRUFBRSxhQUFhO1FBQ2xDLGlCQUFpQixFQUFFLGVBQWU7S0FDbkMsQ0FBQyxDQUFDO0lBRUgsT0FBTyxNQUFNLElBQUEsNkNBQXdCLEVBQUMsTUFBTSxFQUFFO1FBQzVDLG1CQUFtQixFQUNqQixXQUFXLElBQUksQ0FBQyxDQUFDLElBQUEsVUFBSSxFQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFBLDBDQUFxQixFQUFDLGFBQWEsRUFBRSxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsYUFBYTtRQUN4RyxpQkFBaUIsRUFBRSxJQUFBLG9DQUFlLEVBQUMsYUFBYSxFQUFFLE9BQU8sQ0FBQztRQUMxRCxVQUFVO0tBQ1gsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0ksS0FBSyxVQUFVLDBCQUEwQixDQUFDLEdBQW9CO0lBQ25FLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUM7SUFDeEIsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7SUFDakMsSUFBSSxDQUFDLElBQUEsd0NBQW1CLEVBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUkseUJBQWdCLENBQUMsMkNBQTJDLFFBQVEsNkJBQTZCLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDcEgsQ0FBQztJQUNELE1BQU0sSUFBSSxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDbEMsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7SUFDL0IsSUFBSSxPQUFPLFFBQVEsS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUNqQyxNQUFNLElBQUkseUJBQWdCLENBQUMsc0JBQXNCLFFBQVEsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7SUFFRCxNQUFNLEVBQUUsVUFBVSxFQUFFLHFCQUFxQixFQUFFLEdBQUcsSUFBQSx1QkFBWSxFQUN4RCxvQ0FBMkIsQ0FBQyxJQUFJLEVBQ2hDLG9DQUEyQixFQUMzQixHQUFHLENBQUMsSUFBSSxFQUNSLENBQUMsQ0FBQyxFQUFFLEVBQUU7UUFDSiw2RUFBNkU7UUFDN0UsTUFBTSxJQUFJLHlCQUFnQixDQUFDLGdEQUFnRCxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ3BGLENBQUMsQ0FDRixDQUFDO0lBRUYsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztJQUNqRixJQUFJLE1BQU0sQ0FBQyxPQUFPLEVBQUUsS0FBSyxzQkFBc0IsRUFBRSxDQUFDO1FBQ2hELE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyx3Q0FBd0MsUUFBUSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDdEYsQ0FBQztJQUNELE1BQU0sV0FBVyxHQUFHLE1BQU0sQ0FBQyxZQUFZLEVBQUUsRUFBRSxtQkFBbUIsQ0FBQztJQUMvRCxJQUFJLENBQUMsV0FBVyxJQUFJLHFCQUFxQixFQUFFLENBQUM7UUFDMUMsTUFBTSxJQUFJLHlCQUFnQixDQUN4QiwrSUFBK0ksRUFDL0ksR0FBRyxDQUNKLENBQUM7SUFDSixDQUFDO0lBRUQsSUFBSSxXQUFXLElBQUksQ0FBQyxJQUFBLFVBQUksRUFBQyxXQUFXLENBQUMsRUFBRSxDQUFDO1FBQ3RDLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyx1QkFBdUIsV0FBVyw2QkFBNkIsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNuRyxDQUFDO0lBRUQsTUFBTSxlQUFlLEdBQUcsTUFBTSxpQ0FBZSxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRTNFLE1BQU0sNEJBQTRCLEdBQUcsTUFBTSxDQUFDLFlBQVksRUFBRSxFQUFFLDRCQUE0QixDQUFDO0lBQ3pGLElBQUksQ0FBQyw0QkFBNEIsRUFBRSxDQUFDO1FBQ2xDLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyxnREFBZ0QsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNwRixDQUFDO0lBQ0QsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLE9BQU8sQ0FBQztRQUNsQyxRQUFRLEVBQUUsVUFBVTtRQUNwQixLQUFLLEVBQUUsNEJBQTRCO0tBQ3BDLENBQUMsQ0FBQztJQUVILE1BQU0sY0FBYyxHQUFHLE1BQU0sb0JBQW9CLENBQy9DLGVBQWUsRUFDZixFQUFFLGdCQUFnQixFQUFFLGVBQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUMxRSxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzNDLENBQUM7SUFFRixPQUFPLE1BQU0sSUFBQSw2Q0FBd0IsRUFBQyxNQUFNLEVBQUU7UUFDNUMsY0FBYztRQUNkLFVBQVU7S0FDWCxDQUFDLENBQUM7QUFDTCxDQUFDO0FBRUQ7O0dBRUc7QUFDSSxLQUFLLFVBQVUsNkJBQTZCLENBQUMsR0FBb0I7SUFDdEUsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7SUFDakMsSUFBSSxDQUFDLElBQUEsd0NBQW1CLEVBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztRQUNuQyxNQUFNLElBQUkseUJBQWdCLENBQUMsK0NBQStDLFFBQVEsRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQzdGLENBQUM7SUFDRCxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztJQUMvQixJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO1FBQ2pDLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyxzQkFBc0IsUUFBUSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDcEUsQ0FBQztJQUVELE1BQU0sZUFBZSxHQUFHLE1BQU0saUNBQWUsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzRSxPQUFPLE1BQU0sZUFBZSxDQUFDLGNBQWMsRUFBRSxDQUFDO0FBQ2hELENBQUM7QUFFRDs7R0FFRztBQUNJLEtBQUssVUFBVSwyQkFBMkIsQ0FBQyxHQUFvQjtJQUNwRSxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztJQUNqQyxJQUFJLENBQUMsSUFBQSx3Q0FBbUIsRUFBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ25DLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyw0Q0FBNEMsUUFBUSxFQUFFLEVBQUUsR0FBRyxDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUNELE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO0lBQy9CLElBQUksT0FBTyxRQUFRLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDakMsTUFBTSxJQUFJLHlCQUFnQixDQUFDLHNCQUFzQixRQUFRLEVBQUUsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNwRSxDQUFDO0lBRUQsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUEsdUJBQVksRUFDakMscUNBQTRCLENBQUMsSUFBSSxFQUNqQyxxQ0FBNEIsRUFDNUIsR0FBRyxDQUFDLElBQUksRUFDUixDQUFDLENBQUMsRUFBRSxFQUFFO1FBQ0osNkVBQTZFO1FBQzdFLE1BQU0sSUFBSSx5QkFBZ0IsQ0FBQyxpREFBaUQsRUFBRSxHQUFHLENBQUMsQ0FBQztJQUNyRixDQUFDLENBQ0YsQ0FBQztJQUVGLE1BQU0sZUFBZSxHQUFHLE1BQU0saUNBQWUsQ0FBQyxNQUFNLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztJQUMzRSxvRUFBb0U7SUFDcEUscUNBQXFDO0lBQ3JDLE1BQU0sZUFBZSxDQUFDLFlBQVksQ0FBQztRQUNqQyxlQUFlLEVBQUUsZUFBTSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO0tBQzVELENBQUMsQ0FBQztJQUNILE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFDM0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGlzSVAgfSBmcm9tICduZXQnO1xuaW1wb3J0ICogYXMgZXhwcmVzcyBmcm9tICdleHByZXNzJztcbmltcG9ydCB7IGRlY29kZU9yRWxzZSB9IGZyb20gJ0BiaXRnby9zZGstY29yZSc7XG5pbXBvcnQge1xuICBnZXRVdHhvbGliTmV0d29yayxcbiAgc2lnbmVyTWFjYXJvb25QZXJtaXNzaW9ucyxcbiAgY3JlYXRlV2F0Y2hPbmx5LFxuICBhZGRJUENhdmVhdFRvTWFjYXJvb24sXG4gIGlzTGlnaHRuaW5nQ29pbk5hbWUsXG4gIGdldExpZ2h0bmluZ0tleWNoYWluLFxuICBnZXRMaWdodG5pbmdBdXRoS2V5Y2hhaW5zLFxuICB1cGRhdGVXYWxsZXRDb2luU3BlY2lmaWMsXG59IGZyb20gJ0BiaXRnby9hYnN0cmFjdC1saWdodG5pbmcnO1xuaW1wb3J0ICogYXMgdXR4b2xpYiBmcm9tICdAYml0Z28vdXR4by1saWInO1xuaW1wb3J0IHsgQnVmZmVyIH0gZnJvbSAnYnVmZmVyJztcblxuaW1wb3J0IHtcbiAgQ3JlYXRlU2lnbmVyTWFjYXJvb25SZXF1ZXN0LFxuICBHZXRXYWxsZXRTdGF0ZVJlc3BvbnNlLFxuICBJbml0TGlnaHRuaW5nV2FsbGV0UmVxdWVzdCxcbiAgVW5sb2NrTGlnaHRuaW5nV2FsbGV0UmVxdWVzdCxcbn0gZnJvbSAnLi9jb2RlY3MnO1xuaW1wb3J0IHsgTG5kU2lnbmVyQ2xpZW50IH0gZnJvbSAnLi9sbmRTaWduZXJDbGllbnQnO1xuaW1wb3J0IHsgQXBpUmVzcG9uc2VFcnJvciB9IGZyb20gJy4uL2Vycm9ycyc7XG5cbnR5cGUgRGVjcnlwdCA9IChwYXJhbXM6IHsgaW5wdXQ6IHN0cmluZzsgcGFzc3dvcmQ6IHN0cmluZyB9KSA9PiBzdHJpbmc7XG5cbmFzeW5jIGZ1bmN0aW9uIGNyZWF0ZVNpZ25lck1hY2Fyb29uKFxuICBsbmRTaWduZXJDbGllbnQ6IExuZFNpZ25lckNsaWVudCxcbiAgaGVhZGVyOiB7IGFkbWluTWFjYXJvb25IZXg6IHN0cmluZyB9LFxuICB3YXRjaE9ubHlJcDogc3RyaW5nIHwgdW5kZWZpbmVkIHwgbnVsbFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgeyBtYWNhcm9vbiB9ID0gYXdhaXQgbG5kU2lnbmVyQ2xpZW50LmJha2VNYWNhcm9vbih7IHBlcm1pc3Npb25zOiBzaWduZXJNYWNhcm9vblBlcm1pc3Npb25zIH0sIGhlYWRlcik7XG4gIGNvbnN0IG1hY2Fyb29uQmFzZTY0ID0gd2F0Y2hPbmx5SXBcbiAgICA/IGFkZElQQ2F2ZWF0VG9NYWNhcm9vbihCdWZmZXIuZnJvbShtYWNhcm9vbiwgJ2hleCcpLnRvU3RyaW5nKCdiYXNlNjQnKSwgd2F0Y2hPbmx5SXApXG4gICAgOiB1bmRlZmluZWQ7XG4gIHJldHVybiBtYWNhcm9vbkJhc2U2NCA/IEJ1ZmZlci5mcm9tKG1hY2Fyb29uQmFzZTY0LCAnYmFzZTY0JykudG9TdHJpbmcoJ2hleCcpIDogbWFjYXJvb247XG59XG5cbmZ1bmN0aW9uIGdldFNpZ25lclJvb3RLZXkoXG4gIHBhc3NwaHJhc2U6IHN0cmluZyxcbiAgdXNlck1haW5uZXRFbmNyeXB0ZWRQcnY6IHN0cmluZyxcbiAgbmV0d29yazogdXR4b2xpYi5OZXR3b3JrLFxuICBkZWNyeXB0OiBEZWNyeXB0XG4pIHtcbiAgY29uc3QgdXNlck1haW5uZXRQcnYgPSBkZWNyeXB0KHtcbiAgICBwYXNzd29yZDogcGFzc3BocmFzZSxcbiAgICBpbnB1dDogdXNlck1haW5uZXRFbmNyeXB0ZWRQcnYsXG4gIH0pO1xuICByZXR1cm4gdXR4b2xpYi5iaXRnby5rZXl1dGlsLmNvbnZlcnRFeHRlbmRlZEtleU5ldHdvcmsodXNlck1haW5uZXRQcnYsIHV0eG9saWIubmV0d29ya3MuYml0Y29pbiwgbmV0d29yayk7XG59XG5cbmZ1bmN0aW9uIGdldE1hY2Fyb29uUm9vdEtleShwYXNzcGhyYXNlOiBzdHJpbmcsIG5vZGVBdXRoRW5jcnlwdGVkUHJ2OiBzdHJpbmcsIGRlY3J5cHQ6IERlY3J5cHQpIHtcbiAgY29uc3QgaGROb2RlID0gdXR4b2xpYi5iaXAzMi5mcm9tQmFzZTU4KGRlY3J5cHQoeyBwYXNzd29yZDogcGFzc3BocmFzZSwgaW5wdXQ6IG5vZGVBdXRoRW5jcnlwdGVkUHJ2IH0pKTtcbiAgaWYgKCFoZE5vZGUucHJpdmF0ZUtleSkge1xuICAgIHRocm93IG5ldyBFcnJvcignbm9kZUF1dGhFbmNyeXB0ZWRQcnYgaXMgbm90IGEgcHJpdmF0ZSBrZXknKTtcbiAgfVxuICByZXR1cm4gaGROb2RlLnByaXZhdGVLZXkudG9TdHJpbmcoJ2Jhc2U2NCcpO1xufVxuXG4vKipcbiAqIEhhbmRsZSB0aGUgcmVxdWVzdCB0byBpbml0aWFsaXNlIHJlbW90ZSBzaWduZXIgTE5EIGZvciBhIHdhbGxldC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZUluaXRMaWdodG5pbmdXYWxsZXQocmVxOiBleHByZXNzLlJlcXVlc3QpOiBQcm9taXNlPHVua25vd24+IHtcbiAgY29uc3QgYml0Z28gPSByZXEuYml0Z287XG4gIGNvbnN0IGNvaW5OYW1lID0gcmVxLnBhcmFtcy5jb2luO1xuICBpZiAoIWlzTGlnaHRuaW5nQ29pbk5hbWUoY29pbk5hbWUpKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgY29pbiAke2NvaW5OYW1lfS4gVGhpcyBpcyBub3QgYSBsaWdodG5pbmcgY29pbi5gLCA0MDApO1xuICB9XG4gIGNvbnN0IGNvaW4gPSBiaXRnby5jb2luKGNvaW5OYW1lKTtcblxuICBjb25zdCB3YWxsZXRJZCA9IHJlcS5wYXJhbXMuaWQ7XG4gIGlmICh0eXBlb2Ygd2FsbGV0SWQgIT09ICdzdHJpbmcnKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgd2FsbGV0IGlkOiAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cblxuICBjb25zdCB7IHBhc3NwaHJhc2UsIGV4cHJlc3NIb3N0IH0gPSBkZWNvZGVPckVsc2UoXG4gICAgSW5pdExpZ2h0bmluZ1dhbGxldFJlcXVlc3QubmFtZSxcbiAgICBJbml0TGlnaHRuaW5nV2FsbGV0UmVxdWVzdCxcbiAgICByZXEuYm9keSxcbiAgICAoXykgPT4ge1xuICAgICAgLy8gRE9OJ1QgdGhyb3cgZXJyb3JzIGZyb20gZGVjb2RlT3JFbHNlLiBJdCBjb3VsZCBsZWFrIHNlbnNpdGl2ZSBpbmZvcm1hdGlvbi5cbiAgICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKCdJbnZhbGlkIHJlcXVlc3QgYm9keSB0byBpbml0aWFsaXplIGxpZ2h0bmluZyB3YWxsZXQnLCA0MDApO1xuICAgIH1cbiAgKTtcblxuICBjb25zdCB3YWxsZXQgPSBhd2FpdCBjb2luLndhbGxldHMoKS5nZXQoeyBpZDogd2FsbGV0SWQsIGluY2x1ZGVCYWxhbmNlOiBmYWxzZSB9KTtcbiAgaWYgKHdhbGxldC5zdWJUeXBlKCkgIT09ICdsaWdodG5pbmdTZWxmQ3VzdG9keScpIHtcbiAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcihgbm90IGEgc2VsZiBjdXN0b2RpYWwgbGlnaHRpbmcgd2FsbGV0ICR7d2FsbGV0SWR9YCwgNDAwKTtcbiAgfVxuICBjb25zdCBsbmRTaWduZXJDbGllbnQgPSBhd2FpdCBMbmRTaWduZXJDbGllbnQuY3JlYXRlKHdhbGxldElkLCByZXEuY29uZmlnKTtcblxuICBjb25zdCB1c2VyS2V5ID0gYXdhaXQgZ2V0TGlnaHRuaW5nS2V5Y2hhaW4od2FsbGV0KTtcbiAgY29uc3QgdXNlcktleUVuY3J5cHRlZFBydiA9IHVzZXJLZXkuZW5jcnlwdGVkUHJ2O1xuICBpZiAoIXVzZXJLZXlFbmNyeXB0ZWRQcnYpIHtcbiAgICB0aHJvdyBuZXcgQXBpUmVzcG9uc2VFcnJvcignTWlzc2luZyBlbmNyeXB0ZWRQcnYgaW4gdXNlciBrZXljaGFpbicsIDQwMCk7XG4gIH1cbiAgY29uc3QgeyBub2RlQXV0aEtleSB9ID0gYXdhaXQgZ2V0TGlnaHRuaW5nQXV0aEtleWNoYWlucyh3YWxsZXQpO1xuICBjb25zdCBub2RlQXV0aEtleUVuY3J5cHRlZFBydiA9IG5vZGVBdXRoS2V5LmVuY3J5cHRlZFBydjtcbiAgaWYgKCFub2RlQXV0aEtleUVuY3J5cHRlZFBydikge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKCdNaXNzaW5nIGVuY3J5cHRlZFBydiBpbiBub2RlIGF1dGgga2V5Y2hhaW4nLCA0MDApO1xuICB9XG4gIGNvbnN0IG5ldHdvcmsgPSBnZXRVdHhvbGliTmV0d29yayhjb2luLmdldENoYWluKCkpO1xuICBjb25zdCBzaWduZXJSb290S2V5ID0gZ2V0U2lnbmVyUm9vdEtleShwYXNzcGhyYXNlLCB1c2VyS2V5RW5jcnlwdGVkUHJ2LCBuZXR3b3JrLCBiaXRnby5kZWNyeXB0KTtcbiAgY29uc3QgbWFjYXJvb25Sb290S2V5ID0gZ2V0TWFjYXJvb25Sb290S2V5KHBhc3NwaHJhc2UsIG5vZGVBdXRoS2V5RW5jcnlwdGVkUHJ2LCBiaXRnby5kZWNyeXB0KTtcblxuICBjb25zdCB7IGFkbWluX21hY2Fyb29uOiBhZG1pbk1hY2Fyb29uIH0gPSBhd2FpdCBsbmRTaWduZXJDbGllbnQuaW5pdFdhbGxldCh7XG4gICAgLy8gVGhlIHBhc3NwaHJhc2UgYXQgTE5EIGNhbiBvbmx5IGFjY29tbW9kYXRlIGEgYmFzZTY0IGNoYXJhY3RlciBzZXRcbiAgICAvLyBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIEJUQy0xODUxXG4gICAgd2FsbGV0X3Bhc3N3b3JkOiBCdWZmZXIuZnJvbShwYXNzcGhyYXNlKS50b1N0cmluZygnYmFzZTY0JyksXG4gICAgZXh0ZW5kZWRfbWFzdGVyX2tleTogc2lnbmVyUm9vdEtleSxcbiAgICBtYWNhcm9vbl9yb290X2tleTogbWFjYXJvb25Sb290S2V5LFxuICB9KTtcblxuICByZXR1cm4gYXdhaXQgdXBkYXRlV2FsbGV0Q29pblNwZWNpZmljKHdhbGxldCwge1xuICAgIHNpZ25lckFkbWluTWFjYXJvb246XG4gICAgICBleHByZXNzSG9zdCAmJiAhIWlzSVAoZXhwcmVzc0hvc3QpID8gYWRkSVBDYXZlYXRUb01hY2Fyb29uKGFkbWluTWFjYXJvb24sIGV4cHJlc3NIb3N0KSA6IGFkbWluTWFjYXJvb24sXG4gICAgd2F0Y2hPbmx5QWNjb3VudHM6IGNyZWF0ZVdhdGNoT25seShzaWduZXJSb290S2V5LCBuZXR3b3JrKSxcbiAgICBwYXNzcGhyYXNlLFxuICB9KTtcbn1cblxuLyoqXG4gKiBIYW5kbGUgdGhlIHJlcXVlc3QgdG8gY3JlYXRlIGEgc2lnbmVyIG1hY2Fyb29uIGZyb20gcmVtb3RlIHNpZ25lciBMTkQgZm9yIGEgd2FsbGV0LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaGFuZGxlQ3JlYXRlU2lnbmVyTWFjYXJvb24ocmVxOiBleHByZXNzLlJlcXVlc3QpOiBQcm9taXNlPHVua25vd24+IHtcbiAgY29uc3QgYml0Z28gPSByZXEuYml0Z287XG4gIGNvbnN0IGNvaW5OYW1lID0gcmVxLnBhcmFtcy5jb2luO1xuICBpZiAoIWlzTGlnaHRuaW5nQ29pbk5hbWUoY29pbk5hbWUpKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgY29pbiB0byBjcmVhdGUgc2lnbmVyIG1hY2Fyb29uOiAke2NvaW5OYW1lfS4gTXVzdCBiZSBhIGxpZ2h0bmluZyBjb2luLmAsIDQwMCk7XG4gIH1cbiAgY29uc3QgY29pbiA9IGJpdGdvLmNvaW4oY29pbk5hbWUpO1xuICBjb25zdCB3YWxsZXRJZCA9IHJlcS5wYXJhbXMuaWQ7XG4gIGlmICh0eXBlb2Ygd2FsbGV0SWQgIT09ICdzdHJpbmcnKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgd2FsbGV0IGlkOiAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cblxuICBjb25zdCB7IHBhc3NwaHJhc2UsIGFkZElwQ2F2ZWF0VG9NYWNhcm9vbiB9ID0gZGVjb2RlT3JFbHNlKFxuICAgIENyZWF0ZVNpZ25lck1hY2Fyb29uUmVxdWVzdC5uYW1lLFxuICAgIENyZWF0ZVNpZ25lck1hY2Fyb29uUmVxdWVzdCxcbiAgICByZXEuYm9keSxcbiAgICAoXykgPT4ge1xuICAgICAgLy8gRE9OJ1QgdGhyb3cgZXJyb3JzIGZyb20gZGVjb2RlT3JFbHNlLiBJdCBjb3VsZCBsZWFrIHNlbnNpdGl2ZSBpbmZvcm1hdGlvbi5cbiAgICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKCdJbnZhbGlkIHJlcXVlc3QgYm9keSB0byBjcmVhdGUgc2lnbmVyIG1hY2Fyb29uJywgNDAwKTtcbiAgICB9XG4gICk7XG5cbiAgY29uc3Qgd2FsbGV0ID0gYXdhaXQgY29pbi53YWxsZXRzKCkuZ2V0KHsgaWQ6IHdhbGxldElkLCBpbmNsdWRlQmFsYW5jZTogZmFsc2UgfSk7XG4gIGlmICh3YWxsZXQuc3ViVHlwZSgpICE9PSAnbGlnaHRuaW5nU2VsZkN1c3RvZHknKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYG5vdCBhIHNlbGYgY3VzdG9kaWFsIGxpZ2h0aW5nIHdhbGxldCAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cbiAgY29uc3Qgd2F0Y2hPbmx5SXAgPSB3YWxsZXQuY29pblNwZWNpZmljKCk/LndhdGNoT25seUV4dGVybmFsSXA7XG4gIGlmICghd2F0Y2hPbmx5SXAgJiYgYWRkSXBDYXZlYXRUb01hY2Fyb29uKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoXG4gICAgICAnQ2Fubm90IGNyZWF0ZSBzaWduZXIgbWFjYXJvb24gYmVjYXVzZSB0aGUgZXh0ZXJuYWwgSVAgaXMgbm90IHNldC4gVGhpcyBjYW4gdGFrZSBzb21lIHRpbWUuIENvbnRhY3Qgc3VwcG9ydEBiaXRnby5jb20gaWYgbG9uZ2VyIHRoYW4gMjQgaG91cnMuJyxcbiAgICAgIDQwMFxuICAgICk7XG4gIH1cblxuICBpZiAod2F0Y2hPbmx5SXAgJiYgIWlzSVAod2F0Y2hPbmx5SXApKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgSVAgYWRkcmVzczogJHt3YXRjaE9ubHlJcH0uIENvbnRhY3Qgc3VwcG9ydEBiaXRnby5jb21gLCA1MDApO1xuICB9XG5cbiAgY29uc3QgbG5kU2lnbmVyQ2xpZW50ID0gYXdhaXQgTG5kU2lnbmVyQ2xpZW50LmNyZWF0ZSh3YWxsZXRJZCwgcmVxLmNvbmZpZyk7XG5cbiAgY29uc3QgZW5jcnlwdGVkU2lnbmVyQWRtaW5NYWNhcm9vbiA9IHdhbGxldC5jb2luU3BlY2lmaWMoKT8uZW5jcnlwdGVkU2lnbmVyQWRtaW5NYWNhcm9vbjtcbiAgaWYgKCFlbmNyeXB0ZWRTaWduZXJBZG1pbk1hY2Fyb29uKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoJ01pc3NpbmcgZW5jcnlwdGVkU2lnbmVyQWRtaW5NYWNhcm9vbiBpbiB3YWxsZXQnLCA0MDApO1xuICB9XG4gIGNvbnN0IGFkbWluTWFjYXJvb24gPSBiaXRnby5kZWNyeXB0KHtcbiAgICBwYXNzd29yZDogcGFzc3BocmFzZSxcbiAgICBpbnB1dDogZW5jcnlwdGVkU2lnbmVyQWRtaW5NYWNhcm9vbixcbiAgfSk7XG5cbiAgY29uc3Qgc2lnbmVyTWFjYXJvb24gPSBhd2FpdCBjcmVhdGVTaWduZXJNYWNhcm9vbihcbiAgICBsbmRTaWduZXJDbGllbnQsXG4gICAgeyBhZG1pbk1hY2Fyb29uSGV4OiBCdWZmZXIuZnJvbShhZG1pbk1hY2Fyb29uLCAnYmFzZTY0JykudG9TdHJpbmcoJ2hleCcpIH0sXG4gICAgYWRkSXBDYXZlYXRUb01hY2Fyb29uID8gd2F0Y2hPbmx5SXAgOiBudWxsXG4gICk7XG5cbiAgcmV0dXJuIGF3YWl0IHVwZGF0ZVdhbGxldENvaW5TcGVjaWZpYyh3YWxsZXQsIHtcbiAgICBzaWduZXJNYWNhcm9vbixcbiAgICBwYXNzcGhyYXNlLFxuICB9KTtcbn1cblxuLyoqXG4gKiBIYW5kbGUgdGhlIHJlcXVlc3QgdG8gZ2V0IHRoZSBzdGF0ZSBvZiBhIHdhbGxldCBmcm9tIHRoZSBzaWduZXIuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBoYW5kbGVHZXRMaWdodG5pbmdXYWxsZXRTdGF0ZShyZXE6IGV4cHJlc3MuUmVxdWVzdCk6IFByb21pc2U8R2V0V2FsbGV0U3RhdGVSZXNwb25zZT4ge1xuICBjb25zdCBjb2luTmFtZSA9IHJlcS5wYXJhbXMuY29pbjtcbiAgaWYgKCFpc0xpZ2h0bmluZ0NvaW5OYW1lKGNvaW5OYW1lKSkge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIGNvaW4gdG8gZ2V0IGxpZ2h0bmluZyB3YWxsZXQgc3RhdGU6ICR7Y29pbk5hbWV9YCwgNDAwKTtcbiAgfVxuICBjb25zdCB3YWxsZXRJZCA9IHJlcS5wYXJhbXMuaWQ7XG4gIGlmICh0eXBlb2Ygd2FsbGV0SWQgIT09ICdzdHJpbmcnKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgd2FsbGV0IGlkOiAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cblxuICBjb25zdCBsbmRTaWduZXJDbGllbnQgPSBhd2FpdCBMbmRTaWduZXJDbGllbnQuY3JlYXRlKHdhbGxldElkLCByZXEuY29uZmlnKTtcbiAgcmV0dXJuIGF3YWl0IGxuZFNpZ25lckNsaWVudC5nZXRXYWxsZXRTdGF0ZSgpO1xufVxuXG4vKipcbiAqIEhhbmRsZSB0aGUgcmVxdWVzdCB0byB1bmxvY2sgYSB3YWxsZXQgaW4gdGhlIHNpZ25lci5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVVubG9ja0xpZ2h0bmluZ1dhbGxldChyZXE6IGV4cHJlc3MuUmVxdWVzdCk6IFByb21pc2U8eyBtZXNzYWdlOiBzdHJpbmcgfT4ge1xuICBjb25zdCBjb2luTmFtZSA9IHJlcS5wYXJhbXMuY29pbjtcbiAgaWYgKCFpc0xpZ2h0bmluZ0NvaW5OYW1lKGNvaW5OYW1lKSkge1xuICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKGBJbnZhbGlkIGNvaW4gdG8gdW5sb2NrIGxpZ2h0bmluZyB3YWxsZXQ6ICR7Y29pbk5hbWV9YCwgNDAwKTtcbiAgfVxuICBjb25zdCB3YWxsZXRJZCA9IHJlcS5wYXJhbXMuaWQ7XG4gIGlmICh0eXBlb2Ygd2FsbGV0SWQgIT09ICdzdHJpbmcnKSB7XG4gICAgdGhyb3cgbmV3IEFwaVJlc3BvbnNlRXJyb3IoYEludmFsaWQgd2FsbGV0IGlkOiAke3dhbGxldElkfWAsIDQwMCk7XG4gIH1cblxuICBjb25zdCB7IHBhc3NwaHJhc2UgfSA9IGRlY29kZU9yRWxzZShcbiAgICBVbmxvY2tMaWdodG5pbmdXYWxsZXRSZXF1ZXN0Lm5hbWUsXG4gICAgVW5sb2NrTGlnaHRuaW5nV2FsbGV0UmVxdWVzdCxcbiAgICByZXEuYm9keSxcbiAgICAoXykgPT4ge1xuICAgICAgLy8gRE9OJ1QgdGhyb3cgZXJyb3JzIGZyb20gZGVjb2RlT3JFbHNlLiBJdCBjb3VsZCBsZWFrIHNlbnNpdGl2ZSBpbmZvcm1hdGlvbi5cbiAgICAgIHRocm93IG5ldyBBcGlSZXNwb25zZUVycm9yKCdJbnZhbGlkIHJlcXVlc3QgYm9keSB0byB1bmxvY2sgbGlnaHRuaW5nIHdhbGxldCcsIDQwMCk7XG4gICAgfVxuICApO1xuXG4gIGNvbnN0IGxuZFNpZ25lckNsaWVudCA9IGF3YWl0IExuZFNpZ25lckNsaWVudC5jcmVhdGUod2FsbGV0SWQsIHJlcS5jb25maWcpO1xuICAvLyBUaGUgcGFzc3BocmFzZSBhdCBMTkQgY2FuIG9ubHkgYWNjb21tb2RhdGUgYSBiYXNlNjQgY2hhcmFjdGVyIHNldFxuICAvLyBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIEJUQy0xODUxXG4gIGF3YWl0IGxuZFNpZ25lckNsaWVudC51bmxvY2tXYWxsZXQoe1xuICAgIHdhbGxldF9wYXNzd29yZDogQnVmZmVyLmZyb20ocGFzc3BocmFzZSkudG9TdHJpbmcoJ2Jhc2U2NCcpLFxuICB9KTtcbiAgcmV0dXJuIHsgbWVzc2FnZTogJ29rJyB9O1xufVxuIl19Выполнить команду
Для локальной разработки. Не используйте в интернете!