eulith-web3js-core
Version:
Eulith core web3js SDK (code to access Eulith services via web3js)
487 lines (484 loc) • 69.1 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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Provider = void 0;
const assert_1 = require("assert");
const axios_1 = require("axios");
const axios = require("axios"); // For reasons that elude me, doing import instead of require doesn't appear to work with react apps (testing) - error axios.default...
const Eulith = __importStar(require("../src/index"));
function addParamsToURL_(url, params) {
var u = new URL(url);
for (const p in params) {
u.searchParams.set(p, params[p]);
}
return u.toString();
}
function LogErr_(logger, w, e) {
var _a;
logger === null || logger === void 0 ? void 0 : logger.log(Eulith.Logging.LogLevel.ERROR, `Error while ${w}: ${e.message}; details: ${JSON.stringify((_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.data)}`);
}
/*
* @typedef Eulith.Provider
*
* Use Eulith.Provider as your 'provider' with web3js to get automatic AUTH handling,
* mapping refresh tokens to access tokens.
*
* Key differences with including this logic in EulithWeb3 directly:
* o easier to adopt in code base already using web3js (no forced redo with common object):
* no need to switch any existing code, except preset what provider used to point to right
* server.
*
* (Default) Singers:
* In many cases, a given provider maybe used with a number of signers, and most of the relevant
* provider APIs take as argument the signer to use.
*
* However, there are a number of sitations where its helpful for the provider to have builtin
* knowledge of the signer to use.
*
* One reason, is that the Web3Adapater code in https://github.com/safe-global/safe-core-sdk/tree/main/packages/protocol-kit/src/adapters/web3
* assumes there is a builtin signer to use (the eth_signTypedData API takes a signer address, but assumes the underlying signer is
* somehow already known about). NOTE - this COULD have been handled by providing a WALLET API mapping those IDs to signers. FOR THIS scenario
* that might be better.
*
* There are other scenarios I don't understand (@Kristian/Moh) where the Eulith API requires knowledge of the signers address.
* I WONDER if these might be better handled by some other mechanism (like an extra parameter), but I'll reserve judgement
* until I better understand the reasons/cases where this is needed.
*
* Anyhow, for now, both these issues are handled by the presence of a 'defaultSigner' OPTIONAL property on the
* provider.
*
* send(eth_signTypedData) will only work if the signer address==defaultSigner.address.
*
* And those (TBD) Eulith APIs that require an authorized_address parameter all require this defaultSigner to be set
* on the provider.
*/
class Provider {
/**
* @constructor
*
* Example server url: "https://eth-goerli.eulithrpc.com", "http://localhost:7777", or "https://192.168.244.57:7777"
*
* \note - logger may be null or undefined, or any logger (e.g. Eulith.logger.ConsoleLogger). If value
* is null, no logging will be done. If value is undefined (default) - the Eulith.Logging.ConsoleLogger
* will be used.
*
* \note DO NOT use fetcherInstance parameter lightly. For now - just a debugging/analysis tool, and it maybe removed in future
* releases with no notice.
* \note DO NOT use urlQueryParams parameter yet and it maybe removed in future
* releases with no notice.
*/
constructor({ defaultSigner, network, urlQueryParams, refreshToken, logger, fetcherInstance }) {
this.urlQueryParams_ = {}; // @todo better doc and type {a:b,c:d};
if (logger === undefined) {
logger = new Eulith.Logging.ConsoleLogger();
}
if (fetcherInstance == null) {
fetcherInstance = axios.create({});
}
this.fetcherInstance_ = fetcherInstance;
this.logger_ = logger;
if (defaultSigner) {
this.defaultSigner_ = Eulith.Signing.SigningService.assure(defaultSigner, this);
}
if (network == null) {
throw new Error(`network parameter required for Eulith.Provider constructor`);
}
this.serverURL_ = network.eulithURL;
if (!this.serverURL_.endsWith("/v0")) {
if (!this.serverURL_.endsWith("/")) {
this.serverURL_ = this.serverURL_ + "/";
}
this.serverURL_ = this.serverURL_ + "v0";
}
this.network = network;
this.urlQueryParams_ = urlQueryParams !== null && urlQueryParams !== void 0 ? urlQueryParams : {};
this.originalRefreshToken_ = refreshToken;
if (this.defaultSigner_ != null) {
this.urlQueryParams_ = Object.assign(Object.assign({}, this.urlQueryParams_), { auth_address: this.defaultSigner_.address });
}
/*
* Unclear if this is a good idea or not???
* Reasons to check
* - if this ever failed, the user would want to to know. SERIOUS problem.
* Reasons not to check
* - Since CTOR is not async, we cannot just call the server, and get an answer.
* must fork-off an async, which WONT resolve in our current stackframe.
* So - IF this fails, its likely to be QUITE CONFUSING when it does. But still
* better to know than not to know.
* - Some performance costs, for no benefit if user programming carefully/successfully.
*/
const extraTesting = true;
if (extraTesting) {
(() => __awaiter(this, void 0, void 0, function* () {
(0, assert_1.strict)(this.network.chainId == (yield new Eulith.Web3({ provider: this }).eth.getChainId()), "network chainId must match chainId reported by RPC provider");
}))();
}
}
/**
* Eulith.Provider.ProviderOrWeb3
*
* Many APIs in the Eulith client library allow specifying either a provider or web3 (as a short-hand to get its underlying provider).
* This utility just maps either one to itself or underlying provider.
*
* NOT generally needed by users (since APIs will take either one) - but used internally by those APIs to perform the mapping.
*/
static ProviderOrWeb3(providerOrWeb3) {
if (providerOrWeb3 instanceof Eulith.Provider) {
return providerOrWeb3;
}
return providerOrWeb3.provider;
}
/**
* Eulith.Provider.prototype.refreshAPIToken()
*
* This CAN be used to force a refresh of the API token, but there is little point, as its done automatically.
*/
refreshAPIToken() {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
if (this.fetcherInstance_.options != null) {
// best way I can tell to decide fetch vs axios
try {
const apiTokenResponse = yield this.fetcherInstance_.get("api/access", {
baseURL: this.serverURL_,
headers: {
Authorization: "Bearer " + this.originalRefreshToken_
}
});
const apiToken = apiTokenResponse.data["token"];
(_a = this.logger_) === null || _a === void 0 ? void 0 : _a.log(Eulith.Logging.LogLevel.TRACE, `Updating api-token to ${apiToken}`);
this.latestAPIToken_ = apiToken;
this.authorizationHeader_ = "Bearer " + apiToken;
}
catch (e) {
Eulith.Exceptions.Auth.Throw(this.logger_, {
message: `Failed converting refresh token to access token: ${e.message}; serverURL: ${this.serverURL_}, responseData: ${JSON.stringify((_b = e === null || e === void 0 ? void 0 : e.response) === null || _b === void 0 ? void 0 : _b.data)}`
});
}
}
else {
try {
const response = yield this.fetcherInstance_(`${this.serverURL_}/api/access`, {
headers: {
Authorization: `Bearer ${this.originalRefreshToken_}`
}
});
const data = yield response.json();
const apiToken = data["token"];
(_c = this.logger_) === null || _c === void 0 ? void 0 : _c.log(Eulith.Logging.LogLevel.TRACE, `Updating api-token to ${apiToken}`);
this.latestAPIToken_ = apiToken;
this.authorizationHeader_ = "Bearer " + apiToken;
}
catch (e) {
Eulith.Exceptions.Auth.Throw(this.logger_, {
message: `Failed converting refresh token to access token: ${e.message}; serverURL: ${this.serverURL_}`
});
}
}
});
}
/**
* Eulith.Provider.prototype.logger
*
* get/return the logger associated with given provider.
*/
get logger() {
return this.logger_;
}
/**
* Eulith.Provider.prototype.defaultSigner
*
* get the default signer to be used for APIs such as eth_signTypedData, and ( (???) see Eulith.Provider constructor)
*/
get defaultSigner() {
return this.defaultSigner_;
}
/**
* Produce the original (unchanged) provider, if it already has these parameters, and otherwise
* produce a new provider, with these changes.
*/
addURLAdditionsIf(params) {
let hasAll = false;
for (let i in params) {
// kiss for now and assume worse case.
}
if (hasAll) {
return this;
}
else {
return this.cloneWithURLAdditions(params);
}
}
/**
* Clone the provider object, but with a few additional fields added to the URL. This is used internally to
* implement atomic transactions, and a few other things. Probably not useful externally.
*
* DONT USE EXTERNALLY - EXPERIMENTAL/INTERNAL API
*/
cloneWithURLAdditions(params) {
// @todo copy stuff like original auth token etc over here for performnace
let r = new Provider({
network: this.network,
refreshToken: this.originalRefreshToken_,
logger: this.logger_,
fetcherInstance: this.fetcherInstance_,
urlQueryParams: Object.assign(Object.assign({}, this.urlQueryParams_), params),
defaultSigner: this.defaultSigner_
});
// Not needed, but optimization - no use in refetching
r.latestAPIToken_ = this.latestAPIToken_;
r.authorizationHeader_ = this.authorizationHeader_;
return r;
}
/**
* Eulith.Provider.prototype.cloneWithSigner
*
* @returns Copy this provider, but with the provided defaultSigner
*/
cloneWithSigner(defaultSigner) {
let r = new Provider({
network: this.network,
refreshToken: this.originalRefreshToken_,
logger: this.logger_,
fetcherInstance: this.fetcherInstance_,
urlQueryParams: this.urlQueryParams_,
defaultSigner
});
// Not needed, but optimization - no use in refetching
r.latestAPIToken_ = this.latestAPIToken_;
r.authorizationHeader_ = this.authorizationHeader_;
return r;
}
/**
* Eulith.Provider.prototype.signAndSendTransaction
*
* o UNLIKE the web3js API which returns (nonstandard and painful with async) PromiEvents, this
* returns a Promise of the transasction hash (SOMEWHAT akin to web3.py send_transaction
* function, except that its async (returns a promise))
* o This API automatically signs its argument transactionConfig before sending, using the signer provided
* o Unlike the existing python code that sometimes signs, and sometiems doesn't depending on the presense of
* a transaction, and quirks the from/to, etc, this is simple - it ALWAYS SIGNS. Dont call it
* if you dont want the signing.
*
* This function returns txHash - not txReceiept.pt.
*
* to get the txReceipt, CALL
* const txReceipt: TransactionReceipt = await ew3.eth.getTransactionReceipt(txHash);
* or use Eulith.Utils.waitForTxReceipt, or Eulith.Provider.prototype.signAndSendTransactionAndWait
*/
signAndSendTransaction(transactionConfig, signer) {
return __awaiter(this, void 0, void 0, function* () {
if (signer == null) {
throw new Error("signAndSendTransaction requires a valid ISigner");
}
const useSigner = Eulith.Signing.SigningService.assure(signer, this);
return yield useSigner.sendTransaction(transactionConfig);
});
}
sendSignedTransaction(signedTx) {
return __awaiter(this, void 0, void 0, function* () {
// Translate PromiEvent to Promise...
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
try {
const ew3 = new Eulith.Web3({ provider: this });
const txRes = ew3.eth
.sendSignedTransaction(signedTx.rawTransaction)
.once("transactionHash", (transactionHash) => {
resolve(transactionHash);
})
.once("error", (error) => {
reject(error);
});
}
catch (e) {
LogErr_(this.logger_, "Eulith.Provider.prototype.sendSignedTransaction", e);
reject(e);
}
}));
});
}
/**
* shorthand for this.signAndSendTransaction followed by Eulith.Utils.waitForTxReceipt
*/
signAndSendTransactionAndWait(transactionConfig, signer, timeoutInMS) {
return __awaiter(this, void 0, void 0, function* () {
const txHash = yield this.signAndSendTransaction(transactionConfig, signer);
return yield Eulith.Utils.waitForTxReceipt({
logger: this.logger,
txHash: txHash,
provider: this,
timeoutInMS: timeoutInMS
});
});
}
/**
*/
sendAsync(payload, callback) {
// it appears never called, because request is implemented
// probably needs to be fixed to support websockets
}
/// Needed for integration with
send(payload, callback) {
var _a;
if (payload.method == "eth_signTypedData") {
// See https://eips.ethereum.org/EIPS/eip-712 Specification of the eth_signTypedData JSON RPC
// DOCS in https://eips.ethereum.org/EIPS/eip-712 say param [0] = address, and param[1] == TypedData, but the code
// in WebAdapater.js does the reverse - code wins! (for now)
// ACCORDING TO https://www.jsonrpc.org/specification - this is a bug and they must be in order
const signingAccount = payload.params[1];
const typedData = JSON.parse(payload.params[0]);
if (signingAccount != ((_a = this.defaultSigner_) === null || _a === void 0 ? void 0 : _a.address)) {
throw new Error("signer id mismatch");
}
this.defaultSigner_.signTypedData(typedData).then((a) => {
var _a;
(_a = this.logger_) === null || _a === void 0 ? void 0 : _a.log(Eulith.Logging.LogLevel.TRACE, `this.defaultSigner.signTypedData in eth_signedData send callback: ${JSON.stringify(a)}`);
// Eulith.Signing.SigningService.prototype.signTypedData returns ECDSASignature, but this eth_signTypedData
// is defined by eip-712 to return just the rsv string
callback(null, { jsonrpc: payload.jsonrpc, id: payload.id, result: a.rsv });
}); // @todo handle error case - not sure how... .catch and call error path I guess
}
else {
throw new Error("UNSUPPORTED SEND API"); // @todo perhaps should support this, but so far appears no need
}
}
/**
*/
request(args) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
this.connected = true;
// not required for correctness, but so we see fewer error logs, and fewer wasted round-trips to the API-server
if (this.authorizationHeader_ == null) {
yield this.refreshAPIToken(); // must refresh token
}
assert_1.strict.notEqual(this.authorizationHeader_, null, "Failed to acquire authorization token");
try {
return yield this.req_(args);
}
catch (e) {
// One 401 response, and we'll retry
if (((_a = e === null || e === void 0 ? void 0 : e.response) === null || _a === void 0 ? void 0 : _a.status) == 401) {
yield this.refreshAPIToken(); // must refresh token
return yield this.req_(args);
}
throw e;
}
});
}
req_(args) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
// SEE https://www.jsonrpc.org/specification for details on format of JSON RPC 2.0 messages
// probably needs to be fixed to support websockets
const useURL = addParamsToURL_(this.serverURL_, this.urlQueryParams_);
if (!Array.isArray(args.params)) {
throw new Error("RPC webserice requests require an array of params, even if empty");
}
const obj = {
jsonrpc: "2.0",
method: args.method,
params: args.params,
id: Provider.nextID_++
};
// leave out of TRACE by default, since redundant (except for timing info), and more often than not more distracting
// than helpful
// this.logger_?.log(Eulith.Logging.LogLevel.TRACE, `sending request: ${JSON.stringify(args)}`);
let axiosResponse;
let result;
if (this.fetcherInstance_.options != null) {
// best way I can tell to decide fetch vs axios
axiosResponse = yield this.fetcherInstance_.post("", obj, {
baseURL: useURL,
headers: { Authorization: this.authorizationHeader_ },
validateStatus: () => true // don't throw on bad status, so we RPC error object, and can handle at that level
});
result = axiosResponse === null || axiosResponse === void 0 ? void 0 : axiosResponse.data;
}
else {
axiosResponse = (yield this.fetcherInstance_(useURL, {
method: "POST",
headers: {
Authorization: this.authorizationHeader_,
"Content-Type": "text/json"
},
body: JSON.stringify(obj)
}));
const data = yield axiosResponse.json();
result = data;
}
// According to https://www.jsonrpc.org/specification '5 Response...', result required iff success,
// and error required iff there was an error
const wasError = (result === null || result === void 0 ? void 0 : result.result) === undefined;
if (wasError) {
/*
https://www.jsonrpc.org/specification - Section 5.1 says:
When a rpc call encounters an error, the Response Object MUST contain the error member with a value that is a Object with the following members:
code
A Number that indicates the error type that occurred.
This MUST be an integer.
message
A String providing a short description of the error.
The message SHOULD be limited to a concise single sentence.
data
A Primitive or Structured value that contains additional information about the error.
This may be omitted.
The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.).
YET, what we return is:
{"code":32600,"error":"failed ot get quote on-chain, Revert(Bytes(0x))","id":14}
I sent a message to Kristian - 2023-05-01 - confused about format of error messages. It appears our server does this
wrong. Well - we must be compatible with what our server does - right or wrong. So adjusting accordingly for now
*/
if (typeof result.error == "string") {
Eulith.Exceptions.RPC.Throw(this.logger, {
request: obj,
error: { code: result.code, message: result.error }
});
}
else {
Eulith.Exceptions.RPC.Throw(this.logger, { request: obj, error: result.error });
}
}
if (200 < axiosResponse.status || axiosResponse.status >= 300) {
throw new axios_1.AxiosError(`HTTP Error status${axiosResponse.status}`, null, null, axiosResponse);
}
result = result === null || result === void 0 ? void 0 : result.result;
(_a = this.logger_) === null || _a === void 0 ? void 0 : _a.log(Eulith.Logging.LogLevel.TRACE, `RPC \n\tRequest:\n\t\t${JSON.stringify(args)}\n\turl:\n\t\t${useURL}\n\tResponse:\n\t\t${JSON.stringify(result)}`);
return result;
});
}
}
exports.Provider = Provider;
Provider.nextID_ = 1;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcHJvdmlkZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFJQSxtQ0FBMEM7QUFHMUMsaUNBQWlFO0FBQ2pFLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLHVJQUF1STtBQUV2SyxxREFBdUM7QUFFdkMsU0FBUyxlQUFlLENBQUMsR0FBVyxFQUFFLE1BQWM7SUFDaEQsSUFBSSxDQUFDLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDckIsS0FBSyxNQUFNLENBQUMsSUFBSSxNQUFNLEVBQUU7UUFDcEIsQ0FBQyxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQ3BDO0lBQ0QsT0FBTyxDQUFDLENBQUMsUUFBUSxFQUFFLENBQUM7QUFDeEIsQ0FBQztBQUVELFNBQVMsT0FBTyxDQUFDLE1BQThCLEVBQUUsQ0FBUyxFQUFFLENBQXFCOztJQUM3RSxNQUFNLGFBQU4sTUFBTSx1QkFBTixNQUFNLENBQUUsR0FBRyxDQUNQLE1BQU0sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssRUFDN0IsZUFBZSxDQUFDLEtBQUssQ0FBQyxDQUFDLE9BQU8sY0FBYyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQUMsQ0FBZ0IsYUFBaEIsQ0FBQyx1QkFBRCxDQUFDLENBQWlCLFFBQVEsMENBQUUsSUFBSSxDQUFDLEVBQUUsQ0FDbEcsQ0FBQztBQUNOLENBQUM7QUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWtDRztBQUNILE1BQWEsUUFBUTtJQUNqQjs7Ozs7Ozs7Ozs7OztPQWFHO0lBQ0gsWUFBbUIsRUFDZixhQUFhLEVBQ2IsT0FBTyxFQUNQLGNBQWMsRUFDZCxZQUFZ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QVEsQ0FBQyxLQUFLLEVBQzdCLHlCQUF5QixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsTUFBTSxzQkFBc0IsSUFBSSxDQUFDLFNBQVMsQ0FDcEcsTUFBTSxDQUNULEVBQUUsQ0FDTixDQUFDO1lBQ0YsT0FBTyxNQUFNLENBQUM7O0tBQ2pCOztBQWpjTCw0QkFrY0M7QUE5RmtCLGdCQUFPLEdBQVcsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQWJzdHJhY3RQcm92aWRlciwgUmVxdWVzdEFyZ3VtZW50cywgVHJhbnNhY3Rpb25Db25maWcsIFRyYW5zYWN0aW9uUmVjZWlwdCB9IGZyb20gXCJ3ZWIzLWNvcmVcIjtcbmltcG9ydCB7IEpzb25ScGNQYXlsb2FkLCBKc29uUnBjUmVzcG9uc2UgfSBmcm9tIFwid2ViMy1jb3JlLWhlbHBlcnNcIjtcbmltcG9ydCB7IEpzb25ScGNFcnJvciwgSnNvblJwY1Jlc3BvbnNlV2l0aEVycm9yIH0gZnJvbSBcIndlYjMtdHlwZXNcIjtcbmltcG9ydCB7IEpzb25ScGNSZXF1ZXN0IH0gZnJvbSBcIndlYjMtdHlwZXNcIjtcbmltcG9ydCB7IHN0cmljdCBhcyBhc3NlcnQgfSBmcm9tIFwiYXNzZXJ0XCI7XG5pbXBvcnQgKiBhcyBXZWIzQ29yZSBmcm9tIFwid2ViMy1jb3JlXCI7XG5cbmltcG9ydCB7IEF4aW9zSW5zdGFuY2UsIEF4aW9zUmVzcG9uc2UsIEF4aW9zRXJyb3IgfSBmcm9tIFwiYXhpb3NcIjtcbmNvbnN0IGF4aW9zID0gcmVxdWlyZShcImF4aW9zXCIpOyAvLyBGb3IgcmVhc29ucyB0aGF0IGVsdWRlIG1lLCBkb2luZyBpbXBvcnQgaW5zdGVhZCBvZiByZXF1aXJlIGRvZXNuJ3QgYXBwZWFyIHRvIHdvcmsgd2l0aCByZWFjdCBhcHBzICh0ZXN0aW5nKSAtIGVycm9yIGF4aW9zLmRlZmF1bHQuLi5cblxuaW1wb3J0ICogYXMgRXVsaXRoIGZyb20gXCIuLi9zcmMvaW5kZXhcIjtcblxuZnVuY3Rpb24gYWRkUGFyYW1zVG9VUkxfKHVybDogc3RyaW5nLCBwYXJhbXM6IG9iamVjdCkge1xuICAgIHZhciB1ID0gbmV3IFVSTCh1cmwpO1xuICAgIGZvciAoY29uc3QgcCBpbiBwYXJhbXMpIHtcbiAgICAgICAgdS5zZWFyY2hQYXJhbXMuc2V0KHAsIHBhcmFtc1twXSk7XG4gICAgfVxuICAgIHJldHVybiB1LnRvU3RyaW5nKCk7XG59XG5cbmZ1bmN0aW9uIExvZ0Vycl8obG9nZ2VyOiBFdWxpdGguTG9nZ2luZy5JTG9nZ2VyLCB3OiBzdHJpbmcsIGU6IEVycm9yIHwgQXhpb3NFcnJvcik6IHZvaWQge1xuICAgIGxvZ2dlcj8ubG9nKFxuICAgICAgICBFdWxpdGguTG9nZ2luZy5Mb2dMZXZlbC5FUlJPUixcbiAgICAgICAgYEVycm9yIHdoaWxlICR7d306ICR7ZS5tZXNzYWdlfTsgZGV0YWlsczogJHtKU09OLnN0cmluZ2lmeSgoZSBhcyBBeGlvc0Vycm9yKT8ucmVzcG9uc2U/LmRhdGEpfWBcbiAgICApO1xufVxuXG4vKlxuICogIEB0eXBlZGVmIEV1bGl0aC5Qcm92aWRlclxuICpcbiAqICBVc2UgRXVsaXRoLlByb3ZpZGVyIGFzIHlvdXIgJ3Byb3ZpZGVyJyB3aXRoIHdlYjNqcyB0byBnZXQgYXV0b21hdGljIEFVVEggaGFuZGxpbmcsXG4gKiAgbWFwcGluZyByZWZyZXNoIHRva2VucyB0byBhY2Nlc3MgdG9rZW5zLlxuICpcbiAqICBLZXkgZGlmZmVyZW5jZXMgd2l0aCBpbmNsdWRpbmcgdGhpcyBsb2dpYyBpbiBFdWxpdGhXZWIzIGRpcmVjdGx5OlxuICogICAgICBvICAgZWFzaWVyIHRvIGFkb3B0IGluIGNvZGUgYmFzZSBhbHJlYWR5IHVzaW5nIHdlYjNqcyAobm8gZm9yY2VkIHJlZG8gd2l0aCBjb21tb24gb2JqZWN0KTpcbiAqICAgICAgICAgICAgICBubyBuZWVkIHRvIHN3aXRjaCBhbnkgZXhpc3RpbmcgY29kZSwgZXhjZXB0IHByZXNldCB3aGF0IHByb3ZpZGVyIHVzZWQgdG8gcG9pbnQgdG8gcmlnaHRcbiAqICAgICAgICAgICAgICBzZXJ2ZXIuXG4gKlxuICogICAoRGVmYXVsdCkgU2luZ2VyczpcbiAqICAgICAgSW4gbWFueSBjYXNlcywgYSBnaXZlbiBwcm92aWRlciBtYXliZSB1c2VkIHdpdGggYSBudW1iZXIgb2Ygc2lnbmVycywgYW5kIG1vc3Qgb2YgdGhlIHJlbGV2YW50XG4gKiAgICAgIHByb3ZpZGVyIEFQSXMgdGFrZSBhcyBhcmd1bWVudCB0aGUgc2lnbmVyIHRvIHVzZS5cbiAqXG4gKiAgICAgIEhvd2V2ZXIsIHRoZXJlIGFyZSBhIG51bWJlciBvZiBzaXRhdGlvbnMgd2hlcmUgaXRzIGhlbHBmdWwgZm9yIHRoZSBwcm92aWRlciB0byBoYXZlIGJ1aWx0aW5cbiAqICAgICAga25vd2xlZGdlIG9mIHRoZSBzaWduZXIgdG8gdXNlLlxuICpcbiAqICAgICAgT25lIHJlYXNvbiwgaXMgdGhhdCB0aGUgV2ViM0FkYXBhdGVyIGNvZGUgaW4gaHR0cHM6Ly9naXRodWIuY29tL3NhZmUtZ2xvYmFsL3NhZmUtY29yZS1zZGsvdHJlZS9tYWluL3BhY2thZ2VzL3Byb3RvY29sLWtpdC9zcmMvYWRhcHRlcnMvd2ViM1xuICogICAgICBhc3N1bWVzIHRoZXJlIGlzIGEgYnVpbHRpbiBzaWduZXIgdG8gdXNlICh0aGUgZXRoX3NpZ25UeXBlZERhdGEgQVBJIHRha2VzIGEgc2lnbmVyIGFkZHJlc3MsIGJ1dCBhc3N1bWVzIHRoZSB1bmRlcmx5aW5nIHNpZ25lciBpc1xuICogICAgICBzb21laG93IGFscmVhZHkga25vd24gYWJvdXQpLiBOT1RFIC0gdGhpcyBDT1VMRCBoYXZlIGJlZW4gaGFuZGxlZCBieSBwcm92aWRpbmcgYSBXQUxMRVQgQVBJIG1hcHBpbmcgdGhvc2UgSURzIHRvIHNpZ25lcnMuIEZPUiBUSElTIHNjZW5hcmlvXG4gKiAgICAgIHRoYXQgbWlnaHQgYmUgYmV0dGVyLlxuICpcbiAqICAgICAgVGhlcmUgYXJlIG90aGVyIHNjZW5hcmlvcyBJIGRvbid0IHVuZGVyc3RhbmQgKEBLcmlzdGlhbi9Nb2gpIHdoZXJlIHRoZSBFdWxpdGggQVBJIHJlcXVpcmVzIGtub3dsZWRnZSBvZiB0aGUgc2lnbmVycyBhZGRyZXNzLlxuICogICAgICBJIFdPTkRFUiBpZiB0aGVzZSBtaWdodCBiZSBiZXR0ZXIgaGFuZGxlZCBieSBzb21lIG90aGVyIG1lY2hhbmlzbSAobGlrZSBhbiBleHRyYSBwYXJhbWV0ZXIpLCBidXQgSSdsbCByZXNlcnZlIGp1ZGdlbWVudFxuICogICAgICB1bnRpbCBJIGJldHRlciB1bmRlcnN0YW5kIHRoZSByZWFzb25zL2Nhc2VzIHdoZXJlIHRoaXMgaXMgbmVlZGVkLlxuICpcbiAqICAgICAgQW55aG93LCBmb3Igbm93LCBib3RoIHRoZXNlIGlzc3VlcyBhcmUgaGFuZGxlZCBieSB0aGUgcHJlc2VuY2Ugb2YgYSAnZGVmYXVsdFNpZ25lcicgT1BUSU9OQUwgcHJvcGVydHkgb24gdGhlXG4gKiAgICAgIHByb3ZpZGVyLlxuICpcbiAqICAgICAgc2VuZChldGhfc2lnblR5cGVkRGF0YSkgd2lsbCBvbmx5IHdvcmsgaWYgdGhlIHNpZ25lciBhZGRyZXNzPT1kZWZhdWx0U2lnbmVyLmFkZHJlc3MuXG4gKlxuICogICAgICBBbmQgdGhvc2UgKFRCRCkgRXVsaXRoIEFQSXMgdGhhdCByZXF1aXJlIGFuIGF1dGhvcml6ZWRfYWRkcmVzcyBwYXJhbWV0ZXIgYWxsIHJlcXVpcmUgdGhpcyBkZWZhdWx0U2lnbmVyIHRvIGJlIHNldFxuICogICAgICBvbiB0aGUgcHJvdmlkZXIuXG4gKi9cbmV4cG9ydCBjbGFzcyBQcm92aWRlciBpbXBsZW1lbnRzIEFic3RyYWN0UHJvdmlkZXIge1xuICAgIC8qKlxuICAgICAqIEBjb25zdHJ1Y3RvclxuICAgICAqXG4gICAgICogRXhhbXBsZSBzZXJ2ZXIgdXJsOiBcImh0dHBzOi8vZXRoLWdvZXJsaS5ldWxpdGhycGMuY29tXCIsIFwiaHR0cDovL2xvY2FsaG9zdDo3Nzc3XCIsIG9yIFwiaHR0cHM6Ly8xOTIuMTY4LjI0NC41Nzo3Nzc3XCJcbiAgICAgKlxuICAgICAqICBcXG5vdGUgLSBsb2dnZXIgbWF5IGJlIG51bGwgb3IgdW5kZWZpbmVkLCBvciBhbnkgbG9nZ2VyIChlLmcuIEV1bGl0aC5sb2dnZXIuQ29uc29sZUxvZ2dlcikuIElmIHZhbHVlXG4gICAgICogICAgICAgIGlzIG51bGwsIG5vIGxvZ2dpbmcgd2lsbCBiZSBkb25lLiBJZiB2YWx1ZSBpcyB1bmRlZmluZWQgKGRlZmF1bHQpIC0gdGhlIEV1bGl0aC5Mb2dnaW5nLkNvbnNvbGVMb2dnZXJcbiAgICAgKiAgICAgICAgd2lsbCBiZSB1c2VkLlxuICAgICAqXG4gICAgICogIFxcbm90ZSBETyBOT1QgdXNlIGZldGNoZXJJbnN0YW5jZSBwYXJhbWV0ZXIgbGlnaHRseS4gRm9yIG5vdyAtIGp1c3QgYSBkZWJ1Z2dpbmcvYW5hbHlzaXMgdG9vbCwgYW5kIGl0IG1heWJlIHJlbW92ZWQgaW4gZnV0dXJlXG4gICAgICogICAgICAgIHJlbGVhc2VzIHdpdGggbm8gbm90aWNlLlxuICAgICAqICBcXG5vdGUgRE8gTk9UIHVzZSB1cmxRdWVyeVBhcmFtcyBwYXJhbWV0ZXIgeWV0IGFuZCBpdCBtYXliZSByZW1vdmVkIGluIGZ1dHVyZVxuICAgICAqICAgICAgICByZWxlYXNlcyB3aXRoIG5vIG5vdGljZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgY29uc3RydWN0b3Ioe1xuICAgICAgICBkZWZhdWx0U2lnbmVyLFxuICAgICAgICBuZXR3b3JrLFxuICAgICAgICB1cmxRdWVyeVBhcmFtcyxcbiAgICAgICAgcmVmcmVzaFRva2VuLFxuICAgICAgICBsb2dnZXIsXG4gICAgICAgIGZldGNoZXJJbnN0YW5jZVxuICAgIH06IHtcbiAgICAgICAgZGVmYXVsdFNpZ25lcj86IEV1bGl0aC5TaWduaW5nLlNpZ25pbmdTZXJ2aWNlIHwgRXVsaXRoLlNpZ25pbmcuSUNyeXB0b2dyYXBoaWNTaWduZXI7XG4gICAgICAgIG5ldHdvcms6IEV1bGl0aC5OZXR3b3Jrcy5OZXR3b3JrO1xuICAgICAgICB1cmxRdWVyeVBhcmFtcz86IG9iamVjdDsgLy8gQHRvZG8gYmV0dGVyIGRvYyBhbmQgdHlwZSB7YTpiLGM6ZH1cbiAgICAgICAgcmVmcmVzaFRva2VuOiBzdHJpbmc7XG4gICAgICAgIGxvZ2dlcj86IEV1bGl0aC5Mb2dnaW5nLklMb2dnZXIgfCBudWxsO1xuICAgICAgICBmZXRjaGVySW5zdGFuY2U/OiBBeGlvc0luc3RhbmNlIHwgRnVuY3Rpb247IC8vIGF4aW9zIGluc3RhbmNlIG9yIEZFVENIIGZ1bmN0aW9uXG4gICAgfSkge1xuICAgICAgICBpZiAobG9nZ2VyID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGxvZ2dlciA9IG5ldyBFdWxpdGguTG9nZ2luZy5Db25zb2xlTG9nZ2VyKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZldGNoZXJJbnN0YW5jZSA9PSBudWxsKSB7XG4gICAgICAgICAgICBmZXRjaGVySW5zdGFuY2UgPSBheGlvcy5jcmVhdGUoe30pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZmV0Y2hlckluc3RhbmNlXyA9IGZldGNoZXJJbnN0YW5jZTtcbiAgICAgICAgdGhpcy5sb2dnZXJfID0gbG9nZ2VyO1xuICAgICAgICBpZiAoZGVmYXVsdFNpZ25lcikge1xuICAgICAgICAgICAgdGhpcy5kZWZhdWx0U2lnbmVyXyA9IEV1bGl0aC5TaWduaW5nLlNpZ25pbmdTZXJ2aWNlLmFzc3VyZShkZWZhdWx0U2lnbmVyLCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobmV0d29yayA9PSBudWxsKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYG5ldHdvcmsgcGFyYW1ldGVyIHJlcXVpcmVkIGZvciBFdWxpdGguUHJvdmlkZXIgY29uc3RydWN0b3JgKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNlcnZlclVSTF8gPSBuZXR3b3JrLmV1bGl0aFVSTDtcbiAgICAgICAgaWYgKCF0aGlzLnNlcnZlclVSTF8uZW5kc1dpdGgoXCIvdjBcIikpIHtcbiAgICAgICAgICAgIGlmICghdGhpcy5zZXJ2ZXJVUkxfLmVuZHNXaXRoKFwiL1wiKSkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2VydmVyVVJMXyA9IHRoaXMuc2VydmVyVVJMXyArIFwiL1wiO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy5zZXJ2ZXJVUkxfID0gdGhpcy5zZXJ2ZXJVUkxfICsgXCJ2MFwiO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMubmV0d29yayA9IG5ldHdvcms7XG4gICAgICAgIHRoaXMudXJsUXVlcnlQYXJhbXNfID0gdXJsUXVlcnlQYXJhbXMgPz8ge307XG4gICAgICAgIHRoaXMub3JpZ2luYWxSZWZyZXNoVG9rZW5fID0gcmVmcmVzaFRva2VuO1xuXG4gICAgICAgIGlmICh0aGlzLmRlZmF1bHRTaWduZXJfICE9IG51bGwpIHtcbiAgICAgICAgICAgIHRoaXMudXJsUXVlcnlQYXJhbXNfID0ge1xuICAgICAgICAgICAgICAgIC4uLnRoaXMudXJsUXVlcnlQYXJhbXNfLFxuICAgICAgICAgICAgICAgIC4uLnsgYXV0aF9hZGRyZXNzOiB0aGlzLmRlZmF1bHRTaWduZXJfLmFkZHJlc3MgfVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qXG4gICAgICAgICAqICBVbmNsZWFyIGlmIHRoaXMgaXMgYSBnb29kIGlkZWEgb3Igbm90Pz8/XG4gICAgICAgICAqICBSZWFzb25zIHRvIGNoZWNrXG4gICAgICAgICAqICAgICAgLSBpZiB0aGlzIGV2ZXIgZmFpbGVkLCB0aGUgdXNlciB3b3VsZCB3YW50IHRvIHRvIGtub3cuIFNFUklPVVMgcHJvYmxlbS5cbiAgICAgICAgICogIFJlYXNvbnMgbm90IHRvIGNoZWNrXG4gICAgICAgICAqICAgICAgLSAgU2luY2UgQ1RPUiBpcyBub3QgYXN5bmMsIHdlIGNhbm5vdCBqdXN0IGNhbGwgdGhlIHNlcnZlciwgYW5kIGdldCBhbiBhbnN3ZXIuXG4gICAgICAgICAqICAgICAgICAgbXVzdCBmb3JrLW9mZiBhbiBhc3luYywgd2hpY2ggV09OVCByZXNvbHZlIGluIG91ciBjdXJyZW50IHN0YWNrZnJhbWUuXG4gICAgICAgICAqICAgICAgICAgU28gLSBJRiB0aGlzIGZhaWxzLCBpdHMgbGlrZWx5IHRvIGJlIFFVSVRFIENPTkZVU0lORyB3aGVuIGl0IGRvZXMuIEJ1dCBzdGlsbFxuICAgICAgICAgKiAgICAgICAgIGJldHRlciB0byBrbm93IHRoYW4gbm90IHRvIGtub3cuXG4gICAgICAgICAqICAgICAgLSAgU29tZSBwZXJmb3JtYW5jZSBjb3N0cywgZm9yIG5vIGJlbmVmaXQgaWYgdXNlciBwcm9ncmFtbWluZyBjYXJlZnVsbHkvc3VjY2Vzc2Z1bGx5LlxuICAgICAgICAgKi9cbiAgICAgICAgY29uc3QgZXh0cmFUZXN0aW5nID0gdHJ1ZTtcbiAgICAgICAgaWYgKGV4dHJhVGVzdGluZykge1xuICAgICAgICAgICAgKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBhc3NlcnQoXG4gICAgICAgICAgICAgICAgICAgIHRoaXMubmV0d29yay5jaGFpbklkID09IChhd2FpdCBuZXcgRXVsaXRoLldlYjMoeyBwcm92aWRlcjogdGhpcyB9KS5ldGguZ2V0Q2hhaW5JZCgpKSxcbiAgICAgICAgICAgICAgICAgICAgXCJuZXR3b3JrIGNoYWluSWQgbXVzdCBtYXRjaCBjaGFpbklkIHJlcG9ydGVkIGJ5IFJQQyBwcm92aWRlclwiXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0pKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgcmVhZG9ubHkgbmV0d29yazogRXVsaXRoLk5ldHdvcmtzLk5ldHdvcms7XG5cbiAgICAvKipcbiAgICAgKiAgRXVsaXRoLlByb3ZpZGVyLlByb3ZpZGVyT3JXZWIzXG4gICAgICpcbiAgICAgKiAgTWFueSBBUElzIGluIHRoZSBFdWxpdGggY2xpZW50IGxpYnJhcnkgYWxsb3cgc3BlY2lmeWluZyBlaXRoZXIgYSBwcm92aWRlciBvciB3ZWIzIChhcyBhIHNob3J0LWhhbmQgdG8gZ2V0IGl0cyB1bmRlcmx5aW5nIHByb3ZpZGVyKS5cbiAgICAgKiAgVGhpcyB1dGlsaXR5IGp1c3QgbWFwcyBlaXRoZXIgb25lIHRvIGl0c2VsZiBvciB1bmRlcmx5aW5nIHByb3ZpZGVyLlxuICAgICAqXG4gICAgICogIE5PVCBnZW5lcmFsbHkgbmVlZGVkIGJ5IHVzZXJzIChzaW5jZSBBUElzIHdpbGwgdGFrZSBlaXRoZXIgb25lKSAtIGJ1dCB1c2VkIGludGVybmFsbHkgYnkgdGhvc2UgQVBJcyB0byBwZXJmb3JtIHRoZSBtYXBwaW5nLlxuICAgICAqL1xuICAgIHB1YmxpYyBzdGF0aWMgUHJvdmlkZXJPcldlYjMocHJvdmlkZXJPcldlYjM6IEV1bGl0aC5Qcm92aWRlciB8IEV1bGl0aC5XZWIzKTogRXVsaXRoLlByb3ZpZGVyIHtcbiAgICAgICAgaWYgKHByb3ZpZGVyT3JXZWIzIGluc3RhbmNlb2YgRXVsaXRoLlByb3ZpZGVyKSB7XG4gICAgICAgICAgICByZXR1cm4gcHJvdmlkZXJPcldlYjM7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHByb3ZpZGVyT3JXZWIzLnByb3ZpZGVyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqICBFdWxpdGguUHJvdmlkZXIucHJvdG90eXBlLnJlZnJlc2hBUElUb2tlbigpXG4gICAgICpcbiAgICAgKiAgVGhpcyBDQU4gYmUgdXNlZCB0byBmb3JjZSBhIHJlZnJlc2ggb2YgdGhlIEFQSSB0b2tlbiwgYnV0IHRoZXJlIGlzIGxpdHRsZSBwb2ludCwgYXMgaXRzIGRvbmUgYXV0b21hdGljYWxseS5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgcmVmcmVzaEFQSVRva2VuKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoKHRoaXMuZmV0Y2hlckluc3RhbmNlXyBhcyBhbnkpLm9wdGlvbnMgIT0gbnVsbCkge1xuICAgICAgICAgICAgLy8gYmVzdCB3YXkgSSBjYW4gdGVsbCB0byBkZWNpZGUgZmV0Y2ggdnMgYXhpb3NcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgYXBpVG9rZW5SZXNwb25zZTogQXhpb3NSZXNwb25zZSA9IGF3YWl0ICh0aGlzLmZldGNoZXJJbnN0YW5jZV8gYXMgQXhpb3NJbnN0YW5jZSkuZ2V0KFxuICAgICAgICAgICAgICAgICAgICBcImFwaS9hY2Nlc3NcIixcbiAgICAgICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICAgICAgYmFzZVVSTDogdGhpcy5zZXJ2ZXJVUkxfLFxuICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIEF1dGhvcml6YXRpb246IFwiQmVhcmVyIFwiICsgdGhpcy5vcmlnaW5hbFJlZnJlc2hUb2tlbl9cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgY29uc3QgYXBpVG9rZW46IHN0cmluZyA9IGFwaVRva2VuUmVzcG9uc2UuZGF0YVtcInRva2VuXCJdO1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyXz8ubG9nKEV1bGl0aC5Mb2dnaW5nLkxvZ0xldmVsLlRSQUNFLCBgVXBkYXRpbmcgYXBpLXRva2VuIHRvICR7YXBpVG9rZW59YCk7XG4gICAgICAgICAgICAgICAgdGhpcy5sYXRlc3RBUElUb2tlbl8gPSBhcGlUb2tlbjtcbiAgICAgICAgICAgICAgICB0aGlzLmF1dGhvcml6YXRpb25IZWFkZXJfID0gXCJCZWFyZXIgXCIgKyBhcGlUb2tlbjtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICBFdWxpdGguRXhjZXB0aW9ucy5BdXRoLlRocm93KHRoaXMubG9nZ2VyXywge1xuICAgICAgICAgICAgICAgICAgICBtZXNzYWdlOiBgRmFpbGVkIGNvbnZlcnRpbmcgcmVmcmVzaCB0b2tlbiB0byBhY2Nlc3MgdG9rZW46ICR7ZS5tZXNzYWdlfTsgc2VydmVyVVJMOiAke1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXJ2ZXJVUkxfXG4gICAgICAgICAgICAgICAgICAgIH0sIHJlc3BvbnNlRGF0YTogJHtKU09OLnN0cmluZ2lmeShlPy5yZXNwb25zZT8uZGF0YSl9YFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0ICh0aGlzLmZldGNoZXJJbnN0YW5jZV8gYXMgRnVuY3Rpb24pKGAke3RoaXMuc2VydmVyVVJMX30vYXBpL2FjY2Vzc2AsIHtcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgICAgICAgICAgICAgQXV0aG9yaXphdGlvbjogYEJlYXJlciAke3RoaXMub3JpZ2luYWxSZWZyZXNoVG9rZW5ffWBcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gI