UNPKG

@jsprismarine/raknet

Version:
372 lines (370 loc) • 56.4 kB
import BinaryStream from '@jsprismarine/jsbinaryutils'; import assert from 'node:assert'; import { MAX_CHANNELS, UDP_HEADER_SIZE } from './Constants.es.js'; import ACK from './protocol/ACK.es.js'; import { BitFlags } from './protocol/BitFlags.es.js'; import Frame, { MAX_FRAME_BYTE_LENGTH } from './protocol/Frame.es.js'; import { FrameReliability } from './protocol/FrameReliability.es.js'; import FrameSet, { DATAGRAM_HEADER_BYTE_LENGTH } from './protocol/FrameSet.es.js'; import { MessageIdentifiers } from './protocol/MessageIdentifiers.es.js'; import NACK from './protocol/NACK.es.js'; import PacketPool from './protocol/PacketPool.es.js'; import ConnectedPing from './protocol/connection/ConnectedPing.es.js'; import ConnectedPong from './protocol/connection/ConnectedPong.es.js'; import ConnectionRequest from './protocol/login/ConnectionRequest.es.js'; import ConnectionRequestAccepted from './protocol/login/ConnectionRequestAccepted.es.js'; import InetAddress from './utils/InetAddress.es.js'; var RakNetPriority = /* @__PURE__ */ ((RakNetPriority2) => { RakNetPriority2[RakNetPriority2["NORMAL"] = 0] = "NORMAL"; RakNetPriority2[RakNetPriority2["IMMEDIATE"] = 1] = "IMMEDIATE"; return RakNetPriority2; })(RakNetPriority || {}); var SessionStatus = /* @__PURE__ */ ((SessionStatus2) => { SessionStatus2[SessionStatus2["CONNECTING"] = 0] = "CONNECTING"; SessionStatus2[SessionStatus2["CONNECTED"] = 1] = "CONNECTED"; SessionStatus2[SessionStatus2["DISCONNECTING"] = 2] = "DISCONNECTING"; SessionStatus2[SessionStatus2["DISCONNECTED"] = 3] = "DISCONNECTED"; return SessionStatus2; })(SessionStatus || {}); class Session { constructor(listener, mtuSize, rinfo, guid) { this.listener = listener; this.mtuSize = mtuSize; this.rinfo = rinfo; this.guid = guid; this.lastUpdate = Date.now(); this.outputOrderIndex = new Array(MAX_CHANNELS).fill(0); this.outputSequenceIndex = new Array(MAX_CHANNELS).fill(0); this.inputOrderIndex = new Array(MAX_CHANNELS).fill(0); for (let i = 0; i < MAX_CHANNELS; i++) { this.inputOrderingQueue.set(i, /* @__PURE__ */ new Map()); } this.inputHighestSequenceIndex = new Array(MAX_CHANNELS).fill(0); } state = 0 /* CONNECTING */; outputFrameQueue = new FrameSet(); outputSequenceNumber = 0; // TODO: find a better name outputReliableIndex = 0; outputBackupQueue = /* @__PURE__ */ new Map(); outputOrderIndex; outputSequenceIndex; receivedFrameSequences = /* @__PURE__ */ new Set(); lostFrameSequences = /* @__PURE__ */ new Set(); // Map holding fragments of fragmented packets fragmentsQueue = /* @__PURE__ */ new Map(); outputFragmentIndex = 0; lastInputSequenceNumber = -1; inputHighestSequenceIndex; inputOrderIndex; inputOrderingQueue = /* @__PURE__ */ new Map(); // Last timestamp of packet received, helpful for timeout lastUpdate = Date.now(); active = true; // Packet pool is the best option to reduce allocations packetPool = new PacketPool(); // Lookup table for packet handlers, always O(1) in average and worst case packetHandlers = { // 0x40 | 0xc0 = MessageHeaders.ACKNOWLEDGE_PACKET [MessageIdentifiers.ACKNOWLEDGE_PACKET]: (buffer) => { const ack = this.packetPool.getAckInstance(); ack.buffer = buffer; ack.decode(); this.handleACK(ack); }, [MessageIdentifiers.NACKNOWLEDGE_PACKET]: (buffer) => { const nack = this.packetPool.getNackInstance(); nack.buffer = buffer; nack.decode(); this.handleNACK(nack); }, [BitFlags.VALID]: (buffer) => { const frameSet = this.packetPool.getFrameSetInstance(); frameSet.buffer = buffer; frameSet.decode(); this.handleFrameSet(frameSet); } }; update(timestamp) { if (!this.isActive() && !this.isDisconnected() && this.lastUpdate + 1e4 < timestamp) { this.disconnect("timeout"); return; } this.active = false; if (this.receivedFrameSequences.size > 0) { const ack = new ACK(); ack.sequenceNumbers = Array.from(this.receivedFrameSequences).map((seq) => { this.receivedFrameSequences.delete(seq); return seq; }); this.sendPacket(ack); } if (this.lostFrameSequences.size > 0) { const pk = new NACK(); pk.sequenceNumbers = Array.from(this.lostFrameSequences).map((seq) => { this.lostFrameSequences.delete(seq); return seq; }); this.sendPacket(pk); } this.sendFrameQueue(); } // https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/ReliabilityLayer.cpp#L635 handle(buffer) { this.active = true; this.lastUpdate = Date.now(); const handler = this.packetHandlers[buffer[0] & 240]; if (handler) { handler(buffer); } else { this.listener.getLogger().verbose(`Received an unknown packet type=${buffer[0]}`); } } handleFrameSet(frameSet) { if (this.receivedFrameSequences.has(frameSet.sequenceNumber)) { this.listener.getLogger().debug(`Discarded duplicated packet from client=${this.getAddress()}`); return; } this.lostFrameSequences.delete(frameSet.sequenceNumber); if (frameSet.sequenceNumber < this.lastInputSequenceNumber || frameSet.sequenceNumber === this.lastInputSequenceNumber) { return; } this.receivedFrameSequences.add(frameSet.sequenceNumber); const diff = frameSet.sequenceNumber - this.lastInputSequenceNumber; if (diff !== 1) { for (let i = this.lastInputSequenceNumber + 1; i < frameSet.sequenceNumber; i++) { if (!this.receivedFrameSequences.has(i)) { this.lostFrameSequences.add(i); } } } this.lastInputSequenceNumber = frameSet.sequenceNumber; for (const frame of frameSet.frames) { this.handleFrame(frame); } } handleACK(ack) { for (const seq of ack.sequenceNumbers) { this.outputBackupQueue.delete(seq); } } handleNACK(nack) { for (const seq of nack.sequenceNumbers) { if (this.outputBackupQueue.has(seq)) { const lostFrames = this.outputBackupQueue.get(seq); for (const lostFrame of lostFrames) { this.sendFrame(lostFrame, 1 /* IMMEDIATE */); } this.outputBackupQueue.delete(seq); } } } handleFrame(frame) { if (frame.isFragmented()) { this.handleFragment(frame); return; } const orderChannel = frame.orderChannel; const orderIndex = frame.orderIndex; const sequenceIndex = frame.sequenceIndex; if (frame.isSequenced()) { if (sequenceIndex < this.inputHighestSequenceIndex[orderChannel] || orderIndex < this.inputOrderIndex[orderChannel]) { return; } this.inputHighestSequenceIndex[orderChannel] = sequenceIndex + 1; this.handlePacket(frame); } else if (frame.isOrdered()) { if (orderIndex === this.inputOrderIndex[orderChannel]) { this.inputHighestSequenceIndex[orderChannel] = 0; this.inputOrderIndex[orderChannel] = orderIndex + 1; this.handlePacket(frame); let i = this.inputOrderIndex[orderChannel]; const outOfOrderQueue = this.inputOrderingQueue.get(orderChannel); for (; outOfOrderQueue.has(i); i++) { this.handlePacket(outOfOrderQueue.get(i)); outOfOrderQueue.delete(i); } this.inputOrderingQueue.set(orderChannel, outOfOrderQueue); this.inputOrderIndex[orderChannel] = i; } else if (orderIndex > this.inputOrderIndex[orderChannel]) { const unordered = this.inputOrderingQueue.get(orderChannel); unordered.set(orderIndex, frame); } } else { this.handlePacket(frame); } } sendFrame(frame, flags = 0 /* NORMAL */) { assert(typeof frame.orderChannel === "number", "Frame OrderChannel cannot be null"); if (frame.isSequenced()) { frame.orderIndex = this.outputOrderIndex[frame.orderChannel]; frame.sequenceIndex = this.outputSequenceIndex[frame.orderChannel]++; } else if (frame.isOrderedExclusive()) { frame.orderIndex = this.outputOrderIndex[frame.orderChannel]++; this.outputSequenceIndex[frame.orderChannel] = 0; } frame.reliableIndex = this.outputReliableIndex++; const maxSize = this.getMTU() - DATAGRAM_HEADER_BYTE_LENGTH - MAX_FRAME_BYTE_LENGTH; if (frame.content.byteLength > maxSize) { const buffer = Buffer.from(frame.content); const fragmentId = this.outputFragmentIndex++ % 65536; for (let i = 0; i < buffer.byteLength; i += maxSize) { if (i !== 0) { frame.reliableIndex = this.outputReliableIndex++; } frame.content = buffer.slice(i, i + maxSize); frame.fragmentIndex = i / maxSize; frame.fragmentId = fragmentId; frame.fragmentSize = Math.ceil(buffer.byteLength / maxSize); this.addFrameToQueue(frame, flags | 1 /* IMMEDIATE */); } } else { this.addFrameToQueue(frame, flags); } } addFrameToQueue(frame, priority = 0 /* NORMAL */) { let length = 4; for (const queuedFrame of this.outputFrameQueue.frames) { length += queuedFrame.getByteLength(); } if (length + frame.getByteLength() > this.mtuSize - 36) { this.sendFrameQueue(); } this.outputFrameQueue.frames.push(frame); if (priority === 1 /* IMMEDIATE */) { this.sendFrameQueue(); } } handlePacket(packet) { const id = packet.content[0]; if (this.state === 0 /* CONNECTING */) { if (id === MessageIdentifiers.CONNECTION_REQUEST) { this.handleConnectionRequest(packet.content).then( (encapsulated) => this.sendFrame(encapsulated, 1 /* IMMEDIATE */), () => { } ); } else if (id === MessageIdentifiers.NEW_INCOMING_CONNECTION) { this.state = 1 /* CONNECTED */; this.listener.emit("openConnection", this); } } else if (id === MessageIdentifiers.DISCONNECTION_NOTIFICATION) { this.disconnect(); } else if (id === MessageIdentifiers.CONNECTED_PING) { this.handleConnectedPing(packet.content).then( (encapsulated) => this.sendFrame(encapsulated), () => { } ); } else if (this.state === 1 /* CONNECTED */) { this.listener.emit("encapsulated", packet, this.getAddress()); } } async handleConnectionRequest(buffer) { const dataPacket = new ConnectionRequest(buffer); dataPacket.decode(); const pk = new ConnectionRequestAccepted(); pk.clientAddress = this.getAddress(); pk.requestTimestamp = dataPacket.requestTimestamp; pk.acceptedTimestamp = BigInt(Date.now()); pk.encode(); const sendPacket = new Frame(); sendPacket.reliability = FrameReliability.UNRELIABLE; sendPacket.orderChannel = 0; sendPacket.content = pk.getBuffer(); return sendPacket; } async handleConnectedPing(buffer) { const dataPacket = new ConnectedPing(buffer); dataPacket.decode(); const pk = new ConnectedPong(); pk.clientTimestamp = dataPacket.clientTimestamp; pk.serverTimestamp = BigInt(Date.now()); pk.encode(); const sendPacket = new Frame(); sendPacket.reliability = FrameReliability.UNRELIABLE; sendPacket.orderChannel = 0; sendPacket.content = pk.getBuffer(); return sendPacket; } handleFragment(frame) { if (!this.fragmentsQueue.has(frame.fragmentId)) { this.fragmentsQueue.set(frame.fragmentId, /* @__PURE__ */ new Map([[frame.fragmentIndex, frame]])); } else { const value = this.fragmentsQueue.get(frame.fragmentId); value.set(frame.fragmentIndex, frame); if (value.size === frame.fragmentSize) { const stream = new BinaryStream(); for (let i = 0; i < value.size; i++) { const splitPacket = value.get(i); stream.write(splitPacket.content); } const assembledFrame = new Frame(); assembledFrame.content = stream.getBuffer(); assembledFrame.reliability = frame.reliability; assembledFrame.reliableIndex = frame.reliableIndex; assembledFrame.sequenceIndex = frame.sequenceIndex; assembledFrame.orderIndex = frame.orderIndex; assembledFrame.orderChannel = frame.orderChannel; this.fragmentsQueue.delete(frame.fragmentId); this.handleFrame(assembledFrame); } } } sendFrameQueue() { if (this.outputFrameQueue.frames.length > 0) { this.outputFrameQueue.sequenceNumber = this.outputSequenceNumber++; this.sendFrameSet(this.outputFrameQueue); this.outputFrameQueue = new FrameSet(); } } sendFrameSet(frameSet) { this.sendPacket(frameSet); this.outputBackupQueue.set( frameSet.sequenceNumber, frameSet.frames.filter((frame) => frame.isReliable()) ); } sendPacket(packet) { this.listener.sendPacket(packet, this.rinfo); } close() { const stream = new BinaryStream(Buffer.from("\0\0\b", "binary")); this.addFrameToQueue(new Frame().fromBinary(stream), 1 /* IMMEDIATE */); } /** * Kick a client * @param reason - the reason message, optional */ disconnect(reason = "client disconnect") { this.close(); this.state = 3 /* DISCONNECTED */; this.update(Date.now()); this.listener.removeSession(this, reason); } getState() { return this.state; } isActive() { return this.active; } isDisconnected() { return this.state === 3 /* DISCONNECTED */; } getListener() { return this.listener; } /** * Returns the maxmium transfer unit * for this connection. * @returns {number} the UDP adjusted. */ getMTU() { return this.mtuSize - UDP_HEADER_SIZE; } getAddress() { return new InetAddress(this.rinfo.address, this.rinfo.port, 4); } } export { RakNetPriority, SessionStatus, Session as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2Vzc2lvbi5lcy5qcyIsInNvdXJjZXMiOlsiLi4vc3JjL1Nlc3Npb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IEJpbmFyeVN0cmVhbSBmcm9tICdAanNwcmlzbWFyaW5lL2pzYmluYXJ5dXRpbHMnO1xuaW1wb3J0IGFzc2VydCBmcm9tICdub2RlOmFzc2VydCc7XG5pbXBvcnQgeyBNQVhfQ0hBTk5FTFMsIFVEUF9IRUFERVJfU0laRSB9IGZyb20gJy4vQ29uc3RhbnRzJztcbmltcG9ydCBBQ0sgZnJvbSAnLi9wcm90b2NvbC9BQ0snO1xuaW1wb3J0IEJpdEZsYWdzIGZyb20gJy4vcHJvdG9jb2wvQml0RmxhZ3MnO1xuaW1wb3J0IEZyYW1lLCB7IE1BWF9GUkFNRV9CWVRFX0xFTkdUSCB9IGZyb20gJy4vcHJvdG9jb2wvRnJhbWUnO1xuaW1wb3J0IEZyYW1lUmVsaWFiaWxpdHkgZnJvbSAnLi9wcm90b2NvbC9GcmFtZVJlbGlhYmlsaXR5JztcbmltcG9ydCBGcmFtZVNldCwgeyBEQVRBR1JBTV9IRUFERVJfQllURV9MRU5HVEggfSBmcm9tICcuL3Byb3RvY29sL0ZyYW1lU2V0JztcbmltcG9ydCB7IE1lc3NhZ2VJZGVudGlmaWVycyB9IGZyb20gJy4vcHJvdG9jb2wvTWVzc2FnZUlkZW50aWZpZXJzJztcbmltcG9ydCBOQUNLIGZyb20gJy4vcHJvdG9jb2wvTkFDSyc7XG5pbXBvcnQgUGFja2V0UG9vbCBmcm9tICcuL3Byb3RvY29sL1BhY2tldFBvb2wnO1xuaW1wb3J0IENvbm5lY3RlZFBpbmcgZnJvbSAnLi9wcm90b2NvbC9jb25uZWN0aW9uL0Nvbm5lY3RlZFBpbmcnO1xuaW1wb3J0IENvbm5lY3RlZFBvbmcgZnJvbSAnLi9wcm90b2NvbC9jb25uZWN0aW9uL0Nvbm5lY3RlZFBvbmcnO1xuaW1wb3J0IENvbm5lY3Rpb25SZXF1ZXN0IGZyb20gJy4vcHJvdG9jb2wvbG9naW4vQ29ubmVjdGlvblJlcXVlc3QnO1xuaW1wb3J0IENvbm5lY3Rpb25SZXF1ZXN0QWNjZXB0ZWQgZnJvbSAnLi9wcm90b2NvbC9sb2dpbi9Db25uZWN0aW9uUmVxdWVzdEFjY2VwdGVkJztcbmltcG9ydCBJbmV0QWRkcmVzcyBmcm9tICcuL3V0aWxzL0luZXRBZGRyZXNzJztcblxuaW1wb3J0IHR5cGUgeyBSZW1vdGVJbmZvIH0gZnJvbSAnbm9kZTpkZ3JhbSc7XG5pbXBvcnQgdHlwZSBSYWtOZXRMaXN0ZW5lciBmcm9tICcuL1NlcnZlclNvY2tldCc7XG5pbXBvcnQgdHlwZSBQYWNrZXQgZnJvbSAnLi9wcm90b2NvbC9QYWNrZXQnO1xuXG5leHBvcnQgZW51bSBSYWtOZXRQcmlvcml0eSB7XG4gICAgTk9STUFMLFxuICAgIElNTUVESUFURVxufVxuXG5leHBvcnQgZW51bSBTZXNzaW9uU3RhdHVzIHtcbiAgICBDT05ORUNUSU5HLFxuICAgIENPTk5FQ1RFRCxcbiAgICBESVNDT05ORUNUSU5HLFxuICAgIERJU0NPTk5FQ1RFRFxufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTZXNzaW9uIHtcbiAgICBwcml2YXRlIHN0YXRlID0gU2Vzc2lvblN0YXR1cy5DT05ORUNUSU5HO1xuXG4gICAgcHJpdmF0ZSBvdXRwdXRGcmFtZVF1ZXVlID0gbmV3IEZyYW1lU2V0KCk7XG4gICAgcHJpdmF0ZSBvdXRwdXRTZXF1ZW5jZU51bWJlciA9IDA7IC8vIFRPRE86IGZpbmQgYSBiZXR0ZXIgbmFtZVxuICAgIHByaXZhdGUgb3V0cHV0UmVsaWFibGVJbmRleCA9IDA7XG4gICAgcHJpdmF0ZSByZWFkb25seSBvdXRwdXRCYWNrdXBRdWV1ZTogTWFwPG51bWJlciwgRnJhbWVbXT4gPSBuZXcgTWFwKCk7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IG91dHB1dE9yZGVySW5kZXg6IG51bWJlcltdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgb3V0cHV0U2VxdWVuY2VJbmRleDogbnVtYmVyW107XG5cbiAgICBwcml2YXRlIHJlY2VpdmVkRnJhbWVTZXF1ZW5jZXM6IFNldDxudW1iZXI+ID0gbmV3IFNldCgpO1xuICAgIHByaXZhdGUgbG9zdEZyYW1lU2VxdWVuY2VzOiBTZXQ8bnVtYmVyPiA9IG5ldyBTZXQoKTtcblxuICAgIC8vIE1hcCBob2xkaW5nIGZyYWdtZW50cyBvZiBmcmFnbWVudGVkIHBhY2tldHNcbiAgICBwcml2YXRlIHJlYWRvbmx5IGZyYWdtZW50c1F1ZXVlOiBNYXA8bnVtYmVyLCBNYXA8bnVtYmVyLCBGcmFtZT4+ID0gbmV3IE1hcCgpO1xuICAgIHByaXZhdGUgb3V0cHV0RnJhZ21lbnRJbmRleCA9IDA7XG5cbiAgICBwcml2YXRlIGxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyID0gLTE7XG4gICAgcHJpdmF0ZSByZWFkb25seSBpbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4OiBudW1iZXJbXTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGlucHV0T3JkZXJJbmRleDogbnVtYmVyW107XG4gICAgcHJpdmF0ZSBpbnB1dE9yZGVyaW5nUXVldWU6IE1hcDxudW1iZXIsIE1hcDxudW1iZXIsIEZyYW1lPj4gPSBuZXcgTWFwKCk7XG5cbiAgICAvLyBMYXN0IHRpbWVzdGFtcCBvZiBwYWNrZXQgcmVjZWl2ZWQsIGhlbHBmdWwgZm9yIHRpbWVvdXRcbiAgICBwcml2YXRlIGxhc3RVcGRhdGU6IG51bWJlciA9IERhdGUubm93KCk7XG4gICAgcHJpdmF0ZSBhY3RpdmUgPSB0cnVlO1xuXG4gICAgLy8gUGFja2V0IHBvb2wgaXMgdGhlIGJlc3Qgb3B0aW9uIHRvIHJlZHVjZSBhbGxvY2F0aW9uc1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGFja2V0UG9vbCA9IG5ldyBQYWNrZXRQb29sKCk7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgbGlzdGVuZXI6IFJha05ldExpc3RlbmVyLFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IG10dVNpemU6IG51bWJlcixcbiAgICAgICAgcHVibGljIHJlYWRvbmx5IHJpbmZvOiBSZW1vdGVJbmZvLFxuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgZ3VpZDogYmlnaW50XG4gICAgKSB7XG4gICAgICAgIHRoaXMubGFzdFVwZGF0ZSA9IERhdGUubm93KCk7XG5cbiAgICAgICAgdGhpcy5vdXRwdXRPcmRlckluZGV4ID0gbmV3IEFycmF5KE1BWF9DSEFOTkVMUykuZmlsbCgwKTtcbiAgICAgICAgdGhpcy5vdXRwdXRTZXF1ZW5jZUluZGV4ID0gbmV3IEFycmF5KE1BWF9DSEFOTkVMUykuZmlsbCgwKTtcblxuICAgICAgICB0aGlzLmlucHV0T3JkZXJJbmRleCA9IG5ldyBBcnJheShNQVhfQ0hBTk5FTFMpLmZpbGwoMCk7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgTUFYX0NIQU5ORUxTOyBpKyspIHtcbiAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLnNldChpLCBuZXcgTWFwKCkpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4ID0gbmV3IEFycmF5KE1BWF9DSEFOTkVMUykuZmlsbCgwKTtcbiAgICB9XG5cbiAgICAvLyBMb29rdXAgdGFibGUgZm9yIHBhY2tldCBoYW5kbGVycywgYWx3YXlzIE8oMSkgaW4gYXZlcmFnZSBhbmQgd29yc3QgY2FzZVxuICAgIHByaXZhdGUgcmVhZG9ubHkgcGFja2V0SGFuZGxlcnM6IFJlY29yZDxudW1iZXIsIChidWZmZXI6IEJ1ZmZlcikgPT4gdm9pZD4gPSB7XG4gICAgICAgIC8vIDB4NDAgfCAweGMwID0gTWVzc2FnZUhlYWRlcnMuQUNLTk9XTEVER0VfUEFDS0VUXG4gICAgICAgIFtNZXNzYWdlSWRlbnRpZmllcnMuQUNLTk9XTEVER0VfUEFDS0VUXTogKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBhY2sgPSB0aGlzLnBhY2tldFBvb2wuZ2V0QWNrSW5zdGFuY2UoKTtcbiAgICAgICAgICAgIChhY2sgYXMgYW55KS5idWZmZXIgPSBidWZmZXI7XG4gICAgICAgICAgICBhY2suZGVjb2RlKCk7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUFDSyhhY2spO1xuICAgICAgICB9LFxuICAgICAgICBbTWVzc2FnZUlkZW50aWZpZXJzLk5BQ0tOT1dMRURHRV9QQUNLRVRdOiAoYnVmZmVyOiBCdWZmZXIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG5hY2sgPSB0aGlzLnBhY2tldFBvb2wuZ2V0TmFja0luc3RhbmNlKCk7XG4gICAgICAgICAgICAobmFjayBhcyBhbnkpLmJ1ZmZlciA9IGJ1ZmZlcjtcbiAgICAgICAgICAgIG5hY2suZGVjb2RlKCk7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZU5BQ0sobmFjayk7XG4gICAgICAgIH0sXG4gICAgICAgIFtCaXRGbGFncy5WQUxJRF06IChidWZmZXI6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgICAgY29uc3QgZnJhbWVTZXQgPSB0aGlzLnBhY2tldFBvb2wuZ2V0RnJhbWVTZXRJbnN0YW5jZSgpO1xuICAgICAgICAgICAgKGZyYW1lU2V0IGFzIGFueSkuYnVmZmVyID0gYnVmZmVyO1xuICAgICAgICAgICAgZnJhbWVTZXQuZGVjb2RlKCk7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUZyYW1lU2V0KGZyYW1lU2V0KTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBwdWJsaWMgdXBkYXRlKHRpbWVzdGFtcDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5pc0FjdGl2ZSgpICYmICF0aGlzLmlzRGlzY29ubmVjdGVkKCkgJiYgdGhpcy5sYXN0VXBkYXRlICsgMTBfMDAwIDwgdGltZXN0YW1wKSB7XG4gICAgICAgICAgICB0aGlzLmRpc2Nvbm5lY3QoJ3RpbWVvdXQnKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYWN0aXZlID0gZmFsc2U7XG5cbiAgICAgICAgLy8gVE9ETzogYSBxdWV1ZSBqdXN0IGZvciBBQ0tzIHNlcXVlbmNlcyB0byBhdm9pZCBkdXBsaWNhdGVkc1xuICAgICAgICBpZiAodGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLnNpemUgPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBhY2sgPSBuZXcgQUNLKCk7XG4gICAgICAgICAgICBhY2suc2VxdWVuY2VOdW1iZXJzID0gQXJyYXkuZnJvbSh0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMpLm1hcCgoc2VxKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLmRlbGV0ZShzZXEpO1xuICAgICAgICAgICAgICAgIHJldHVybiBzZXE7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc2VuZFBhY2tldChhY2spO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzLnNpemUgPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBwayA9IG5ldyBOQUNLKCk7XG4gICAgICAgICAgICBway5zZXF1ZW5jZU51bWJlcnMgPSBBcnJheS5mcm9tKHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzKS5tYXAoKHNlcSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzLmRlbGV0ZShzZXEpO1xuICAgICAgICAgICAgICAgIHJldHVybiBzZXE7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIHRoaXMuc2VuZFBhY2tldChwayk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnNlbmRGcmFtZVF1ZXVlKCk7XG4gICAgfVxuXG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi8xYTE2OTg5NWE5MDBjOWZjNDg0MWM1NTZlMTY1MTQxODJiNzVmYWY4L1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMNjM1XG4gICAgcHVibGljIGhhbmRsZShidWZmZXI6IEJ1ZmZlcik6IHZvaWQge1xuICAgICAgICB0aGlzLmFjdGl2ZSA9IHRydWU7XG4gICAgICAgIHRoaXMubGFzdFVwZGF0ZSA9IERhdGUubm93KCk7XG5cbiAgICAgICAgLy8gTWFzayB0aGUgbG93ZXIgNCBiaXRzIG9mIHRoZSBoZWFkZXJcbiAgICAgICAgLy8gdG8gZ2V0IHRoZSByYW5nZSBvZiBoZWFkZXIgdmFsdWVzXG4gICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLnBhY2tldEhhbmRsZXJzW2J1ZmZlclswXSEgJiAweGYwXTtcbiAgICAgICAgaWYgKGhhbmRsZXIpIHtcbiAgICAgICAgICAgIGhhbmRsZXIoYnVmZmVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZ2V0TG9nZ2VyKCkudmVyYm9zZShgUmVjZWl2ZWQgYW4gdW5rbm93biBwYWNrZXQgdHlwZT0ke2J1ZmZlclswXX1gKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlRnJhbWVTZXQoZnJhbWVTZXQ6IEZyYW1lU2V0KTogdm9pZCB7XG4gICAgICAgIC8vIFRPRE86IGFkZGl0aW9uYWwgc2FuaXR5IGNoZWNrc1xuICAgICAgICBpZiAodGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLmhhcyhmcmFtZVNldC5zZXF1ZW5jZU51bWJlcikpIHtcbiAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZ2V0TG9nZ2VyKCkuZGVidWcoYERpc2NhcmRlZCBkdXBsaWNhdGVkIHBhY2tldCBmcm9tIGNsaWVudD0ke3RoaXMuZ2V0QWRkcmVzcygpfWApO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gaWYgaXQgd2FzIG1pc3NpbmcsIHJlbW92ZSBmcm9tIHRoZSBxdWV1ZSBiZWNhdXNlIHdlIHJlY2VpdmVkIGl0IG5vd1xuICAgICAgICB0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcy5kZWxldGUoZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIpO1xuXG4gICAgICAgIC8vIEZvciBub3cgaXMgZ29vZCBsaWtlIHRoYXRcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIgPCB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyIHx8XG4gICAgICAgICAgICBmcmFtZVNldC5zZXF1ZW5jZU51bWJlciA9PT0gdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlclxuICAgICAgICApIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEFkZCB0aGUgZnJhbWUgdG8gdGhlIEFDSyBxdWV1ZVxuICAgICAgICB0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuYWRkKGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyKTtcblxuICAgICAgICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgbWlzc2luZyBwYWNrZXRzIGJldHdlZW4gdGhlIHJlY2VpdmVkIHBhY2tldCBhbmQgdGhlIGxhc3QgcmVjZWl2ZWQgb25lXG4gICAgICAgIGNvbnN0IGRpZmYgPSBmcmFtZVNldC5zZXF1ZW5jZU51bWJlciAtIHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXI7XG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIHNlcXVlbmNlIGhhcyBhIGhvbGUgZHVlIHRvIGEgbG9zdCBwYWNrZXRcbiAgICAgICAgaWYgKGRpZmYgIT09IDEpIHtcbiAgICAgICAgICAgIC8vIEFzIGkgc2FpZCBiZWZvcmUsIHRoZXJlIHdlIHNlYXJjaCBmb3IgbWlzc2luZyBwYWNrZXRzIGluIHRoZSBsaXN0IG9mIHRoZSByZWNpZXZlZCBvbmVzXG4gICAgICAgICAgICBmb3IgKGxldCBpID0gdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlciArIDE7IGkgPCBmcmFtZVNldC5zZXF1ZW5jZU51bWJlcjsgaSsrKSB7XG4gICAgICAgICAgICAgICAgLy8gQWRkaW5nIHRoZSBwYWNrZXQgc2VxdWVuY2UgbnVtYmVyIHRvIHRoZSBOQUNLIHF1ZXVlIGFuZCB0aGVuIHNlbmRpbmcgYSBOQUNLXG4gICAgICAgICAgICAgICAgLy8gd2lsbCBtYWtlIHRoZSBDbGllbnQgc2VuZGluZyBhZ2FpbiB0aGUgbG9zdCBwYWNrZXRcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5oYXMoaSkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuYWRkKGkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIHdlIHJlY2VpdmVkIGEgbG9zdCBwYWNrZXQgd2Ugc2VudCBpbiBOQUNLIG9yIGEgbm9ybWFsIHNlcXVlbmNlZCBvbmVcbiAgICAgICAgdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlciA9IGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyO1xuXG4gICAgICAgIC8vIEhhbmRsZSBlbmNhcHN1bGF0ZWRcbiAgICAgICAgZm9yIChjb25zdCBmcmFtZSBvZiBmcmFtZVNldC5mcmFtZXMpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRnJhbWUoZnJhbWUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVBQ0soYWNrOiBBQ0spOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogcGluZyBjYWxjdWxhdGlvblxuXG4gICAgICAgIGZvciAoY29uc3Qgc2VxIG9mIGFjay5zZXF1ZW5jZU51bWJlcnMpIHtcbiAgICAgICAgICAgIHRoaXMub3V0cHV0QmFja3VwUXVldWUuZGVsZXRlKHNlcSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZU5BQ0sobmFjazogTkFDSyk6IHZvaWQge1xuICAgICAgICBmb3IgKGNvbnN0IHNlcSBvZiBuYWNrLnNlcXVlbmNlTnVtYmVycykge1xuICAgICAgICAgICAgaWYgKHRoaXMub3V0cHV0QmFja3VwUXVldWUuaGFzKHNlcSkpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBsb3N0RnJhbWVzID0gdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5nZXQoc2VxKSE7XG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBsb3N0RnJhbWUgb2YgbG9zdEZyYW1lcykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNlbmRGcmFtZShsb3N0RnJhbWUsIFJha05ldFByaW9yaXR5LklNTUVESUFURSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHRoaXMub3V0cHV0QmFja3VwUXVldWUuZGVsZXRlKHNlcSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZUZyYW1lKGZyYW1lOiBGcmFtZSk6IHZvaWQge1xuICAgICAgICBpZiAoZnJhbWUuaXNGcmFnbWVudGVkKCkpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRnJhZ21lbnQoZnJhbWUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgb3JkZXJDaGFubmVsID0gZnJhbWUub3JkZXJDaGFubmVsITtcbiAgICAgICAgY29uc3Qgb3JkZXJJbmRleCA9IGZyYW1lLm9yZGVySW5kZXghO1xuICAgICAgICBjb25zdCBzZXF1ZW5jZUluZGV4ID0gZnJhbWUuc2VxdWVuY2VJbmRleCE7XG5cbiAgICAgICAgaWYgKGZyYW1lLmlzU2VxdWVuY2VkKCkpIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBzZXF1ZW5jZUluZGV4IDwgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4W29yZGVyQ2hhbm5lbF0hIHx8XG4gICAgICAgICAgICAgICAgb3JkZXJJbmRleCA8IHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0hXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAvLyBTZXF1ZW5jZWQgcGFja2V0IGlzIHRvbyBvbGQsIGRpc2NhcmQgaXRcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdID0gc2VxdWVuY2VJbmRleCArIDE7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVBhY2tldChmcmFtZSk7XG4gICAgICAgIH0gZWxzZSBpZiAoZnJhbWUuaXNPcmRlcmVkKCkpIHtcbiAgICAgICAgICAgIGlmIChvcmRlckluZGV4ID09PSB0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4W29yZGVyQ2hhbm5lbF0gPSAwO1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0gPSBvcmRlckluZGV4ICsgMTtcblxuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcbiAgICAgICAgICAgICAgICBsZXQgaSA9IHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0hO1xuICAgICAgICAgICAgICAgIGNvbnN0IG91dE9mT3JkZXJRdWV1ZSA9IHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLmdldChvcmRlckNoYW5uZWwpITtcbiAgICAgICAgICAgICAgICBmb3IgKDsgb3V0T2ZPcmRlclF1ZXVlLmhhcyhpKTsgaSsrKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KG91dE9mT3JkZXJRdWV1ZS5nZXQoaSkhKTtcbiAgICAgICAgICAgICAgICAgICAgb3V0T2ZPcmRlclF1ZXVlLmRlbGV0ZShpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBTZXQgdGhlIHVwZGF0ZWQgcXVldWVcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0T3JkZXJpbmdRdWV1ZS5zZXQob3JkZXJDaGFubmVsLCBvdXRPZk9yZGVyUXVldWUpO1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0gPSBpO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChvcmRlckluZGV4ID4gdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSEpIHtcbiAgICAgICAgICAgICAgICBjb25zdCB1bm9yZGVyZWQgPSB0aGlzLmlucHV0T3JkZXJpbmdRdWV1ZS5nZXQob3JkZXJDaGFubmVsKSE7XG4gICAgICAgICAgICAgICAgdW5vcmRlcmVkLnNldChvcmRlckluZGV4LCBmcmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVBhY2tldChmcmFtZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgc2VuZEZyYW1lKGZyYW1lOiBGcmFtZSwgZmxhZ3MgPSBSYWtOZXRQcmlvcml0eS5OT1JNQUwpOiB2b2lkIHtcbiAgICAgICAgYXNzZXJ0KHR5cGVvZiBmcmFtZS5vcmRlckNoYW5uZWwgPT09ICdudW1iZXInLCAnRnJhbWUgT3JkZXJDaGFubmVsIGNhbm5vdCBiZSBudWxsJyk7XG5cbiAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi8xYTE2OTg5NWE5MDBjOWZjNDg0MWM1NTZlMTY1MTQxODJiNzVmYWY4L1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTYyNVxuICAgICAgICBpZiAoZnJhbWUuaXNTZXF1ZW5jZWQoKSkge1xuICAgICAgICAgICAgLy8gU2VxdWVuY2VkIHBhY2tldHMgZG9uJ3QgaW5jcmVhc2UgdGhlIG9yZGVyZWQgY2hhbm5lbCBpbmRleFxuICAgICAgICAgICAgZnJhbWUub3JkZXJJbmRleCA9IHRoaXMub3V0cHV0T3JkZXJJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdITtcbiAgICAgICAgICAgIGZyYW1lLnNlcXVlbmNlSW5kZXggPSB0aGlzLm91dHB1dFNlcXVlbmNlSW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSErKztcbiAgICAgICAgfSBlbHNlIGlmIChmcmFtZS5pc09yZGVyZWRFeGNsdXNpdmUoKSkge1xuICAgICAgICAgICAgLy8gaW1wbGllcyBzZXF1ZW5jZWQsIGJ1dCB3ZSBoYXZlIHRvIGRpc3RpbmN0IHRoZW1cbiAgICAgICAgICAgIGZyYW1lLm9yZGVySW5kZXggPSB0aGlzLm91dHB1dE9yZGVySW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSErKztcbiAgICAgICAgICAgIHRoaXMub3V0cHV0U2VxdWVuY2VJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdID0gMDtcbiAgICAgICAgfVxuXG4gICAgICAgIGZyYW1lLnJlbGlhYmxlSW5kZXggPSB0aGlzLm91dHB1dFJlbGlhYmxlSW5kZXgrKztcblxuICAgICAgICAvLyBTcGxpdCBwYWNrZXQgaWYgYmlnZ2VyIHRoYW4gTVRVIHNpemVcbiAgICAgICAgY29uc3QgbWF4U2l6ZSA9IHRoaXMuZ2V0TVRVKCkgLSBEQVRBR1JBTV9IRUFERVJfQllURV9MRU5HVEggLSBNQVhfRlJBTUVfQllURV9MRU5HVEg7XG4gICAgICAgIGlmIChmcmFtZS5jb250ZW50LmJ5dGVMZW5ndGggPiBtYXhTaXplKSB7XG4gICAgICAgICAgICAvLyBJZiB3ZSB1c2UgdGhlIGZyYW1lIGFzIHJlZmVyZW5jZSwgd2UgaGF2ZSB0byBjb3B5IHNvbWV3aGVyZVxuICAgICAgICAgICAgLy8gdGhlIG9yaWdpbmFsIGJ1ZmZlci4gVGhlbiB3ZSB3aWxsIHBvaW50IHRvIHRoaXMgYnVmZmVyIGNvbnRlbnRcbiAgICAgICAgICAgIGNvbnN0IGJ1ZmZlciA9IEJ1ZmZlci5mcm9tKGZyYW1lLmNvbnRlbnQpO1xuICAgICAgICAgICAgY29uc3QgZnJhZ21lbnRJZCA9IHRoaXMub3V0cHV0RnJhZ21lbnRJbmRleCsrICUgNjU1MzY7XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ1ZmZlci5ieXRlTGVuZ3RoOyBpICs9IG1heFNpemUpIHtcbiAgICAgICAgICAgICAgICAvLyBMaWtlIHRoZSBvcmlnaW5hbCByYWtuZXQsIHdlIGxpa2UgdG8gdXNlIGEgcG9pbnRlciB0byB0aGUgb3JpZ2luYWxcbiAgICAgICAgICAgICAgICAvLyBmcmFtZSwgd2UgZG9uJ3QgcmVhbGx5IGNhcmUgYWJvdXQgc2lkZSBlZmZlY3RzIGluIHRoaXMgY2FzZS5cbiAgICAgICAgICAgICAgICAvLyBSYWtOZXQgd2lsbCBhbGxvY2F0ZSB0aGUgd2hvbGUgc3RyY3R1cmUgY29udGFpbmluZyBzcGxpdHMsXG4gICAgICAgICAgICAgICAgLy8gYnV0IGkgdGhpbmsgY2FjaGluZyBqdXN0IHRoZSBidWZmZXIgaXMgZW5vdWdoLlxuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvMWExNjk4OTVhOTAwYzlmYzQ4NDFjNTU2ZTE2NTE0MTgyYjc1ZmFmOC9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDI5NjNcblxuICAgICAgICAgICAgICAgIC8vIFNraXAgdGhlIGZpcnN0IGluZGV4IGFzIGl0J3MgYWxyZWFkeSBpbmNyZWFzZWQgYnkgaXRzZWxmLlxuICAgICAgICAgICAgICAgIGlmIChpICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGZyYW1lLnJlbGlhYmxlSW5kZXggPSB0aGlzLm91dHB1dFJlbGlhYmxlSW5kZXgrKztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBmcmFtZS5jb250ZW50ID0gYnVmZmVyLnNsaWNlKGksIGkgKyBtYXhTaXplKTtcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudEluZGV4ID0gaSAvIG1heFNpemU7XG4gICAgICAgICAgICAgICAgZnJhbWUuZnJhZ21lbnRJZCA9IGZyYWdtZW50SWQ7XG4gICAgICAgICAgICAgICAgZnJhbWUuZnJhZ21lbnRTaXplID0gTWF0aC5jZWlsKGJ1ZmZlci5ieXRlTGVuZ3RoIC8gbWF4U2l6ZSk7XG4gICAgICAgICAgICAgICAgdGhpcy5hZGRGcmFtZVRvUXVldWUoZnJhbWUsIGZsYWdzIHwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuYWRkRnJhbWVUb1F1ZXVlKGZyYW1lLCBmbGFncyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFkZEZyYW1lVG9RdWV1ZShmcmFtZTogRnJhbWUsIHByaW9yaXR5ID0gUmFrTmV0UHJpb3JpdHkuTk9STUFMKTogdm9pZCB7XG4gICAgICAgIGxldCBsZW5ndGggPSA0OyAvLyBkYXRhZ3JhbSBoZWFkZXIgc2l6ZVxuICAgICAgICBmb3IgKGNvbnN0IHF1ZXVlZEZyYW1lIG9mIHRoaXMub3V0cHV0RnJhbWVRdWV1ZS5mcmFtZXMpIHtcbiAgICAgICAgICAgIGxlbmd0aCArPSBxdWV1ZWRGcmFtZS5nZXRCeXRlTGVuZ3RoKCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobGVuZ3RoICsgZnJhbWUuZ2V0Qnl0ZUxlbmd0aCgpID4gdGhpcy5tdHVTaXplIC0gMzYpIHtcbiAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lUXVldWUoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMub3V0cHV0RnJhbWVRdWV1ZS5mcmFtZXMucHVzaChmcmFtZSk7XG5cbiAgICAgICAgaWYgKHByaW9yaXR5ID09PSBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpIHtcbiAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lUXVldWUoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlUGFja2V0KHBhY2tldDogRnJhbWUpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgaWQgPSBwYWNrZXQuY29udGVudFswXTtcblxuICAgICAgICBpZiAodGhpcy5zdGF0ZSA9PT0gU2Vzc2lvblN0YXR1cy5DT05ORUNUSU5HKSB7XG4gICAgICAgICAgICBpZiAoaWQgPT09IE1lc3NhZ2VJZGVudGlmaWVycy5DT05ORUNUSU9OX1JFUVVFU1QpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUNvbm5lY3Rpb25SZXF1ZXN0KHBhY2tldC5jb250ZW50KS50aGVuKFxuICAgICAgICAgICAgICAgICAgICAoZW5jYXBzdWxhdGVkKSA9PiB0aGlzLnNlbmRGcmFtZShlbmNhcHN1bGF0ZWQsIFJha05ldFByaW9yaXR5LklNTUVESUFURSksXG4gICAgICAgICAgICAgICAgICAgICgpID0+IHt9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaWQgPT09IE1lc3NhZ2VJZGVudGlmaWVycy5ORVdfSU5DT01JTkdfQ09OTkVDVElPTikge1xuICAgICAgICAgICAgICAgIC8vIFRPRE86IG9ubGluZSBtb2RlXG4gICAgICAgICAgICAgICAgdGhpcy5zdGF0ZSA9IFNlc3Npb25TdGF0dXMuQ09OTkVDVEVEO1xuICAgICAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZW1pdCgnb3BlbkNvbm5lY3Rpb24nLCB0aGlzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLkRJU0NPTk5FQ1RJT05fTk9USUZJQ0FUSU9OKSB7XG4gICAgICAgICAgICB0aGlzLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgfSBlbHNlIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLkNPTk5FQ1RFRF9QSU5HKSB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUNvbm5lY3RlZFBpbmcocGFja2V0LmNvbnRlbnQpLnRoZW4oXG4gICAgICAgICAgICAgICAgKGVuY2Fwc3VsYXRlZCkgPT4gdGhpcy5zZW5kRnJhbWUoZW5jYXBzdWxhdGVkKSxcbiAgICAgICAgICAgICAgICAoKSA9PiB7fVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIGlmICh0aGlzLnN0YXRlID09PSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RFRCkge1xuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lbWl0KCdlbmNhcHN1bGF0ZWQnLCBwYWNrZXQsIHRoaXMuZ2V0QWRkcmVzcygpKTsgLy8gVG8gZml0IGluIHNvZnR3YXJlIG5lZWRzIGxhdGVyXG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgaGFuZGxlQ29ubmVjdGlvblJlcXVlc3QoYnVmZmVyOiBCdWZmZXIpOiBQcm9taXNlPEZyYW1lPiB7XG4gICAgICAgIGNvbnN0IGRhdGFQYWNrZXQgPSBuZXcgQ29ubmVjdGlvblJlcXVlc3QoYnVmZmVyKTtcbiAgICAgICAgZGF0YVBhY2tldC5kZWNvZGUoKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBDb25uZWN0aW9uUmVxdWVzdEFjY2VwdGVkKCk7XG4gICAgICAgIHBrLmNsaWVudEFkZHJlc3MgPSB0aGlzLmdldEFkZHJlc3MoKTtcbiAgICAgICAgcGsucmVxdWVzdFRpbWVzdGFtcCA9IGRhdGFQYWNrZXQucmVxdWVzdFRpbWVzdGFtcDtcbiAgICAgICAgcGsuYWNjZXB0ZWRUaW1lc3RhbXAgPSBCaWdJbnQoRGF0ZS5ub3coKSk7XG4gICAgICAgIHBrLmVuY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHNlbmRQYWNrZXQgPSBuZXcgRnJhbWUoKTtcbiAgICAgICAgc2VuZFBhY2tldC5yZWxpYWJpbGl0eSA9IEZyYW1lUmVsaWFiaWxpdHkuVU5SRUxJQUJMRTtcbiAgICAgICAgc2VuZFBhY2tldC5vcmRlckNoYW5uZWwgPSAwO1xuICAgICAgICBzZW5kUGFja2V0LmNvbnRlbnQgPSBway5nZXRCdWZmZXIoKTtcblxuICAgICAgICByZXR1cm4gc2VuZFBhY2tldDtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgaGFuZGxlQ29ubmVjdGVkUGluZyhidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8RnJhbWU+IHtcbiAgICAgICAgY29uc3QgZGF0YVBhY2tldCA9IG5ldyBDb25uZWN0ZWRQaW5nKGJ1ZmZlcik7XG4gICAgICAgIGRhdGFQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgY29uc3QgcGsgPSBuZXcgQ29ubmVjdGVkUG9uZygpO1xuICAgICAgICBway5jbGllbnRUaW1lc3RhbXAgPSBkYXRhUGFja2V0LmNsaWVudFRpbWVzdGFtcDtcbiAgICAgICAgcGsuc2VydmVyVGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICBway5lbmNvZGUoKTtcblxuICAgICAgICBjb25zdCBzZW5kUGFja2V0ID0gbmV3IEZyYW1lKCk7XG4gICAgICAgIHNlbmRQYWNrZXQucmVsaWFiaWxpdHkgPSBGcmFtZVJlbGlhYmlsaXR5LlVOUkVMSUFCTEU7XG4gICAgICAgIHNlbmRQYWNrZXQub3JkZXJDaGFubmVsID0gMDtcbiAgICAgICAgc2VuZFBhY2tldC5jb250ZW50ID0gcGsuZ2V0QnVmZmVyKCk7XG5cbiAgICAgICAgcmV0dXJuIHNlbmRQYWNrZXQ7XG4gICAgfVxuXG4gICAgcHVibGljIGhhbmRsZUZyYWdtZW50KGZyYW1lOiBGcmFtZSk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuZnJhZ21lbnRzUXVldWUuaGFzKGZyYW1lLmZyYWdtZW50SWQpKSB7XG4gICAgICAgICAgICB0aGlzLmZyYWdtZW50c1F1ZXVlLnNldChmcmFtZS5mcmFnbWVudElkLCBuZXcgTWFwKFtbZnJhbWUuZnJhZ21lbnRJbmRleCwgZnJhbWVdXSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSB0aGlzLmZyYWdtZW50c1F1ZXVlLmdldChmcmFtZS5mcmFnbWVudElkKSE7XG4gICAgICAgICAgICB2YWx1ZS5zZXQoZnJhbWUuZnJhZ21lbnRJbmRleCwgZnJhbWUpO1xuXG4gICAgICAgICAgICAvLyBJZiB3ZSBoYXZlIGFsbCBwaWVjZXMsIHB1dCB0aGVtIHRvZ2V0aGVyXG4gICAgICAgICAgICBpZiAodmFsdWUuc2l6ZSA9PT0gZnJhbWUuZnJhZ21lbnRTaXplKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgc3RyZWFtID0gbmV3IEJpbmFyeVN0cmVhbSgpO1xuICAgICAgICAgICAgICAgIC8vIEVuc3VyZSB0aGUgY29ycmVjdG5lc3Mgb2YgdGhlIGJ1ZmZlciBvcmRlcnNcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHZhbHVlLnNpemU7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBzcGxpdFBhY2tldCA9IHZhbHVlLmdldChpKSE7XG4gICAgICAgICAgICAgICAgICAgIHN0cmVhbS53cml0ZShzcGxpdFBhY2tldC5jb250ZW50KTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBjb25zdCBhc3NlbWJsZWRGcmFtZSA9IG5ldyBGcmFtZSgpO1xuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLmNvbnRlbnQgPSBzdHJlYW0uZ2V0QnVmZmVyKCk7XG5cbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5yZWxpYWJpbGl0eSA9IGZyYW1lLnJlbGlhYmlsaXR5O1xuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLnJlbGlhYmxlSW5kZXggPSBmcmFtZS5yZWxpYWJsZUluZGV4O1xuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLnNlcXVlbmNlSW5kZXggPSBmcmFtZS5zZXF1ZW5jZUluZGV4O1xuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLm9yZGVySW5kZXggPSBmcmFtZS5vcmRlckluZGV4O1xuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLm9yZGVyQ2hhbm5lbCA9IGZyYW1lLm9yZGVyQ2hhbm5lbDtcblxuICAgICAgICAgICAgICAgIHRoaXMuZnJhZ21lbnRzUXVldWUuZGVsZXRlKGZyYW1lLmZyYWdtZW50SWQpO1xuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlRnJhbWUoYXNzZW1ibGVkRnJhbWUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHNlbmRGcmFtZVF1ZXVlKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5vdXRwdXRGcmFtZVF1ZXVlLmZyYW1lcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICB0aGlzLm91dHB1dEZyYW1lUXVldWUuc2VxdWVuY2VOdW1iZXIgPSB0aGlzLm91dHB1dFNlcXVlbmNlTnVtYmVyKys7XG4gICAgICAgICAgICB0aGlzLnNlbmRGcmFtZVNldCh0aGlzLm91dHB1dEZyYW1lUXVldWUpO1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlID0gbmV3IEZyYW1lU2V0KCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIHNlbmRGcmFtZVNldChmcmFtZVNldDogRnJhbWVTZXQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zZW5kUGFja2V0KGZyYW1lU2V0KTtcbiAgICAgICAgdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5zZXQoXG4gICAgICAgICAgICBmcmFtZVNldC5zZXF1ZW5jZU51bWJlcixcbiAgICAgICAgICAgIGZyYW1lU2V0LmZyYW1lcy5maWx0ZXIoKGZyYW1lKSA9PiBmcmFtZS5pc1JlbGlhYmxlKCkpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZW5kUGFja2V0KHBhY2tldDogUGFja2V0KTogdm9pZCB7XG4gICAgICAgIHRoaXMubGlzdGVuZXIuc2VuZFBhY2tldChwYWNrZXQsIHRoaXMucmluZm8pO1xuICAgIH1cblxuICAgIHB1YmxpYyBjbG9zZSgpOiB2b2lkIHtcbiAgICAgICAgY29uc3Qgc3RyZWFtID0gbmV3IEJpbmFyeVN0cmVhbShCdWZmZXIuZnJvbSgnXFx1MDAwMFxcdTAwMDBcXHUwMDA4XFx1MDAxNScsICdiaW5hcnknKSk7XG4gICAgICAgIHRoaXMuYWRkRnJhbWVUb1F1ZXVlKG5ldyBGcmFtZSgpLmZyb21CaW5hcnkoc3RyZWFtKSwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKTsgLy8gQ2xpZW50IGRpc2Njb25lY3QgcGFja2V0IDB4MTVcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBLaWNrIGEgY2xpZW50XG4gICAgICogQHBhcmFtIHJlYXNvbiAtIHRoZSByZWFzb24gbWVzc2FnZSwgb3B0aW9uYWxcbiAgICAgKi9cbiAgICBwdWJsaWMgZGlzY29ubmVjdChyZWFzb24gPSAnY2xpZW50IGRpc2Nvbm5lY3QnKTogdm9pZCB7XG4gICAgICAgIC8vIFRPRE86IHJld3JpdGUsIHdvcmtzIGJ1dCBjYW4gYmUgaW1wcm92ZWRcbiAgICAgICAgdGhpcy5jbG9zZSgpO1xuICAgICAgICAvLyBTZW5kIGRpc2Nvbm5lY3QgQUNLXG4gICAgICAgIHRoaXMuc3RhdGUgPSBTZXNzaW9uU3RhdHVzLkRJU0NPTk5FQ1RFRDtcbiAgICAgICAgdGhpcy51cGRhdGUoRGF0ZS5ub3coKSk7XG4gICAgICAgIHRoaXMubGlzdGVuZXIucmVtb3ZlU2Vzc2lvbih0aGlzLCByZWFzb24pO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTdGF0ZSgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5zdGF0ZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNBY3RpdmUoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLmFjdGl2ZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNEaXNjb25uZWN0ZWQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0aGlzLnN0YXRlID09PSBTZXNzaW9uU3RhdHVzLkRJU0NPTk5FQ1RFRDtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TGlzdGVuZXIoKTogUmFrTmV0TGlzdGVuZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5saXN0ZW5lcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBtYXhtaXVtIHRyYW5zZmVyIHVuaXRcbiAgICAgKiBmb3IgdGhpcyBjb25uZWN0aW9uLlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IHRoZSBVRFAgYWRqdXN0ZWQuXG4gICAgICovXG4gICAgcHVibGljIGdldE1UVSgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5tdHVTaXplIC0gVURQX0hFQURFUl9TSVpFO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRBZGRyZXNzKCk6IEluZXRBZGRyZXNzIHtcbiAgICAgICAgcmV0dXJuIG5ldyBJbmV0QWRkcmVzcyh0aGlzLnJpbmZvLmFkZHJlc3MsIHRoaXMucmluZm8ucG9ydCwgNCk7XG4gICAgfVxufVxuIl0sIm5hbWVzIjpbIlJha05ldFByaW9yaXR5IiwiU2Vzc2lvblN0YXR1cyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFxQlksSUFBQSxjQUFBLHFCQUFBQSxlQUFMLEtBQUE7QUFDSCxFQUFBQSxlQUFBLENBQUEsZUFBQSxDQUFBLFFBQUEsQ0FBQSxHQUFBLENBQUEsQ0FBQSxHQUFBLFFBQUE7QUFDQSxFQUFBQSxlQUFBLENBQUEsZUFBQSxDQUFBLFdBQUEsQ0FBQSxHQUFBLENBQUEsQ0FBQSxHQUFBLFdBQUE7QUFGUSxFQUFBQSxPQUFBQSxlQUFBQTtBQUFBLENBQUEsRUFBQSxjQUFBLElBQUEsRUFBQTtBQUtBLElBQUEsYUFBQSxxQkFBQUMsY0FBTCxLQUFBO0FBQ0gsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxZQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxZQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxXQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxXQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxlQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxlQUFBO0FBQ0EsRUFBQUEsY0FBQSxDQUFBLGNBQUEsQ0FBQSxjQUFBLENBQUEsR0FBQSxDQUFBLENBQUEsR0FBQSxjQUFBO0FBSlEsRUFBQUEsT0FBQUEsY0FBQUE7QUFBQSxDQUFBLEVBQUEsYUFBQSxJQUFBLEVBQUE7QUFPWixNQUFxQixPQUFRLENBQUE7QUFBQSxFQThCbEIsV0FDYyxDQUFBLFFBQUEsRUFDQSxPQUNELEVBQUEsS0FBQSxFQUNBLElBQ2xCLEVBQUE7QUFKbUIsSUFBQSxJQUFBLENBQUEsUUFBQSxHQUFBLFFBQUE7QUFDQSxJQUFBLElBQUEsQ0FB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