UNPKG

metaapi.cloud-sdk

Version:

SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)

407 lines (406 loc) 53.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return MetaApiConnectionInstance; } }); const _logger = /*#__PURE__*/ _interop_require_default(require("../logger")); const _randomstring = /*#__PURE__*/ _interop_require_default(require("randomstring")); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } let MetaApiConnectionInstance = class MetaApiConnectionInstance { /** * Opens the connection. Can only be called the first time, next calls will be ignored. * @return {Promise} promise resolving when the connection is opened */ async connect() { this._opened = true; } /** * Closes the connection. The instance of the class should no longer be used after this method is invoked. */ async close() { this._opened = false; this._closed = true; } /** * Common trade options * @typedef {Object} TradeOptions * @property {String} [comment] optional order comment. The sum of the line lengths of the comment and the * clientId must be less than or equal to 26. For more information see * https://metaapi.cloud/docs/client/clientIdUsage/ * @property {String} [clientId] optional client-assigned id. The id value can be assigned when submitting a trade and * will be present on position, history orders and history deals related to the trade. You can use this field to bind * your trades to objects in your application and then track trade progress. The sum of the line lengths of the * comment and the clientId must be less than or equal to 26. For more information see * https://metaapi.cloud/docs/client/clientIdUsage/ * @property {Number} [magic] optional magic (expert id) number. If not set default value specified in account entity * will be used. * @property {Number} [slippage] optional slippage in points. Should be greater or equal to zero. In not set, * default value specified in account entity will be used. Slippage is ignored if execution mode set to * SYMBOL_TRADE_EXECUTION_MARKET in symbol specification. Not used for close by orders. */ /** * Market trade options * @typedef {TradeOptions} MarketTradeOptions * @property {Array<String>} [fillingModes] optional allowed filling modes in the order of priority. Default is to * allow all filling modes and prefer ORDER_FILLING_FOK over ORDER_FILLING_IOC. See * https://www.mql5.com/en/docs/constants/tradingconstants/orderproperties#enum_order_type_filling for extra * explanation */ /** * Market trade options * @typedef {MarketTradeOptions} CreateMarketTradeOptions * @property {TrailingStopLoss} [trailingStopLoss] distance trailing stop loss configuration * @property {String} [stopPriceBase] defines the base price to calculate SL/TP relative to for pending order * requests. Default is CURRENT_PRICE, one of CURRENT_PRICE */ /** * Pending order trade options * @typedef {TradeOptions} PendingTradeOptions * @property {ExpirationOptions} [expiration] optional pending order expiration settings. See Pending order expiration * settings section * @property {TrailingStopLoss} [trailingStopLoss] distance trailing stop loss configuration * @property {String} [stopPriceBase] defined the base price to calculate SL/TP relative to for *_MODIFY and pending * order requests. STOP_PRICE means the SL/TP is relative to previous SL/TP value. Default is OPEN_PRICE, one of * CURRENT_PRICE, OPEN_PRICE * @property {String} [openPriceUnits] open price units. ABSOLUTE_PRICE means the that the value of openPrice field * is a final open price value. RELATIVE* means that the openPrice field value contains relative open price expressed * either in price, points, pips, account currency or balance percentage. Default is ABSOLUTE_PRICE. One of * ABSOLUTE_PRICE, RELATIVE_PRICE, RELATIVE_POINTS, RELATIVE_PIPS, RELATIVE_CURRENCY, RELATIVE_BALANCE_PERCENTAGE */ /** * Stop options * @typedef {Object} StopOptions * @property {number} value stop (SL or TP) value * @property {string} units stop units. ABSOLUTE_PRICE means the that the value of value field is a final stop value. * RELATIVE_* means that the value field value contains relative stop expressed either in price, points, pips, account * currency or balance percentage. Default is ABSOLUTE_PRICE. Allowed values are ABSOLUTE_PRICE, RELATIVE_PRICE, * RELATIVE_POINTS, RELATIVE_PIPS, RELATIVE_CURRENCY, RELATIVE_BALANCE_PERCENTAGE */ /** * Creates a market buy order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {CreateMarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createMarketBuyOrder(symbol, volume, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_BUY", symbol, volume }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a market sell order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {CreateMarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createMarketSellOrder(symbol, volume, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_SELL", symbol, volume }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a limit buy order * @param {String} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order limit price * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {PendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createLimitBuyOrder(symbol, volume, openPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_BUY_LIMIT", symbol, volume, openPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a limit sell order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order limit price * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {PendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createLimitSellOrder(symbol, volume, openPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_SELL_LIMIT", symbol, volume, openPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a stop buy order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order stop price * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {PendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createStopBuyOrder(symbol, volume, openPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_BUY_STOP", symbol, volume, openPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a stop sell order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order stop price * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {PendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createStopSellOrder(symbol, volume, openPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_SELL_STOP", symbol, volume, openPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a stop limit buy order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order stop price * @param {number} stopLimitPrice the limit order price for the stop limit order * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {StopLimitPendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createStopLimitBuyOrder(symbol, volume, openPrice, stopLimitPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_BUY_STOP_LIMIT", symbol, volume, openPrice, stopLimitPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Creates a stop limit sell order * @param {string} symbol symbol to trade * @param {number} volume order volume * @param {number} openPrice order stop price * @param {number} stopLimitPrice the limit order price for the stop limit order * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {StopLimitPendingTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ createStopLimitSellOrder(symbol, volume, openPrice, stopLimitPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_TYPE_SELL_STOP_LIMIT", symbol, volume, openPrice, stopLimitPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Modifies a position * @param {string} positionId position id to modify * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {TrailingStopLoss} [trailingStopLoss] distance trailing stop loss configuration * @param {String} [stopPriceBase] defines the base price to calculate SL relative to for POSITION_MODIFY and * pending order requests. Default is OPEN_PRICE. One of CURRENT_PRICE, OPEN_PRICE, STOP_PRICE * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ modifyPosition(positionId, stopLoss, takeProfit, trailingStopLoss, stopPriceBase) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "POSITION_MODIFY", positionId, trailingStopLoss, stopPriceBase }, this._generateStopOptions(stopLoss, takeProfit))); } /** * Partially closes a position * @param {string} positionId position id to modify * @param {number} volume volume to close * @param {MarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ closePositionPartially(positionId, volume, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "POSITION_PARTIAL", positionId, volume }, options || {})); } /** * Fully closes a position * @param {string} positionId position id to modify * @param {MarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ closePosition(positionId, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "POSITION_CLOSE_ID", positionId }, options || {})); } /** * Fully closes a position * @param {string} positionId position id to close by opposite position * @param {string} oppositePositionId opposite position id to close * @param {MarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ closeBy(positionId, oppositePositionId, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "POSITION_CLOSE_BY", positionId, closeByPositionId: oppositePositionId }, options || {})); } /** * Closes positions by a symbol * @param {string} symbol symbol to trade * @param {MarketTradeOptions} options optional trade options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ closePositionsBySymbol(symbol, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "POSITIONS_CLOSE_SYMBOL", symbol }, options || {})); } /** * Modifies a pending order * @param {string} orderId order id (ticket number) * @param {number} openPrice order stop price * @param {number|StopOptions} [stopLoss] stop loss price * @param {number|StopOptions} [takeProfit] take profit price * @param {ModifyOrderOptions} [options] optional modify order options * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ modifyOrder(orderId, openPrice, stopLoss, takeProfit, options = {}) { this._checkIsConnectionActive(); return this._trade(Object.assign({ actionType: "ORDER_MODIFY", orderId, openPrice }, this._generateStopOptions(stopLoss, takeProfit), options || {})); } /** * Cancels order * @param {string} orderId order id (ticket number) * @returns {Promise<TradeResponse>} promise resolving with trade result * @throws {TradeError} on trade error, check error properties for error code details */ cancelOrder(orderId) { this._checkIsConnectionActive(); return this._trade({ actionType: "ORDER_CANCEL", orderId }); } _trade(request) { return this._websocketClient.trade(this._metaApiConnection.account.id, request, this._metaApiConnection.application, this._metaApiConnection.account.reliability); } /** * Calculates margin required to open a trade on the specified trading account * @param {MarginOrder} order order to calculate margin for * @returns {Promise<Margin>} promise resolving with margin calculation result */ calculateMargin(order) { this._checkIsConnectionActive(); return this._websocketClient.calculateMargin(this._metaApiConnection.account.id, this._metaApiConnection.application, this._metaApiConnection.account.reliability, order); } /** * Forces refresh and retrieves latest quotes for a subset of symbols the terminal is subscribed to. Note, that this * method works separately from the streamed data (for streaming connection), so the response may be obsolete already, * if some updates happen during the request * @param {string[]} symbols quote symbols to refresh * @returns {Promise<RefreshedQuotes>} quotes that was actually updated (a subset of specified symbols), and some of * basic account information */ refreshSymbolQuotes(symbols) { this._checkIsConnectionActive(); return this._websocketClient.refreshSymbolQuotes(this._metaApiConnection.account.id, symbols); } /** * Returns MetaApi account * @return {MetatraderAccount} MetaApi account */ get account() { return this._metaApiConnection.account; } /** * Returns connection instance id * @return {String} connection instance id */ get instanceId() { return this._instanceId; } _generateStopOptions(stopLoss, takeProfit) { let trade = {}; if (typeof stopLoss === "number") { trade.stopLoss = stopLoss; } else if (stopLoss) { trade.stopLoss = stopLoss.value; trade.stopLossUnits = stopLoss.units; } if (typeof takeProfit === "number") { trade.takeProfit = takeProfit; } else if (takeProfit) { trade.takeProfit = takeProfit.value; trade.takeProfitUnits = takeProfit.units; } return trade; } _checkIsConnectionActive() { if (!this._opened) { throw new Error("This connection has not been initialized yet, please invoke await connection.connect()"); } if (this._closed) { throw new Error("This connection has been closed, please create a new connection"); } } /** * Constructs MetaApi MetaTrader Api connection instance * @param {MetaApiWebsocketClient} websocketClient MetaApi websocket client * @param {MetaApiConnection} metaApiConnection MetaApi connection to use */ constructor(websocketClient, metaApiConnection){ this._websocketClient = websocketClient; this._metaApiConnection = metaApiConnection; this._instanceId = _randomstring.default.generate(32); this._logger = _logger.default.getLogger("MetaApiConnectionInstance"); } }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBNZXRhQXBpV2Vic29ja2V0Q2xpZW50IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9tZXRhQXBpV2Vic29ja2V0LmNsaWVudCc7XG5pbXBvcnQge1N0b3BMaW1pdFBlbmRpbmdUcmFkZU9wdGlvbnMsIE1vZGlmeU9yZGVyT3B0aW9uc30gZnJvbSAnLi9tZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlJztcbmltcG9ydCBMb2dnZXJNYW5hZ2VyIGZyb20gJy4uL2xvZ2dlcic7XG5pbXBvcnQgcmFuZG9tc3RyaW5nIGZyb20gJ3JhbmRvbXN0cmluZyc7XG5cbi8qKlxuICogRXhwb3NlcyBNZXRhQXBpIE1ldGFUcmFkZXIgQVBJIGNvbm5lY3Rpb24gaW5zdGFuY2UgdG8gY29uc3VtZXJzXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2Uge1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIE1ldGFBcGkgTWV0YVRyYWRlciBBcGkgY29ubmVjdGlvbiBpbnN0YW5jZVxuICAgKiBAcGFyYW0ge01ldGFBcGlXZWJzb2NrZXRDbGllbnR9IHdlYnNvY2tldENsaWVudCBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnRcbiAgICogQHBhcmFtIHtNZXRhQXBpQ29ubmVjdGlvbn0gbWV0YUFwaUNvbm5lY3Rpb24gTWV0YUFwaSBjb25uZWN0aW9uIHRvIHVzZVxuICAgKi9cbiAgY29uc3RydWN0b3Iod2Vic29ja2V0Q2xpZW50LCBtZXRhQXBpQ29ubmVjdGlvbikge1xuICAgIHRoaXMuX3dlYnNvY2tldENsaWVudCA9IHdlYnNvY2tldENsaWVudDtcbiAgICB0aGlzLl9tZXRhQXBpQ29ubmVjdGlvbiA9IG1ldGFBcGlDb25uZWN0aW9uO1xuICAgIHRoaXMuX2luc3RhbmNlSWQgPSByYW5kb21zdHJpbmcuZ2VuZXJhdGUoMzIpO1xuICAgIHRoaXMuX2xvZ2dlciA9IExvZ2dlck1hbmFnZXIuZ2V0TG9nZ2VyKCdNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlJyk7XG4gIH1cblxuICAvKipcbiAgICogT3BlbnMgdGhlIGNvbm5lY3Rpb24uIENhbiBvbmx5IGJlIGNhbGxlZCB0aGUgZmlyc3QgdGltZSwgbmV4dCBjYWxscyB3aWxsIGJlIGlnbm9yZWQuXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gdGhlIGNvbm5lY3Rpb24gaXMgb3BlbmVkXG4gICAqL1xuICBhc3luYyBjb25uZWN0KCkge1xuICAgIHRoaXMuX29wZW5lZCA9IHRydWU7XG4gIH1cblxuICAvKipcbiAgICogQ2xvc2VzIHRoZSBjb25uZWN0aW9uLiBUaGUgaW5zdGFuY2Ugb2YgdGhlIGNsYXNzIHNob3VsZCBubyBsb25nZXIgYmUgdXNlZCBhZnRlciB0aGlzIG1ldGhvZCBpcyBpbnZva2VkLlxuICAgKi9cbiAgYXN5bmMgY2xvc2UoKSB7XG4gICAgdGhpcy5fb3BlbmVkID0gZmFsc2U7XG4gICAgdGhpcy5fY2xvc2VkID0gdHJ1ZTtcbiAgfVxuICBcbiAgLyoqXG4gICAqIENvbW1vbiB0cmFkZSBvcHRpb25zXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFRyYWRlT3B0aW9uc1xuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW2NvbW1lbnRdIG9wdGlvbmFsIG9yZGVyIGNvbW1lbnQuIFRoZSBzdW0gb2YgdGhlIGxpbmUgbGVuZ3RocyBvZiB0aGUgY29tbWVudCBhbmQgdGhlXG4gICAqIGNsaWVudElkIG11c3QgYmUgbGVzcyB0aGFuIG9yIGVxdWFsIHRvIDI2LiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWVcbiAgICogaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3MvY2xpZW50L2NsaWVudElkVXNhZ2UvXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbY2xpZW50SWRdIG9wdGlvbmFsIGNsaWVudC1hc3NpZ25lZCBpZC4gVGhlIGlkIHZhbHVlIGNhbiBiZSBhc3NpZ25lZCB3aGVuIHN1Ym1pdHRpbmcgYSB0cmFkZSBhbmRcbiAgICogd2lsbCBiZSBwcmVzZW50IG9uIHBvc2l0aW9uLCBoaXN0b3J5IG9yZGVycyBhbmQgaGlzdG9yeSBkZWFscyByZWxhdGVkIHRvIHRoZSB0cmFkZS4gWW91IGNhbiB1c2UgdGhpcyBmaWVsZCB0byBiaW5kXG4gICAqIHlvdXIgdHJhZGVzIHRvIG9iamVjdHMgaW4geW91ciBhcHBsaWNhdGlvbiBhbmQgdGhlbiB0cmFjayB0cmFkZSBwcm9ncmVzcy4gVGhlIHN1bSBvZiB0aGUgbGluZSBsZW5ndGhzIG9mIHRoZVxuICAgKiBjb21tZW50IGFuZCB0aGUgY2xpZW50SWQgbXVzdCBiZSBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMjYuIEZvciBtb3JlIGluZm9ybWF0aW9uIHNlZVxuICAgKiBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9jbGllbnQvY2xpZW50SWRVc2FnZS9cbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFttYWdpY10gb3B0aW9uYWwgbWFnaWMgKGV4cGVydCBpZCkgbnVtYmVyLiBJZiBub3Qgc2V0IGRlZmF1bHQgdmFsdWUgc3BlY2lmaWVkIGluIGFjY291bnQgZW50aXR5XG4gICAqIHdpbGwgYmUgdXNlZC5cbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFtzbGlwcGFnZV0gb3B0aW9uYWwgc2xpcHBhZ2UgaW4gcG9pbnRzLiBTaG91bGQgYmUgZ3JlYXRlciBvciBlcXVhbCB0byB6ZXJvLiBJbiBub3Qgc2V0LFxuICAgKiBkZWZhdWx0IHZhbHVlIHNwZWNpZmllZCBpbiBhY2NvdW50IGVudGl0eSB3aWxsIGJlIHVzZWQuIFNsaXBwYWdlIGlzIGlnbm9yZWQgaWYgZXhlY3V0aW9uIG1vZGUgc2V0IHRvXG4gICAqIFNZTUJPTF9UUkFERV9FWEVDVVRJT05fTUFSS0VUIGluIHN5bWJvbCBzcGVjaWZpY2F0aW9uLiBOb3QgdXNlZCBmb3IgY2xvc2UgYnkgb3JkZXJzLlxuICAgKi9cblxuICAvKipcbiAgICogTWFya2V0IHRyYWRlIG9wdGlvbnNcbiAgICogQHR5cGVkZWYge1RyYWRlT3B0aW9uc30gTWFya2V0VHJhZGVPcHRpb25zXG4gICAqIEBwcm9wZXJ0eSB7QXJyYXk8U3RyaW5nPn0gW2ZpbGxpbmdNb2Rlc10gb3B0aW9uYWwgYWxsb3dlZCBmaWxsaW5nIG1vZGVzIGluIHRoZSBvcmRlciBvZiBwcmlvcml0eS4gRGVmYXVsdCBpcyB0b1xuICAgKiBhbGxvdyBhbGwgZmlsbGluZyBtb2RlcyBhbmQgcHJlZmVyIE9SREVSX0ZJTExJTkdfRk9LIG92ZXIgT1JERVJfRklMTElOR19JT0MuIFNlZVxuICAgKiBodHRwczovL3d3dy5tcWw1LmNvbS9lbi9kb2NzL2NvbnN0YW50cy90cmFkaW5nY29uc3RhbnRzL29yZGVycHJvcGVydGllcyNlbnVtX29yZGVyX3R5cGVfZmlsbGluZyBmb3IgZXh0cmFcbiAgICogZXhwbGFuYXRpb25cbiAgICovXG5cbiAgLyoqXG4gICAqIE1hcmtldCB0cmFkZSBvcHRpb25zXG4gICAqIEB0eXBlZGVmIHtNYXJrZXRUcmFkZU9wdGlvbnN9IENyZWF0ZU1hcmtldFRyYWRlT3B0aW9uc1xuICAgKiBAcHJvcGVydHkge1RyYWlsaW5nU3RvcExvc3N9IFt0cmFpbGluZ1N0b3BMb3NzXSBkaXN0YW5jZSB0cmFpbGluZyBzdG9wIGxvc3MgY29uZmlndXJhdGlvblxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW3N0b3BQcmljZUJhc2VdIGRlZmluZXMgdGhlIGJhc2UgcHJpY2UgdG8gY2FsY3VsYXRlIFNML1RQIHJlbGF0aXZlIHRvIGZvciBwZW5kaW5nIG9yZGVyXG4gICAqIHJlcXVlc3RzLiBEZWZhdWx0IGlzIENVUlJFTlRfUFJJQ0UsIG9uZSBvZiBDVVJSRU5UX1BSSUNFXG4gICAqL1xuXG4gIC8qKlxuICAgKiBQZW5kaW5nIG9yZGVyIHRyYWRlIG9wdGlvbnNcbiAgICogQHR5cGVkZWYge1RyYWRlT3B0aW9uc30gUGVuZGluZ1RyYWRlT3B0aW9uc1xuICAgKiBAcHJvcGVydHkge0V4cGlyYXRpb25PcHRpb25zfSBbZXhwaXJhdGlvbl0gb3B0aW9uYWwgcGVuZGluZyBvcmRlciBleHBpcmF0aW9uIHNldHRpbmdzLiBTZWUgUGVuZGluZyBvcmRlciBleHBpcmF0aW9uXG4gICAqIHNldHRpbmdzIHNlY3Rpb25cbiAgICogQHByb3BlcnR5IHtUcmFpbGluZ1N0b3BMb3NzfSBbdHJhaWxpbmdTdG9wTG9zc10gZGlzdGFuY2UgdHJhaWxpbmcgc3RvcCBsb3NzIGNvbmZpZ3VyYXRpb25cbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IFtzdG9wUHJpY2VCYXNlXSBkZWZpbmVkIHRoZSBiYXNlIHByaWNlIHRvIGNhbGN1bGF0ZSBTTC9UUCByZWxhdGl2ZSB0byBmb3IgKl9NT0RJRlkgYW5kIHBlbmRpbmdcbiAgICogb3JkZXIgcmVxdWVzdHMuIFNUT1BfUFJJQ0UgbWVhbnMgdGhlIFNML1RQIGlzIHJlbGF0aXZlIHRvIHByZXZpb3VzIFNML1RQIHZhbHVlLiBEZWZhdWx0IGlzIE9QRU5fUFJJQ0UsIG9uZSBvZlxuICAgKiBDVVJSRU5UX1BSSUNFLCBPUEVOX1BSSUNFXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbb3BlblByaWNlVW5pdHNdIG9wZW4gcHJpY2UgdW5pdHMuIEFCU09MVVRFX1BSSUNFIG1lYW5zIHRoZSB0aGF0IHRoZSB2YWx1ZSBvZiBvcGVuUHJpY2UgZmllbGRcbiAgICogaXMgYSBmaW5hbCBvcGVuIHByaWNlIHZhbHVlLiBSRUxBVElWRSogbWVhbnMgdGhhdCB0aGUgb3BlblByaWNlIGZpZWxkIHZhbHVlIGNvbnRhaW5zIHJlbGF0aXZlIG9wZW4gcHJpY2UgZXhwcmVzc2VkXG4gICAqIGVpdGhlciBpbiBwcmljZSwgcG9pbnRzLCBwaXBzLCBhY2NvdW50IGN1cnJlbmN5IG9yIGJhbGFuY2UgcGVyY2VudGFnZS4gRGVmYXVsdCBpcyBBQlNPTFVURV9QUklDRS4gT25lIG9mXG4gICAqIEFCU09MVVRFX1BSSUNFLCBSRUxBVElWRV9QUklDRSwgUkVMQVRJVkVfUE9JTlRTLCBSRUxBVElWRV9QSVBTLCBSRUxBVElWRV9DVVJSRU5DWSwgUkVMQVRJVkVfQkFMQU5DRV9QRVJDRU5UQUdFXG4gICAqL1xuXG4gIC8qKlxuICAgKiBTdG9wIG9wdGlvbnNcbiAgICogQHR5cGVkZWYge09iamVjdH0gU3RvcE9wdGlvbnNcbiAgICogQHByb3BlcnR5IHtudW1iZXJ9IHZhbHVlIHN0b3AgKFNMIG9yIFRQKSB2YWx1ZVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gdW5pdHMgc3RvcCB1bml0cy4gQUJTT0xVVEVfUFJJQ0UgbWVhbnMgdGhlIHRoYXQgdGhlIHZhbHVlIG9mIHZhbHVlIGZpZWxkIGlzIGEgZmluYWwgc3RvcCB2YWx1ZS5cbiAgICogUkVMQVRJVkVfKiBtZWFucyB0aGF0IHRoZSB2YWx1ZSBmaWVsZCB2YWx1ZSBjb250YWlucyByZWxhdGl2ZSBzdG9wIGV4cHJlc3NlZCBlaXRoZXIgaW4gcHJpY2UsIHBvaW50cywgcGlwcywgYWNjb3VudFxuICAgKiBjdXJyZW5jeSBvciBiYWxhbmNlIHBlcmNlbnRhZ2UuIERlZmF1bHQgaXMgQUJTT0xVVEVfUFJJQ0UuIEFsbG93ZWQgdmFsdWVzIGFyZSBBQlNPTFVURV9QUklDRSwgUkVMQVRJVkVfUFJJQ0UsXG4gICAqIFJFTEFUSVZFX1BPSU5UUywgUkVMQVRJVkVfUElQUywgUkVMQVRJVkVfQ1VSUkVOQ1ksIFJFTEFUSVZFX0JBTEFOQ0VfUEVSQ0VOVEFHRVxuICAgKi9cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIG1hcmtldCBidXkgb3JkZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgdG8gdHJhZGVcbiAgICogQHBhcmFtIHtudW1iZXJ9IHZvbHVtZSBvcmRlciB2b2x1bWVcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFtzdG9wTG9zc10gc3RvcCBsb3NzIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbdGFrZVByb2ZpdF0gdGFrZSBwcm9maXQgcHJpY2VcbiAgICogQHBhcmFtIHtDcmVhdGVNYXJrZXRUcmFkZU9wdGlvbnN9IG9wdGlvbnMgb3B0aW9uYWwgdHJhZGUgb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUcmFkZVJlc3BvbnNlPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCB0cmFkZSByZXN1bHRcbiAgICogQHRocm93cyB7VHJhZGVFcnJvcn0gb24gdHJhZGUgZXJyb3IsIGNoZWNrIGVycm9yIHByb3BlcnRpZXMgZm9yIGVycm9yIGNvZGUgZGV0YWlsc1xuICAgKi9cbiAgY3JlYXRlTWFya2V0QnV5T3JkZXIoc3ltYm9sLCB2b2x1bWUsIHN0b3BMb3NzLCB0YWtlUHJvZml0LCBvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZShPYmplY3QuYXNzaWduKHthY3Rpb25UeXBlOiAnT1JERVJfVFlQRV9CVVknLCBzeW1ib2wsIHZvbHVtZX0sXG4gICAgICB0aGlzLl9nZW5lcmF0ZVN0b3BPcHRpb25zKHN0b3BMb3NzLCB0YWtlUHJvZml0KSwgb3B0aW9ucyB8fCB7fSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBtYXJrZXQgc2VsbCBvcmRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byB0cmFkZVxuICAgKiBAcGFyYW0ge251bWJlcn0gdm9sdW1lIG9yZGVyIHZvbHVtZVxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3N0b3BMb3NzXSBzdG9wIGxvc3MgcHJpY2VcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFt0YWtlUHJvZml0XSB0YWtlIHByb2ZpdCBwcmljZVxuICAgKiBAcGFyYW0ge0NyZWF0ZU1hcmtldFRyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjcmVhdGVNYXJrZXRTZWxsT3JkZXIoc3ltYm9sLCB2b2x1bWUsIHN0b3BMb3NzLCB0YWtlUHJvZml0LCBvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZShPYmplY3QuYXNzaWduKHthY3Rpb25UeXBlOiAnT1JERVJfVFlQRV9TRUxMJywgc3ltYm9sLCB2b2x1bWV9LFxuICAgICAgdGhpcy5fZ2VuZXJhdGVTdG9wT3B0aW9ucyhzdG9wTG9zcywgdGFrZVByb2ZpdCksIG9wdGlvbnMgfHwge30pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbGltaXQgYnV5IG9yZGVyXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzeW1ib2wgc3ltYm9sIHRvIHRyYWRlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB2b2x1bWUgb3JkZXIgdm9sdW1lXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBvcGVuUHJpY2Ugb3JkZXIgbGltaXQgcHJpY2VcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFtzdG9wTG9zc10gc3RvcCBsb3NzIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbdGFrZVByb2ZpdF0gdGFrZSBwcm9maXQgcHJpY2VcbiAgICogQHBhcmFtIHtQZW5kaW5nVHJhZGVPcHRpb25zfSBvcHRpb25zIG9wdGlvbmFsIHRyYWRlIG9wdGlvbnNcbiAgICogQHJldHVybnMge1Byb21pc2U8VHJhZGVSZXNwb25zZT59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggdHJhZGUgcmVzdWx0XG4gICAqIEB0aHJvd3Mge1RyYWRlRXJyb3J9IG9uIHRyYWRlIGVycm9yLCBjaGVjayBlcnJvciBwcm9wZXJ0aWVzIGZvciBlcnJvciBjb2RlIGRldGFpbHNcbiAgICovXG4gIGNyZWF0ZUxpbWl0QnV5T3JkZXIoc3ltYm9sLCB2b2x1bWUsIG9wZW5QcmljZSwgc3RvcExvc3MsIHRha2VQcm9maXQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdPUkRFUl9UWVBFX0JVWV9MSU1JVCcsIHN5bWJvbCxcbiAgICAgIHZvbHVtZSwgb3BlblByaWNlfSwgdGhpcy5fZ2VuZXJhdGVTdG9wT3B0aW9ucyhzdG9wTG9zcywgdGFrZVByb2ZpdCksIG9wdGlvbnMgfHwge30pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgbGltaXQgc2VsbCBvcmRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byB0cmFkZVxuICAgKiBAcGFyYW0ge251bWJlcn0gdm9sdW1lIG9yZGVyIHZvbHVtZVxuICAgKiBAcGFyYW0ge251bWJlcn0gb3BlblByaWNlIG9yZGVyIGxpbWl0IHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbc3RvcExvc3NdIHN0b3AgbG9zcyBwcmljZVxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3Rha2VQcm9maXRdIHRha2UgcHJvZml0IHByaWNlXG4gICAqIEBwYXJhbSB7UGVuZGluZ1RyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjcmVhdGVMaW1pdFNlbGxPcmRlcihzeW1ib2wsIHZvbHVtZSwgb3BlblByaWNlLCBzdG9wTG9zcywgdGFrZVByb2ZpdCwgb3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5fY2hlY2tJc0Nvbm5lY3Rpb25BY3RpdmUoKTtcbiAgICByZXR1cm4gdGhpcy5fdHJhZGUoT2JqZWN0LmFzc2lnbih7YWN0aW9uVHlwZTogJ09SREVSX1RZUEVfU0VMTF9MSU1JVCcsIHN5bWJvbCxcbiAgICAgIHZvbHVtZSwgb3BlblByaWNlfSwgdGhpcy5fZ2VuZXJhdGVTdG9wT3B0aW9ucyhzdG9wTG9zcywgdGFrZVByb2ZpdCksIG9wdGlvbnMgfHwge30pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc3RvcCBidXkgb3JkZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgdG8gdHJhZGVcbiAgICogQHBhcmFtIHtudW1iZXJ9IHZvbHVtZSBvcmRlciB2b2x1bWVcbiAgICogQHBhcmFtIHtudW1iZXJ9IG9wZW5QcmljZSBvcmRlciBzdG9wIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbc3RvcExvc3NdIHN0b3AgbG9zcyBwcmljZVxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3Rha2VQcm9maXRdIHRha2UgcHJvZml0IHByaWNlXG4gICAqIEBwYXJhbSB7UGVuZGluZ1RyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjcmVhdGVTdG9wQnV5T3JkZXIoc3ltYm9sLCB2b2x1bWUsIG9wZW5QcmljZSwgc3RvcExvc3MsIHRha2VQcm9maXQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdPUkRFUl9UWVBFX0JVWV9TVE9QJywgc3ltYm9sLFxuICAgICAgdm9sdW1lLCBvcGVuUHJpY2V9LCB0aGlzLl9nZW5lcmF0ZVN0b3BPcHRpb25zKHN0b3BMb3NzLCB0YWtlUHJvZml0KSwgb3B0aW9ucyB8fCB7fSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBzdG9wIHNlbGwgb3JkZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgdG8gdHJhZGVcbiAgICogQHBhcmFtIHtudW1iZXJ9IHZvbHVtZSBvcmRlciB2b2x1bWVcbiAgICogQHBhcmFtIHtudW1iZXJ9IG9wZW5QcmljZSBvcmRlciBzdG9wIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbc3RvcExvc3NdIHN0b3AgbG9zcyBwcmljZVxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3Rha2VQcm9maXRdIHRha2UgcHJvZml0IHByaWNlXG4gICAqIEBwYXJhbSB7UGVuZGluZ1RyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjcmVhdGVTdG9wU2VsbE9yZGVyKHN5bWJvbCwgdm9sdW1lLCBvcGVuUHJpY2UsIHN0b3BMb3NzLCB0YWtlUHJvZml0LCBvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZShPYmplY3QuYXNzaWduKHthY3Rpb25UeXBlOiAnT1JERVJfVFlQRV9TRUxMX1NUT1AnLCBzeW1ib2wsXG4gICAgICB2b2x1bWUsIG9wZW5QcmljZX0sIHRoaXMuX2dlbmVyYXRlU3RvcE9wdGlvbnMoc3RvcExvc3MsIHRha2VQcm9maXQpLCBvcHRpb25zIHx8IHt9KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHN0b3AgbGltaXQgYnV5IG9yZGVyXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzeW1ib2wgc3ltYm9sIHRvIHRyYWRlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB2b2x1bWUgb3JkZXIgdm9sdW1lXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBvcGVuUHJpY2Ugb3JkZXIgc3RvcCBwcmljZVxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RvcExpbWl0UHJpY2UgdGhlIGxpbWl0IG9yZGVyIHByaWNlIGZvciB0aGUgc3RvcCBsaW1pdCBvcmRlclxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3N0b3BMb3NzXSBzdG9wIGxvc3MgcHJpY2VcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFt0YWtlUHJvZml0XSB0YWtlIHByb2ZpdCBwcmljZVxuICAgKiBAcGFyYW0ge1N0b3BMaW1pdFBlbmRpbmdUcmFkZU9wdGlvbnN9IG9wdGlvbnMgb3B0aW9uYWwgdHJhZGUgb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUcmFkZVJlc3BvbnNlPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCB0cmFkZSByZXN1bHRcbiAgICogQHRocm93cyB7VHJhZGVFcnJvcn0gb24gdHJhZGUgZXJyb3IsIGNoZWNrIGVycm9yIHByb3BlcnRpZXMgZm9yIGVycm9yIGNvZGUgZGV0YWlsc1xuICAgKi9cbiAgY3JlYXRlU3RvcExpbWl0QnV5T3JkZXIoc3ltYm9sLCB2b2x1bWUsIG9wZW5QcmljZSwgc3RvcExpbWl0UHJpY2UsIHN0b3BMb3NzLCB0YWtlUHJvZml0LCBvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZShPYmplY3QuYXNzaWduKHthY3Rpb25UeXBlOiAnT1JERVJfVFlQRV9CVVlfU1RPUF9MSU1JVCcsXG4gICAgICBzeW1ib2wsIHZvbHVtZSwgb3BlblByaWNlLCBzdG9wTGltaXRQcmljZX0sIHRoaXMuX2dlbmVyYXRlU3RvcE9wdGlvbnMoc3RvcExvc3MsIHRha2VQcm9maXQpLCBvcHRpb25zIHx8IHt9KSk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHN0b3AgbGltaXQgc2VsbCBvcmRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3ltYm9sIHN5bWJvbCB0byB0cmFkZVxuICAgKiBAcGFyYW0ge251bWJlcn0gdm9sdW1lIG9yZGVyIHZvbHVtZVxuICAgKiBAcGFyYW0ge251bWJlcn0gb3BlblByaWNlIG9yZGVyIHN0b3AgcHJpY2VcbiAgICogQHBhcmFtIHtudW1iZXJ9IHN0b3BMaW1pdFByaWNlIHRoZSBsaW1pdCBvcmRlciBwcmljZSBmb3IgdGhlIHN0b3AgbGltaXQgb3JkZXJcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFtzdG9wTG9zc10gc3RvcCBsb3NzIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbdGFrZVByb2ZpdF0gdGFrZSBwcm9maXQgcHJpY2VcbiAgICogQHBhcmFtIHtTdG9wTGltaXRQZW5kaW5nVHJhZGVPcHRpb25zfSBvcHRpb25zIG9wdGlvbmFsIHRyYWRlIG9wdGlvbnNcbiAgICogQHJldHVybnMge1Byb21pc2U8VHJhZGVSZXNwb25zZT59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggdHJhZGUgcmVzdWx0XG4gICAqIEB0aHJvd3Mge1RyYWRlRXJyb3J9IG9uIHRyYWRlIGVycm9yLCBjaGVjayBlcnJvciBwcm9wZXJ0aWVzIGZvciBlcnJvciBjb2RlIGRldGFpbHNcbiAgICovXG4gIGNyZWF0ZVN0b3BMaW1pdFNlbGxPcmRlcihzeW1ib2wsIHZvbHVtZSwgb3BlblByaWNlLCBzdG9wTGltaXRQcmljZSwgc3RvcExvc3MsIHRha2VQcm9maXQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdPUkRFUl9UWVBFX1NFTExfU1RPUF9MSU1JVCcsXG4gICAgICBzeW1ib2wsIHZvbHVtZSwgb3BlblByaWNlLCBzdG9wTGltaXRQcmljZX0sIHRoaXMuX2dlbmVyYXRlU3RvcE9wdGlvbnMoc3RvcExvc3MsIHRha2VQcm9maXQpLCBvcHRpb25zIHx8IHt9KSk7XG4gIH1cblxuICAvKipcbiAgICogTW9kaWZpZXMgYSBwb3NpdGlvblxuICAgKiBAcGFyYW0ge3N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZCB0byBtb2RpZnlcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFtzdG9wTG9zc10gc3RvcCBsb3NzIHByaWNlXG4gICAqIEBwYXJhbSB7bnVtYmVyfFN0b3BPcHRpb25zfSBbdGFrZVByb2ZpdF0gdGFrZSBwcm9maXQgcHJpY2VcbiAgICogQHBhcmFtIHtUcmFpbGluZ1N0b3BMb3NzfSBbdHJhaWxpbmdTdG9wTG9zc10gZGlzdGFuY2UgdHJhaWxpbmcgc3RvcCBsb3NzIGNvbmZpZ3VyYXRpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IFtzdG9wUHJpY2VCYXNlXSBkZWZpbmVzIHRoZSBiYXNlIHByaWNlIHRvIGNhbGN1bGF0ZSBTTCByZWxhdGl2ZSB0byBmb3IgUE9TSVRJT05fTU9ESUZZIGFuZFxuICAgKiBwZW5kaW5nIG9yZGVyIHJlcXVlc3RzLiBEZWZhdWx0IGlzIE9QRU5fUFJJQ0UuIE9uZSBvZiBDVVJSRU5UX1BSSUNFLCBPUEVOX1BSSUNFLCBTVE9QX1BSSUNFXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBtb2RpZnlQb3NpdGlvbihwb3NpdGlvbklkLCBzdG9wTG9zcywgdGFrZVByb2ZpdCwgdHJhaWxpbmdTdG9wTG9zcywgc3RvcFByaWNlQmFzZSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdQT1NJVElPTl9NT0RJRlknLCBwb3NpdGlvbklkLCB0cmFpbGluZ1N0b3BMb3NzLCBzdG9wUHJpY2VCYXNlfSxcbiAgICAgIHRoaXMuX2dlbmVyYXRlU3RvcE9wdGlvbnMoc3RvcExvc3MsIHRha2VQcm9maXQpKSk7XG4gIH1cblxuICAvKipcbiAgICogUGFydGlhbGx5IGNsb3NlcyBhIHBvc2l0aW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwb3NpdGlvbklkIHBvc2l0aW9uIGlkIHRvIG1vZGlmeVxuICAgKiBAcGFyYW0ge251bWJlcn0gdm9sdW1lIHZvbHVtZSB0byBjbG9zZVxuICAgKiBAcGFyYW0ge01hcmtldFRyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjbG9zZVBvc2l0aW9uUGFydGlhbGx5KHBvc2l0aW9uSWQsIHZvbHVtZSwgb3B0aW9ucyA9IHt9KSB7XG4gICAgdGhpcy5fY2hlY2tJc0Nvbm5lY3Rpb25BY3RpdmUoKTtcbiAgICByZXR1cm4gdGhpcy5fdHJhZGUoT2JqZWN0LmFzc2lnbih7YWN0aW9uVHlwZTogJ1BPU0lUSU9OX1BBUlRJQUwnLCBwb3NpdGlvbklkLFxuICAgICAgdm9sdW1lfSwgb3B0aW9ucyB8fCB7fSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZ1bGx5IGNsb3NlcyBhIHBvc2l0aW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwb3NpdGlvbklkIHBvc2l0aW9uIGlkIHRvIG1vZGlmeVxuICAgKiBAcGFyYW0ge01hcmtldFRyYWRlT3B0aW9uc30gb3B0aW9ucyBvcHRpb25hbCB0cmFkZSBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBjbG9zZVBvc2l0aW9uKHBvc2l0aW9uSWQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdQT1NJVElPTl9DTE9TRV9JRCcsIHBvc2l0aW9uSWR9LFxuICAgICAgb3B0aW9ucyB8fCB7fSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIEZ1bGx5IGNsb3NlcyBhIHBvc2l0aW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwb3NpdGlvbklkIHBvc2l0aW9uIGlkIHRvIGNsb3NlIGJ5IG9wcG9zaXRlIHBvc2l0aW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBvcHBvc2l0ZVBvc2l0aW9uSWQgb3Bwb3NpdGUgcG9zaXRpb24gaWQgdG8gY2xvc2VcbiAgICogQHBhcmFtIHtNYXJrZXRUcmFkZU9wdGlvbnN9IG9wdGlvbnMgb3B0aW9uYWwgdHJhZGUgb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUcmFkZVJlc3BvbnNlPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCB0cmFkZSByZXN1bHRcbiAgICogQHRocm93cyB7VHJhZGVFcnJvcn0gb24gdHJhZGUgZXJyb3IsIGNoZWNrIGVycm9yIHByb3BlcnRpZXMgZm9yIGVycm9yIGNvZGUgZGV0YWlsc1xuICAgKi9cbiAgY2xvc2VCeShwb3NpdGlvbklkLCBvcHBvc2l0ZVBvc2l0aW9uSWQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdQT1NJVElPTl9DTE9TRV9CWScsIHBvc2l0aW9uSWQsXG4gICAgICBjbG9zZUJ5UG9zaXRpb25JZDogb3Bwb3NpdGVQb3NpdGlvbklkfSwgb3B0aW9ucyB8fCB7fSkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyBwb3NpdGlvbnMgYnkgYSBzeW1ib2xcbiAgICogQHBhcmFtIHtzdHJpbmd9IHN5bWJvbCBzeW1ib2wgdG8gdHJhZGVcbiAgICogQHBhcmFtIHtNYXJrZXRUcmFkZU9wdGlvbnN9IG9wdGlvbnMgb3B0aW9uYWwgdHJhZGUgb3B0aW9uc1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxUcmFkZVJlc3BvbnNlPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCB0cmFkZSByZXN1bHRcbiAgICogQHRocm93cyB7VHJhZGVFcnJvcn0gb24gdHJhZGUgZXJyb3IsIGNoZWNrIGVycm9yIHByb3BlcnRpZXMgZm9yIGVycm9yIGNvZGUgZGV0YWlsc1xuICAgKi9cbiAgY2xvc2VQb3NpdGlvbnNCeVN5bWJvbChzeW1ib2wsIG9wdGlvbnMgPSB7fSkge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3RyYWRlKE9iamVjdC5hc3NpZ24oe2FjdGlvblR5cGU6ICdQT1NJVElPTlNfQ0xPU0VfU1lNQk9MJywgc3ltYm9sfSxcbiAgICAgIG9wdGlvbnMgfHwge30pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNb2RpZmllcyBhIHBlbmRpbmcgb3JkZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IG9yZGVySWQgb3JkZXIgaWQgKHRpY2tldCBudW1iZXIpXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBvcGVuUHJpY2Ugb3JkZXIgc3RvcCBwcmljZVxuICAgKiBAcGFyYW0ge251bWJlcnxTdG9wT3B0aW9uc30gW3N0b3BMb3NzXSBzdG9wIGxvc3MgcHJpY2VcbiAgICogQHBhcmFtIHtudW1iZXJ8U3RvcE9wdGlvbnN9IFt0YWtlUHJvZml0XSB0YWtlIHByb2ZpdCBwcmljZVxuICAgKiBAcGFyYW0ge01vZGlmeU9yZGVyT3B0aW9uc30gW29wdGlvbnNdIG9wdGlvbmFsIG1vZGlmeSBvcmRlciBvcHRpb25zXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFRyYWRlUmVzcG9uc2U+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHRyYWRlIHJlc3VsdFxuICAgKiBAdGhyb3dzIHtUcmFkZUVycm9yfSBvbiB0cmFkZSBlcnJvciwgY2hlY2sgZXJyb3IgcHJvcGVydGllcyBmb3IgZXJyb3IgY29kZSBkZXRhaWxzXG4gICAqL1xuICBtb2RpZnlPcmRlcihvcmRlcklkLCBvcGVuUHJpY2UsIHN0b3BMb3NzLCB0YWtlUHJvZml0LCBvcHRpb25zID0ge30pIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZShPYmplY3QuYXNzaWduKHthY3Rpb25UeXBlOiAnT1JERVJfTU9ESUZZJywgb3JkZXJJZCwgb3BlblByaWNlfSxcbiAgICAgIHRoaXMuX2dlbmVyYXRlU3RvcE9wdGlvbnMoc3RvcExvc3MsIHRha2VQcm9maXQpLCBvcHRpb25zIHx8IHt9KSk7XG4gIH1cblxuICAvKipcbiAgICogQ2FuY2VscyBvcmRlclxuICAgKiBAcGFyYW0ge3N0cmluZ30gb3JkZXJJZCBvcmRlciBpZCAodGlja2V0IG51bWJlcilcbiAgICogQHJldHVybnMge1Byb21pc2U8VHJhZGVSZXNwb25zZT59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggdHJhZGUgcmVzdWx0XG4gICAqIEB0aHJvd3Mge1RyYWRlRXJyb3J9IG9uIHRyYWRlIGVycm9yLCBjaGVjayBlcnJvciBwcm9wZXJ0aWVzIGZvciBlcnJvciBjb2RlIGRldGFpbHNcbiAgICovXG4gIGNhbmNlbE9yZGVyKG9yZGVySWQpIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl90cmFkZSh7YWN0aW9uVHlwZTogJ09SREVSX0NBTkNFTCcsIG9yZGVySWR9KTtcbiAgfVxuXG4gIF90cmFkZShyZXF1ZXN0KSB7XG4gICAgcmV0dXJuIHRoaXMuX3dlYnNvY2tldENsaWVudC50cmFkZSh0aGlzLl9tZXRhQXBpQ29ubmVjdGlvbi5hY2NvdW50LmlkLCByZXF1ZXN0LFxuICAgICAgdGhpcy5fbWV0YUFwaUNvbm5lY3Rpb24uYXBwbGljYXRpb24sIHRoaXMuX21ldGFBcGlDb25uZWN0aW9uLmFjY291bnQucmVsaWFiaWxpdHkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgbWFyZ2luIHJlcXVpcmVkIHRvIG9wZW4gYSB0cmFkZSBvbiB0aGUgc3BlY2lmaWVkIHRyYWRpbmcgYWNjb3VudFxuICAgKiBAcGFyYW0ge01hcmdpbk9yZGVyfSBvcmRlciBvcmRlciB0byBjYWxjdWxhdGUgbWFyZ2luIGZvclxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxNYXJnaW4+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIG1hcmdpbiBjYWxjdWxhdGlvbiByZXN1bHRcbiAgICovXG4gIGNhbGN1bGF0ZU1hcmdpbihvcmRlcikge1xuICAgIHRoaXMuX2NoZWNrSXNDb25uZWN0aW9uQWN0aXZlKCk7XG4gICAgcmV0dXJuIHRoaXMuX3dlYnNvY2tldENsaWVudC5jYWxjdWxhdGVNYXJnaW4odGhpcy5fbWV0YUFwaUNvbm5lY3Rpb24uYWNjb3VudC5pZCxcbiAgICAgIHRoaXMuX21ldGFBcGlDb25uZWN0aW9uLmFwcGxpY2F0aW9uLCB0aGlzLl9tZXRhQXBpQ29ubmVjdGlvbi5hY2NvdW50LnJlbGlhYmlsaXR5LCBvcmRlcik7XG4gIH1cblxuICAvKipcbiAgICogRm9yY2VzIHJlZnJlc2ggYW5kIHJldHJpZXZlcyBsYXRlc3QgcXVvdGVzIGZvciBhIHN1YnNldCBvZiBzeW1ib2xzIHRoZSB0ZXJtaW5hbCBpcyBzdWJzY3JpYmVkIHRvLiBOb3RlLCB0aGF0IHRoaXNcbiAgICogbWV0aG9kIHdvcmtzIHNlcGFyYXRlbHkgZnJvbSB0aGUgc3RyZWFtZWQgZGF0YSAoZm9yIHN0cmVhbWluZyBjb25uZWN0aW9uKSwgc28gdGhlIHJlc3BvbnNlIG1heSBiZSBvYnNvbGV0ZSBhbHJlYWR5LFxuICAgKiBpZiBzb21lIHVwZGF0ZXMgaGFwcGVuIGR1cmluZyB0aGUgcmVxdWVzdFxuICAgKiBAcGFyYW0ge3N0cmluZ1tdfSBzeW1ib2xzIHF1b3RlIHN5bWJvbHMgdG8gcmVmcmVzaFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZWZyZXNoZWRRdW90ZXM+fSBxdW90ZXMgdGhhdCB3YXMgYWN0dWFsbHkgdXBkYXRlZCAoYSBzdWJzZXQgb2Ygc3BlY2lmaWVkIHN5bWJvbHMpLCBhbmQgc29tZSBvZlxuICAgKiBiYXNpYyBhY2NvdW50IGluZm9ybWF0aW9uXG4gICAqL1xuICByZWZyZXNoU3ltYm9sUXVvdGVzKHN5bWJvbHMpIHtcbiAgICB0aGlzLl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpO1xuICAgIHJldHVybiB0aGlzLl93ZWJzb2NrZXRDbGllbnQucmVmcmVzaFN5bWJvbFF1b3Rlcyh0aGlzLl9tZXRhQXBpQ29ubmVjdGlvbi5hY2NvdW50LmlkLCBzeW1ib2xzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIE1ldGFBcGkgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudH0gTWV0YUFwaSBhY2NvdW50XG4gICAqL1xuICBnZXQgYWNjb3VudCgpIHtcbiAgICByZXR1cm4gdGhpcy5fbWV0YUFwaUNvbm5lY3Rpb24uYWNjb3VudDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGNvbm5lY3Rpb24gaW5zdGFuY2UgaWRcbiAgICogQHJldHVybiB7U3RyaW5nfSBjb25uZWN0aW9uIGluc3RhbmNlIGlkXG4gICAqL1xuICBnZXQgaW5zdGFuY2VJZCgpIHtcbiAgICByZXR1cm4gdGhpcy5faW5zdGFuY2VJZDtcbiAgfVxuXG4gIF9nZW5lcmF0ZVN0b3BPcHRpb25zKHN0b3BMb3NzLCB0YWtlUHJvZml0KSB7XG4gICAgbGV0IHRyYWRlID0ge307XG4gICAgaWYgKHR5cGVvZiBzdG9wTG9zcyA9PT0gJ251bWJlcicpIHtcbiAgICAgIHRyYWRlLnN0b3BMb3NzID0gc3RvcExvc3M7XG4gICAgfSBlbHNlIGlmIChzdG9wTG9zcykge1xuICAgICAgdHJhZGUuc3RvcExvc3MgPSBzdG9wTG9zcy52YWx1ZTtcbiAgICAgIHRyYWRlLnN0b3BMb3NzVW5pdHMgPSBzdG9wTG9zcy51bml0cztcbiAgICB9XG4gICAgaWYgKHR5cGVvZiB0YWtlUHJvZml0ID09PSAnbnVtYmVyJykge1xuICAgICAgdHJhZGUudGFrZVByb2ZpdCA9IHRha2VQcm9maXQ7XG4gICAgfSBlbHNlIGlmICh0YWtlUHJvZml0KSB7XG4gICAgICB0cmFkZS50YWtlUHJvZml0ID0gdGFrZVByb2ZpdC52YWx1ZTtcbiAgICAgIHRyYWRlLnRha2VQcm9maXRVbml0cyA9IHRha2VQcm9maXQudW5pdHM7XG4gICAgfVxuICAgIHJldHVybiB0cmFkZTtcbiAgfVxuXG4gIF9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSgpIHtcbiAgICBpZighdGhpcy5fb3BlbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1RoaXMgY29ubmVjdGlvbiBoYXMgbm90IGJlZW4gaW5pdGlhbGl6ZWQgeWV0LCBwbGVhc2UgaW52b2tlIGF3YWl0IGNvbm5lY3Rpb24uY29ubmVjdCgpJyk7XG4gICAgfVxuICAgIGlmKHRoaXMuX2Nsb3NlZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUaGlzIGNvbm5lY3Rpb24gaGFzIGJlZW4gY2xvc2VkLCBwbGVhc2UgY3JlYXRlIGEgbmV3IGNvbm5lY3Rpb24nKTtcbiAgICB9XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbIk1ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2UiLCJjb25uZWN0IiwiX29wZW5lZCIsImNsb3NlIiwiX2Nsb3NlZCIsImNyZWF0ZU1hcmtldEJ1eU9yZGVyIiwic3ltYm9sIiwidm9sdW1lIiwic3RvcExvc3MiLCJ0YWtlUHJvZml0Iiwib3B0aW9ucyIsIl9jaGVja0lzQ29ubmVjdGlvbkFjdGl2ZSIsIl90cmFkZSIsIk9iamVjdCIsImFzc2lnbiIsImFjdGlvblR5cGUiLCJfZ2VuZXJhdGVTdG9wT3B0aW9ucyIsImNyZWF0ZU1hcmtldFNlbGxPcmRlciIsImNyZWF0ZUxpbWl0QnV5T3JkZXIiLCJvcGVuUHJpY2UiLCJjcmVhdGVMaW1pdFNlbGxPcmRlciIsImNyZWF0ZVN0b3BCdXlPcmRlciIsImNyZWF0ZVN0b3BTZWxsT3JkZXIiLCJjcmVhdGVTdG9wTGltaXRCdXlPcmRlciIsInN0b3BMaW1pdFByaWNlIiwiY3JlYXRlU3RvcExpbWl0U2VsbE9yZGVyIiwibW9kaWZ5UG9zaXRpb24iLCJwb3NpdGlvbklkIiwidHJhaWxpbmdTdG9wTG9zcyIsInN0b3BQcmljZUJhc2UiLCJjbG9zZVBvc2l0aW9uUGFydGlhbGx5IiwiY2xvc2VQb3NpdGlvbiIsImNsb3NlQnkiLCJvcHBvc2l0ZVBvc2l0aW9uSWQiLCJjbG9zZUJ5UG9zaXRpb25JZCIsImNsb3NlUG9zaXRpb25zQnlTeW1ib2wiLCJtb2RpZnlPcmRlciIsIm9yZGVySWQiLCJjYW5jZWxPcmRlciIsInJlcXVlc3QiLCJfd2Vic29ja2V0Q2xpZW50IiwidHJhZGUiLCJfbWV0YUFwaUNvbm5lY3Rpb24iLCJhY2NvdW50IiwiaWQiLCJhcHBsaWNhdGlvbiIsInJlbGlhYmlsaXR5IiwiY2FsY3VsYXRlTWFyZ2luIiwib3JkZXIiLCJyZWZyZXNoU3ltYm9sUXVvdGVzIiwic3ltYm9scyIsImluc3RhbmNlSWQiLCJfaW5zdGFuY2VJZCIsInZhbHVlIiwic3RvcExvc3NVbml0cyIsInVuaXRzIiwidGFrZVByb2ZpdFVuaXRzIiwiRXJyb3IiLCJjb25zdHJ1Y3RvciIsIndlYnNvY2tldENsaWVudCIsIm1ldGFBcGlDb25uZWN0aW9uIiwicmFuZG9tc3RyaW5nIiwiZ2VuZXJhdGUiLCJfbG9nZ2VyIiwiTG9nZ2VyTWFuYWdlciIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7ZUFVcUJBOzs7K0RBTks7cUVBQ0Q7Ozs7OztBQUtWLElBQUEsQUFBTUEsNEJBQU4sTUFBTUE7SUFjbkI7OztHQUdDLEdBQ0QsTUFBTUMsVUFBVTtRQUNkLElBQUksQ0FBQ0MsT0FBTyxHQUFHO0lBQ2pCO0lBRUE7O0dBRUMsR0FDRCxNQUFNQyxRQUFRO1FBQ1osSUFBSSxDQUFDRCxPQUFPLEdBQUc7UUFDZixJQUFJLENBQUNFLE9BQU8sR0FBRztJQUNqQjtJQUVBOzs7Ozs7Ozs7Ozs7Ozs7O0dBZ0JDLEdBRUQ7Ozs7Ozs7R0FPQyxHQUVEOzs7Ozs7R0FNQyxHQUVEOzs7Ozs7Ozs7Ozs7O0dBYUMsR0FFRDs7Ozs7Ozs7R0FRQyxHQUVEOzs7Ozs7Ozs7R0FTQyxHQUNEQyxxQkFBcUJDLE1BQU0sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLEVBQUVDLFVBQVUsRUFBRUMsVUFBVSxDQUFDLENBQUMsRUFBRTtRQUN2RSxJQUFJLENBQUNDLHdCQUF3QjtRQUM3QixPQUFPLElBQUksQ0FBQ0MsTUFBTSxDQUFDQyxPQUFPQyxNQUFNLENBQUM7WUFBQ0MsWUFBWTtZQUFrQlQ7WUFBUUM7UUFBTSxHQUM1RSxJQUFJLENBQUNTLG9CQUFvQixDQUFDUixVQUFVQyxhQUFhQyxXQUFXLENBQUM7SUFDakU7SUFFQTs7Ozs7Ozs7O0dB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