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)

188 lines (187 loc) 27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return _default; } }); function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Class which orders the synchronization packets */ let PacketOrderer = class PacketOrderer { /** * Initializes the packet orderer */ start() { this._sequenceNumberByInstance = {}; this._lastSessionStartTimestamp = {}; this._packetsByInstance = {}; if (!this._outOfOrderInterval) { this._outOfOrderInterval = setInterval(()=>this._emitOutOfOrderEvents(), 1000); } } /** * Deinitialized the packet orderer */ stop() { clearInterval(this._outOfOrderInterval); } /** * Processes the packet and resolves in the order of packet sequence number * @param {Object} packet packet to process * @return {Array<Object>} ordered packets when the packets are ready to be processed in order */ // eslint-disable-next-line complexity restoreOrder(packet) { let instanceId = packet.accountId + ":" + (packet.instanceIndex || 0) + ":" + (packet.host || 0); if (packet.sequenceNumber === undefined) { return [ packet ]; } if (packet.type === "synchronizationStarted" && packet.synchronizationId && (!this._lastSessionStartTimestamp[instanceId] || this._lastSessionStartTimestamp[instanceId] < packet.sequenceTimestamp)) { // synchronization packet sequence just started this._isOutOfOrderEmitted[instanceId] = false; this._sequenceNumberByInstance[instanceId] = packet.sequenceNumber; this._lastSessionStartTimestamp[instanceId] = packet.sequenceTimestamp; this._packetsByInstance[instanceId] = (this._packetsByInstance[instanceId] || []).filter((waitPacket)=>waitPacket.packet.sequenceTimestamp >= packet.sequenceTimestamp); return [ packet ].concat(this._findNextPacketsFromWaitList(instanceId)); } else if (packet.sequenceTimestamp < this._lastSessionStartTimestamp[instanceId]) { // filter out previous packets return []; } else if (packet.sequenceNumber === this._sequenceNumberByInstance[instanceId]) { // let the duplicate s/n packet to pass through return [ packet ]; } else if (packet.sequenceNumber === this._sequenceNumberByInstance[instanceId] + 1) { // in-order packet was received this._sequenceNumberByInstance[instanceId]++; this._lastSessionStartTimestamp[instanceId] = packet.sequenceTimestamp || this._lastSessionStartTimestamp[instanceId]; return [ packet ].concat(this._findNextPacketsFromWaitList(instanceId)); } else { // out-of-order packet was received, add it to the wait list this._packetsByInstance[instanceId] = this._packetsByInstance[instanceId] || []; let waitList = this._packetsByInstance[instanceId]; waitList.push({ instanceId, accountId: packet.accountId, instanceIndex: packet.instanceIndex || 0, sequenceNumber: packet.sequenceNumber, packet: packet, receivedAt: new Date() }); waitList.sort((e1, e2)=>e1.sequenceNumber - e2.sequenceNumber); while(waitList.length > this._waitListSizeLimit){ waitList.shift(); } return []; } } /** * Resets state for instance id * @param {String} instanceId instance id to reset state for */ onStreamClosed(instanceId) { delete this._packetsByInstance[instanceId]; delete this._lastSessionStartTimestamp[instanceId]; delete this._sequenceNumberByInstance[instanceId]; } /** * Resets state for specified accounts on reconnect * @param {String[]} reconnectAccountIds reconnected account ids */ onReconnected(reconnectAccountIds) { Object.keys(this._packetsByInstance).forEach((instanceId)=>{ if (reconnectAccountIds.includes(this._getAccountIdFromInstance(instanceId))) { delete this._packetsByInstance[instanceId]; } }); Object.keys(this._lastSessionStartTimestamp).forEach((instanceId)=>{ if (reconnectAccountIds.includes(this._getAccountIdFromInstance(instanceId))) { delete this._lastSessionStartTimestamp[instanceId]; } }); Object.keys(this._sequenceNumberByInstance).forEach((instanceId)=>{ if (reconnectAccountIds.includes(this._getAccountIdFromInstance(instanceId))) { delete this._sequenceNumberByInstance[instanceId]; } }); } _getAccountIdFromInstance(instanceId) { return instanceId.split(":")[0]; } // eslint-disable-next-line complexity _findNextPacketsFromWaitList(instanceId) { let result = []; let waitList = this._packetsByInstance[instanceId] || []; while(waitList.length && ([ this._sequenceNumberByInstance[instanceId], this._sequenceNumberByInstance[instanceId] + 1 ].includes(waitList[0].sequenceNumber) || waitList[0].packet.sequenceTimestamp < this._lastSessionStartTimestamp[instanceId])){ if (waitList[0].packet.sequenceTimestamp >= this._lastSessionStartTimestamp[instanceId]) { result.push(waitList[0].packet); if (waitList[0].packet.sequenceNumber === this._sequenceNumberByInstance[instanceId] + 1) { this._sequenceNumberByInstance[instanceId]++; this._lastSessionStartTimestamp[instanceId] = waitList[0].packet.sequenceTimestamp || this._lastSessionStartTimestamp[instanceId]; } } waitList.splice(0, 1); } if (!waitList.length) { delete this._packetsByInstance[instanceId]; } return result; } _emitOutOfOrderEvents() { for (let waitList of Object.values(this._packetsByInstance)){ if (waitList.length && waitList[0].receivedAt.getTime() + this._orderingTimeoutInSeconds * 1000 < Date.now()) { const instanceId = waitList[0].instanceId; if (!this._isOutOfOrderEmitted[instanceId]) { this._isOutOfOrderEmitted[instanceId] = true; // Do not emit onOutOfOrderPacket for packets that come before synchronizationStarted if (this._sequenceNumberByInstance[instanceId] !== undefined) { this._outOfOrderListener.onOutOfOrderPacket(waitList[0].accountId, waitList[0].instanceIndex, this._sequenceNumberByInstance[instanceId] + 1, waitList[0].sequenceNumber, waitList[0].packet, waitList[0].receivedAt); } } } } } /** * Constructs the class * @param {Function} outOfOrderListener function which will receive out of order packet events * @param {Number} orderingTimeoutInSeconds packet ordering timeout */ constructor(outOfOrderListener, orderingTimeoutInSeconds){ _define_property(this, "_outOfOrderListener", void 0); _define_property(this, "_orderingTimeoutInSeconds", void 0); _define_property(this, "_isOutOfOrderEmitted", void 0); _define_property(this, "_waitListSizeLimit", void 0); _define_property(this, "_sequenceNumberByInstance", void 0); _define_property(this, "_lastSessionStartTimestamp", void 0); _define_property(this, "_packetsByInstance", void 0); _define_property(this, "_outOfOrderInterval", void 0); this._outOfOrderListener = outOfOrderListener; this._orderingTimeoutInSeconds = orderingTimeoutInSeconds; this._isOutOfOrderEmitted = {}; this._waitListSizeLimit = 100; this._sequenceNumberByInstance = {}; this._lastSessionStartTimestamp = {}; this._packetsByInstance = {}; } }; const _default = PacketOrderer; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbi8qKlxuICogQ2xhc3Mgd2hpY2ggb3JkZXJzIHRoZSBzeW5jaHJvbml6YXRpb24gcGFja2V0c1xuICovXG5jbGFzcyBQYWNrZXRPcmRlcmVyIHtcbiAgXG4gIHByaXZhdGUgX291dE9mT3JkZXJMaXN0ZW5lcjogYW55O1xuICBwcml2YXRlIF9vcmRlcmluZ1RpbWVvdXRJblNlY29uZHM6IGFueTtcbiAgcHJpdmF0ZSBfaXNPdXRPZk9yZGVyRW1pdHRlZDoge307XG4gIHByaXZhdGUgX3dhaXRMaXN0U2l6ZUxpbWl0OiBudW1iZXI7XG4gIHByaXZhdGUgX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZToge307XG4gIHByaXZhdGUgX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXA6IHt9O1xuICBwcml2YXRlIF9wYWNrZXRzQnlJbnN0YW5jZToge307XG4gIHByaXZhdGUgX291dE9mT3JkZXJJbnRlcnZhbDogYW55O1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIHRoZSBjbGFzc1xuICAgKiBAcGFyYW0ge0Z1bmN0aW9ufSBvdXRPZk9yZGVyTGlzdGVuZXIgZnVuY3Rpb24gd2hpY2ggd2lsbCByZWNlaXZlIG91dCBvZiBvcmRlciBwYWNrZXQgZXZlbnRzXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBvcmRlcmluZ1RpbWVvdXRJblNlY29uZHMgcGFja2V0IG9yZGVyaW5nIHRpbWVvdXRcbiAgICovXG4gIGNvbnN0cnVjdG9yKG91dE9mT3JkZXJMaXN0ZW5lciwgb3JkZXJpbmdUaW1lb3V0SW5TZWNvbmRzKSB7XG4gICAgdGhpcy5fb3V0T2ZPcmRlckxpc3RlbmVyID0gb3V0T2ZPcmRlckxpc3RlbmVyO1xuICAgIHRoaXMuX29yZGVyaW5nVGltZW91dEluU2Vjb25kcyA9IG9yZGVyaW5nVGltZW91dEluU2Vjb25kcztcbiAgICB0aGlzLl9pc091dE9mT3JkZXJFbWl0dGVkID0ge307XG4gICAgdGhpcy5fd2FpdExpc3RTaXplTGltaXQgPSAxMDA7XG4gICAgdGhpcy5fc2VxdWVuY2VOdW1iZXJCeUluc3RhbmNlID0ge307XG4gICAgdGhpcy5fbGFzdFNlc3Npb25TdGFydFRpbWVzdGFtcCA9IHt9O1xuICAgIHRoaXMuX3BhY2tldHNCeUluc3RhbmNlID0ge307XG4gIH1cblxuICAvKipcbiAgICogSW5pdGlhbGl6ZXMgdGhlIHBhY2tldCBvcmRlcmVyXG4gICAqL1xuICBzdGFydCgpIHtcbiAgICB0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2UgPSB7fTtcbiAgICB0aGlzLl9sYXN0U2Vzc2lvblN0YXJ0VGltZXN0YW1wID0ge307XG4gICAgdGhpcy5fcGFja2V0c0J5SW5zdGFuY2UgPSB7fTtcbiAgICBpZiAoIXRoaXMuX291dE9mT3JkZXJJbnRlcnZhbCkge1xuICAgICAgdGhpcy5fb3V0T2ZPcmRlckludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4gdGhpcy5fZW1pdE91dE9mT3JkZXJFdmVudHMoKSwgMTAwMCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIERlaW5pdGlhbGl6ZWQgdGhlIHBhY2tldCBvcmRlcmVyXG4gICAqL1xuICBzdG9wKCkge1xuICAgIGNsZWFySW50ZXJ2YWwodGhpcy5fb3V0T2ZPcmRlckludGVydmFsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm9jZXNzZXMgdGhlIHBhY2tldCBhbmQgcmVzb2x2ZXMgaW4gdGhlIG9yZGVyIG9mIHBhY2tldCBzZXF1ZW5jZSBudW1iZXJcbiAgICogQHBhcmFtIHtPYmplY3R9IHBhY2tldCBwYWNrZXQgdG8gcHJvY2Vzc1xuICAgKiBAcmV0dXJuIHtBcnJheTxPYmplY3Q+fSBvcmRlcmVkIHBhY2tldHMgd2hlbiB0aGUgcGFja2V0cyBhcmUgcmVhZHkgdG8gYmUgcHJvY2Vzc2VkIGluIG9yZGVyXG4gICAqL1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICByZXN0b3JlT3JkZXI8VCBleHRlbmRzIFBhY2tldE9yZGVyZXIuUGFja2V0PihwYWNrZXQ6IFQpOiBQYWNrZXRPcmRlcmVyLlBhY2tldFtdIHtcbiAgICBsZXQgaW5zdGFuY2VJZCA9IHBhY2tldC5hY2NvdW50SWQgKyAnOicgKyAocGFja2V0Lmluc3RhbmNlSW5kZXggfHwgMCkgKyAnOicgKyAocGFja2V0Lmhvc3QgfHwgMCk7XG4gICAgaWYgKHBhY2tldC5zZXF1ZW5jZU51bWJlciA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gW3BhY2tldF07XG4gICAgfVxuICAgIGlmIChwYWNrZXQudHlwZSA9PT0gJ3N5bmNocm9uaXphdGlvblN0YXJ0ZWQnICYmIHBhY2tldC5zeW5jaHJvbml6YXRpb25JZCAmJlxuICAgICAgKCF0aGlzLl9sYXN0U2Vzc2lvblN0YXJ0VGltZXN0YW1wW2luc3RhbmNlSWRdIHx8IHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF0gPFxuICAgICAgICBwYWNrZXQuc2VxdWVuY2VUaW1lc3RhbXApKSB7XG4gICAgICAvLyBzeW5jaHJvbml6YXRpb24gcGFja2V0IHNlcXVlbmNlIGp1c3Qgc3RhcnRlZFxuICAgICAgdGhpcy5faXNPdXRPZk9yZGVyRW1pdHRlZFtpbnN0YW5jZUlkXSA9IGZhbHNlO1xuICAgICAgdGhpcy5fc2VxdWVuY2VOdW1iZXJCeUluc3RhbmNlW2luc3RhbmNlSWRdID0gcGFja2V0LnNlcXVlbmNlTnVtYmVyO1xuICAgICAgdGhpcy5fbGFzdFNlc3Npb25TdGFydFRpbWVzdGFtcFtpbnN0YW5jZUlkXSA9IHBhY2tldC5zZXF1ZW5jZVRpbWVzdGFtcDtcbiAgICAgIHRoaXMuX3BhY2tldHNCeUluc3RhbmNlW2luc3RhbmNlSWRdID0gKHRoaXMuX3BhY2tldHNCeUluc3RhbmNlW2luc3RhbmNlSWRdIHx8IFtdKVxuICAgICAgICAuZmlsdGVyKHdhaXRQYWNrZXQgPT4gd2FpdFBhY2tldC5wYWNrZXQuc2VxdWVuY2VUaW1lc3RhbXAgPj0gcGFja2V0LnNlcXVlbmNlVGltZXN0YW1wKTtcbiAgICAgIHJldHVybiBbcGFja2V0XS5jb25jYXQodGhpcy5fZmluZE5leHRQYWNrZXRzRnJvbVdhaXRMaXN0KGluc3RhbmNlSWQpKTtcbiAgICB9IGVsc2UgaWYgKHBhY2tldC5zZXF1ZW5jZVRpbWVzdGFtcCA8IHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF0pIHtcbiAgICAgIC8vIGZpbHRlciBvdXQgcHJldmlvdXMgcGFja2V0c1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH0gZWxzZSBpZiAocGFja2V0LnNlcXVlbmNlTnVtYmVyID09PSB0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0pIHtcbiAgICAgIC8vIGxldCB0aGUgZHVwbGljYXRlIHMvbiBwYWNrZXQgdG8gcGFzcyB0aHJvdWdoXG4gICAgICByZXR1cm4gW3BhY2tldF07XG4gICAgfSBlbHNlIGlmIChwYWNrZXQuc2VxdWVuY2VOdW1iZXIgPT09IHRoaXMuX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSArIDEpIHtcbiAgICAgIC8vIGluLW9yZGVyIHBhY2tldCB3YXMgcmVjZWl2ZWRcbiAgICAgIHRoaXMuX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSsrO1xuICAgICAgdGhpcy5fbGFzdFNlc3Npb25TdGFydFRpbWVzdGFtcFtpbnN0YW5jZUlkXSA9IHBhY2tldC5zZXF1ZW5jZVRpbWVzdGFtcCB8fFxuICAgICAgICB0aGlzLl9sYXN0U2Vzc2lvblN0YXJ0VGltZXN0YW1wW2luc3RhbmNlSWRdO1xuICAgICAgcmV0dXJuIFtwYWNrZXRdLmNvbmNhdCh0aGlzLl9maW5kTmV4dFBhY2tldHNGcm9tV2FpdExpc3QoaW5zdGFuY2VJZCkpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBvdXQtb2Ytb3JkZXIgcGFja2V0IHdhcyByZWNlaXZlZCwgYWRkIGl0IHRvIHRoZSB3YWl0IGxpc3RcbiAgICAgIHRoaXMuX3BhY2tldHNCeUluc3RhbmNlW2luc3RhbmNlSWRdID0gdGhpcy5fcGFja2V0c0J5SW5zdGFuY2VbaW5zdGFuY2VJZF0gfHwgW107XG4gICAgICBsZXQgd2FpdExpc3QgPSB0aGlzLl9wYWNrZXRzQnlJbnN0YW5jZVtpbnN0YW5jZUlkXTtcbiAgICAgIHdhaXRMaXN0LnB1c2goe1xuICAgICAgICBpbnN0YW5jZUlkLFxuICAgICAgICBhY2NvdW50SWQ6IHBhY2tldC5hY2NvdW50SWQsXG4gICAgICAgIGluc3RhbmNlSW5kZXg6IHBhY2tldC5pbnN0YW5jZUluZGV4IHx8IDAsXG4gICAgICAgIHNlcXVlbmNlTnVtYmVyOiBwYWNrZXQuc2VxdWVuY2VOdW1iZXIsXG4gICAgICAgIHBhY2tldDogcGFja2V0LFxuICAgICAgICByZWNlaXZlZEF0OiBuZXcgRGF0ZSgpXG4gICAgICB9KTtcbiAgICAgIHdhaXRMaXN0LnNvcnQoKGUxLCBlMikgPT4gZTEuc2VxdWVuY2VOdW1iZXIgLSBlMi5zZXF1ZW5jZU51bWJlcik7XG4gICAgICB3aGlsZSAod2FpdExpc3QubGVuZ3RoID4gdGhpcy5fd2FpdExpc3RTaXplTGltaXQpIHtcbiAgICAgICAgd2FpdExpc3Quc2hpZnQoKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzZXRzIHN0YXRlIGZvciBpbnN0YW5jZSBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJZCBpbnN0YW5jZSBpZCB0byByZXNldCBzdGF0ZSBmb3JcbiAgICovXG4gIG9uU3RyZWFtQ2xvc2VkKGluc3RhbmNlSWQpIHtcbiAgICBkZWxldGUgdGhpcy5fcGFja2V0c0J5SW5zdGFuY2VbaW5zdGFuY2VJZF07XG4gICAgZGVsZXRlIHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF07XG4gICAgZGVsZXRlIHRoaXMuX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZVtpbnN0YW5jZUlkXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXNldHMgc3RhdGUgZm9yIHNwZWNpZmllZCBhY2NvdW50cyBvbiByZWNvbm5lY3RcbiAgICogQHBhcmFtIHtTdHJpbmdbXX0gcmVjb25uZWN0QWNjb3VudElkcyByZWNvbm5lY3RlZCBhY2NvdW50IGlkc1xuICAgKi9cbiAgb25SZWNvbm5lY3RlZChyZWNvbm5lY3RBY2NvdW50SWRzKSB7XG4gICAgT2JqZWN0LmtleXModGhpcy5fcGFja2V0c0J5SW5zdGFuY2UpLmZvckVhY2goaW5zdGFuY2VJZCA9PiB7XG4gICAgICBpZihyZWNvbm5lY3RBY2NvdW50SWRzLmluY2x1ZGVzKHRoaXMuX2dldEFjY291bnRJZEZyb21JbnN0YW5jZShpbnN0YW5jZUlkKSkpIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuX3BhY2tldHNCeUluc3RhbmNlW2luc3RhbmNlSWRdO1xuICAgICAgfVxuICAgIH0pO1xuICAgIE9iamVjdC5rZXlzKHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXApLmZvckVhY2goaW5zdGFuY2VJZCA9PiB7XG4gICAgICBpZihyZWNvbm5lY3RBY2NvdW50SWRzLmluY2x1ZGVzKHRoaXMuX2dldEFjY291bnRJZEZyb21JbnN0YW5jZShpbnN0YW5jZUlkKSkpIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF07XG4gICAgICB9XG4gICAgfSk7XG4gICAgT2JqZWN0LmtleXModGhpcy5fc2VxdWVuY2VOdW1iZXJCeUluc3RhbmNlKS5mb3JFYWNoKGluc3RhbmNlSWQgPT4ge1xuICAgICAgaWYocmVjb25uZWN0QWNjb3VudElkcy5pbmNsdWRlcyh0aGlzLl9nZXRBY2NvdW50SWRGcm9tSW5zdGFuY2UoaW5zdGFuY2VJZCkpKSB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2VbaW5zdGFuY2VJZF07XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBfZ2V0QWNjb3VudElkRnJvbUluc3RhbmNlKGluc3RhbmNlSWQpIHtcbiAgICByZXR1cm4gaW5zdGFuY2VJZC5zcGxpdCgnOicpWzBdO1xuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgX2ZpbmROZXh0UGFja2V0c0Zyb21XYWl0TGlzdChpbnN0YW5jZUlkKSB7XG4gICAgbGV0IHJlc3VsdCA9IFtdO1xuICAgIGxldCB3YWl0TGlzdCA9IHRoaXMuX3BhY2tldHNCeUluc3RhbmNlW2luc3RhbmNlSWRdIHx8IFtdO1xuICAgIHdoaWxlICh3YWl0TGlzdC5sZW5ndGggJiYgKFt0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0sXG4gICAgICB0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0gKyAxXS5pbmNsdWRlcyh3YWl0TGlzdFswXS5zZXF1ZW5jZU51bWJlcikgfHxcbiAgICAgIHdhaXRMaXN0WzBdLnBhY2tldC5zZXF1ZW5jZVRpbWVzdGFtcCA8IHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF0pKSB7XG4gICAgICBpZiAod2FpdExpc3RbMF0ucGFja2V0LnNlcXVlbmNlVGltZXN0YW1wID49IHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF0pIHtcbiAgICAgICAgcmVzdWx0LnB1c2god2FpdExpc3RbMF0ucGFja2V0KTtcbiAgICAgICAgaWYgKHdhaXRMaXN0WzBdLnBhY2tldC5zZXF1ZW5jZU51bWJlciA9PT0gdGhpcy5fc2VxdWVuY2VOdW1iZXJCeUluc3RhbmNlW2luc3RhbmNlSWRdICsgMSkge1xuICAgICAgICAgIHRoaXMuX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSsrO1xuICAgICAgICAgIHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF0gPSB3YWl0TGlzdFswXS5wYWNrZXQuc2VxdWVuY2VUaW1lc3RhbXAgfHxcbiAgICAgICAgICAgIHRoaXMuX2xhc3RTZXNzaW9uU3RhcnRUaW1lc3RhbXBbaW5zdGFuY2VJZF07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHdhaXRMaXN0LnNwbGljZSgwLCAxKTtcbiAgICB9XG4gICAgaWYgKCF3YWl0TGlzdC5sZW5ndGgpIHtcbiAgICAgIGRlbGV0ZSB0aGlzLl9wYWNrZXRzQnlJbnN0YW5jZVtpbnN0YW5jZUlkXTtcbiAgICB9XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfVxuXG4gIF9lbWl0T3V0T2ZPcmRlckV2ZW50cygpIHtcbiAgICBmb3IgKGxldCB3YWl0TGlzdCBvZiBPYmplY3QudmFsdWVzPGFueT4odGhpcy5fcGFja2V0c0J5SW5zdGFuY2UpKSB7XG4gICAgICBpZiAod2FpdExpc3QubGVuZ3RoICYmIHdhaXRMaXN0WzBdLnJlY2VpdmVkQXQuZ2V0VGltZSgpICsgdGhpcy5fb3JkZXJpbmdUaW1lb3V0SW5TZWNvbmRzICogMTAwMCA8IERhdGUubm93KCkpIHtcbiAgICAgICAgY29uc3QgaW5zdGFuY2VJZCA9IHdhaXRMaXN0WzBdLmluc3RhbmNlSWQ7XG4gICAgICAgIGlmKCF0aGlzLl9pc091dE9mT3JkZXJFbWl0dGVkW2luc3RhbmNlSWRdKSB7XG4gICAgICAgICAgdGhpcy5faXNPdXRPZk9yZGVyRW1pdHRlZFtpbnN0YW5jZUlkXSA9IHRydWU7XG4gICAgICAgICAgLy8gRG8gbm90IGVtaXQgb25PdXRPZk9yZGVyUGFja2V0IGZvciBwYWNrZXRzIHRoYXQgY29tZSBiZWZvcmUgc3luY2hyb25pemF0aW9uU3RhcnRlZFxuICAgICAgICAgIGlmICh0aGlzLl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2VbaW5zdGFuY2VJZF0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgdGhpcy5fb3V0T2ZPcmRlckxpc3RlbmVyLm9uT3V0T2ZPcmRlclBhY2tldCh3YWl0TGlzdFswXS5hY2NvdW50SWQsIHdhaXRMaXN0WzBdLmluc3RhbmNlSW5kZXgsXG4gICAgICAgICAgICAgIHRoaXMuX3NlcXVlbmNlTnVtYmVyQnlJbnN0YW5jZVtpbnN0YW5jZUlkXSArIDEsIHdhaXRMaXN0WzBdLnNlcXVlbmNlTnVtYmVyLCB3YWl0TGlzdFswXS5wYWNrZXQsXG4gICAgICAgICAgICAgIHdhaXRMaXN0WzBdLnJlY2VpdmVkQXQpO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxufVxuXG5uYW1lc3BhY2UgUGFja2V0T3JkZXJlciB7XG5cbiAgLyoqIFBhY2tldCB0byBvcmRlci4gQ2FuIGJlIGV4dGVuZGVkLCB0aGUgc2FtZSBpbnB1dCBwYWNrZXQgb2JqZWN0IHJlZmVyZW5jZSB3aWxsIGJlIHJldHVybmVkICovXG4gIGV4cG9ydCB0eXBlIFBhY2tldCA9IHtcbiAgICAvKiogQWNjb3VudCBJRCAqL1xuICAgIGFjY291bnRJZD86IHN0cmluZztcbiAgICAvKiogSW5zdGFuY2UgaW5kZXguIERlZmF1bHRzIHRvIGAwYCAqL1xuICAgIGluc3RhbmNlSW5kZXg/OiBudW1iZXI7XG4gICAgLyoqIFNvdXJjZSBzZXJ2ZXIgaG9zdC4gRGVmYXVsdHMgdG8gYDBgICovXG4gICAgaG9zdD86IHN0cmluZyB8IG51bWJlcjtcbiAgICAvKiogUGFja2V0IHR5cGUgKi9cbiAgICB0eXBlPzogc3RyaW5nO1xuICAgIC8qKiBTZXF1ZW5jZSBudW1iZXIgKi9cbiAgICBzZXF1ZW5jZU51bWJlcj86IG51bWJlcjtcbiAgICAvKiogU3luY2hyb25pemF0aW9uIElEICovXG4gICAgc3luY2hyb25pemF0aW9uSWQ/OiBzdHJpbmc7XG4gICAgLyoqIFNlcXVlbmNlIHRpbWVzdGFtcCAqL1xuICAgIHNlcXVlbmNlVGltZXN0YW1wPzogbnVtYmVyO1xuICB9O1xufVxuXG5leHBvcnQgZGVmYXVsdCBQYWNrZXRPcmRlcmVyO1xuIl0sIm5hbWVzIjpbIlBhY2tldE9yZGVyZXIiLCJzdGFydCIsIl9zZXF1ZW5jZU51bWJlckJ5SW5zdGFuY2UiLCJfbGFzdFNlc3Npb25TdGFydFRpbWVzdGFtcCIsIl9wYWNrZXRzQnlJbnN0YW5jZSIsIl9vdXRPZk9yZGVySW50ZXJ2YWwiLCJzZXRJbnRlcnZhbCIsIl9lbWl0T3V0T2ZPcmRlckV2ZW50cyIsInN0b3AiLCJjbGVhckludGVydmFsIiwicmVzdG9yZU9yZGVyIiwicGFja2V0IiwiaW5zdGFuY2VJZCIsImFjY291bnRJZCIsImluc3RhbmNlSW5kZXgiLCJob3N0Iiwic2VxdWVuY2VOdW1iZXIiLCJ1bmRlZmluZWQiLCJ0eXBlIiwic3luY2hyb25pemF0aW9uSWQiLCJzZXF1ZW5jZVRpbWVzdGFtcCIsIl9pc091dE9mT3JkZXJFbWl0dGVkIiwiZmlsdGVyIiwid2FpdFBhY2tldCIsImNvbmNhdCIsIl9maW5kTmV4dFBhY2tldHNGcm9tV2FpdExpc3QiLCJ3YWl0TGlzdCIsInB1c2giLCJyZWNlaXZlZEF0IiwiRGF0ZSIsInNvcnQiLCJlMSIsImUyIiwibGVuZ3RoIiwiX3dhaXRMaXN0U2l6ZUxpbWl0Iiwic2hpZnQiLCJvblN0cmVhbUNsb3NlZCIsIm9uUmVjb25uZWN0ZWQiLCJyZWNvbm5lY3RBY2NvdW50SWRzIiwiT2JqZWN0Iiwia2V5cyIsImZvckVhY2giLCJpbmNsdWRlcyIsIl9nZXRBY2NvdW50SWRGcm9tSW5zdGFuY2UiLCJzcGxpdCIsInJlc3VsdCIsInNwbGljZSIsInZhbHVlcyIsImdldFRpbWUiLCJfb3JkZXJpbmdUaW1lb3V0SW5TZWNvbmRzIiwibm93IiwiX291dE9mT3JkZXJMaXN0ZW5lciIsIm9uT3V0T2ZPcmRlclBhY2tldCIsImNvbnN0cnVjdG9yIiwib3V0T2ZPcmRlckxpc3RlbmVyIiwib3JkZXJpbmdUaW1lb3V0SW5TZWNvbmRzIl0sIm1hcHBpbmdzIjoiQUFBQTs7OzsrQkF5TUE7OztlQUFBOzs7Ozs7Ozs7Ozs7Ozs7O0FBdk1BOztDQUVDLEdBQ0QsSUFBQSxBQUFNQSxnQkFBTixNQUFNQTtJQTBCSjs7R0FFQyxHQUNEQyxRQUFRO1FBQ04sSUFBSSxDQUFDQyx5QkFBeUIsR0FBRyxDQUFDO1FB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