UNPKG

@jsprismarine/raknet

Version:
344 lines (343 loc) • 52.2 kB
import InetAddress from "./utils/InetAddress.es.js"; import { MessageIdentifiers } from "./protocol/MessageIdentifiers.es.js"; import ACK from "./protocol/ACK.es.js"; import { BitFlags } from "./protocol/BitFlags.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 { FrameReliability } from "./protocol/FrameReliability.es.js"; import Frame from "./protocol/Frame.es.js"; import FrameSet from "./protocol/FrameSet.es.js"; import NACK from "./protocol/NACK.es.js"; import "./Constants.es.js"; import PacketPool from "./protocol/PacketPool.es.js"; import BinaryStream from "@jsprismarine/jsbinaryutils"; import assert from "node:assert"; //#region src/Session.ts var RakNetPriority = /* @__PURE__ */ function(RakNetPriority) { RakNetPriority[RakNetPriority["NORMAL"] = 0] = "NORMAL"; RakNetPriority[RakNetPriority["IMMEDIATE"] = 1] = "IMMEDIATE"; return RakNetPriority; }({}); var SessionStatus = /* @__PURE__ */ function(SessionStatus) { SessionStatus[SessionStatus["CONNECTING"] = 0] = "CONNECTING"; SessionStatus[SessionStatus["CONNECTED"] = 1] = "CONNECTED"; SessionStatus[SessionStatus["DISCONNECTING"] = 2] = "DISCONNECTING"; SessionStatus[SessionStatus["DISCONNECTED"] = 3] = "DISCONNECTED"; return SessionStatus; }({}); var Session = class { listener; mtuSize; rinfo; guid; state = 0; outputFrameQueue = new FrameSet(); outputSequenceNumber = 0; outputReliableIndex = 0; outputBackupQueue = /* @__PURE__ */ new Map(); outputOrderIndex; outputSequenceIndex; receivedFrameSequences = /* @__PURE__ */ new Set(); lostFrameSequences = /* @__PURE__ */ new Set(); fragmentsQueue = /* @__PURE__ */ new Map(); outputFragmentIndex = 0; lastInputSequenceNumber = -1; inputHighestSequenceIndex; inputOrderIndex; inputOrderingQueue = /* @__PURE__ */ new Map(); lastUpdate = Date.now(); active = true; packetPool = new PacketPool(); 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(32).fill(0); this.outputSequenceIndex = new Array(32).fill(0); this.inputOrderIndex = new Array(32).fill(0); for (let i = 0; i < 32; i++) this.inputOrderingQueue.set(i, /* @__PURE__ */ new Map()); this.inputHighestSequenceIndex = new Array(32).fill(0); } packetHandlers = { [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(); } 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); if (frameSet.sequenceNumber - this.lastInputSequenceNumber !== 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); this.outputBackupQueue.delete(seq); } } handleFrame(frame) { if (frame.isFragmented()) { this.handleFragment(frame); return; } if (!frame.isOrdered()) { this.handlePacket(frame); return; } const orderChannel = frame.orderChannel; const orderIndex = frame.orderIndex; const expectedOrderIndex = this.inputOrderIndex[orderChannel]; if (orderIndex === expectedOrderIndex) if (frame.isSequenced()) { const sequenceIndex = frame.sequenceIndex; if (this.isOlderOrderedFrame(sequenceIndex, this.inputHighestSequenceIndex[orderChannel])) return; this.inputHighestSequenceIndex[orderChannel] = sequenceIndex + 1; this.handlePacket(frame); } else { this.inputOrderIndex[orderChannel] = orderIndex + 1; this.inputHighestSequenceIndex[orderChannel] = 0; this.handlePacket(frame); this.processOrderingHeap(orderChannel); } else if (!this.isOlderOrderedFrame(orderIndex, expectedOrderIndex)) this.inputOrderingQueue.get(orderChannel).set(orderIndex, frame); } processOrderingHeap(orderChannel) { const queue = this.inputOrderingQueue.get(orderChannel); let expectedIndex = this.inputOrderIndex[orderChannel]; while (queue.has(this.inputOrderIndex[orderChannel])) { const bufferedFrame = queue.get(expectedIndex); queue.delete(expectedIndex); if (bufferedFrame.isSequenced()) { const seqIndex = bufferedFrame.sequenceIndex; if (!this.isOlderOrderedFrame(seqIndex, this.inputHighestSequenceIndex[orderChannel])) { this.inputHighestSequenceIndex[orderChannel] = seqIndex + 1; this.handlePacket(bufferedFrame); } } else { expectedIndex++; this.inputOrderIndex[orderChannel] = expectedIndex; this.inputHighestSequenceIndex[orderChannel] = 0; this.handlePacket(bufferedFrame); } } } /** * Determines whether a packet is older than expected, handling wraparound. * @see https://github.com/facebookarchive/RakNet/blob/master/Source/ReliabilityLayer.cpp#L2901-L2920 */ isOlderOrderedFrame(newIndex, currentIndex, maxValue = 16777215) { const halfRange = Math.floor(maxValue / 2); if (currentIndex > halfRange) return newIndex >= currentIndex - halfRange && newIndex < currentIndex; else return newIndex >= currentIndex - halfRange + maxValue + 1 || newIndex < currentIndex; } sendFrame(frame, flags = 0) { 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() - 6 - 23; if (frame.content.byteLength > maxSize) { const buffer = Buffer.from(frame.content); const fragmentId = this.outputFragmentIndex++ % 65536; let fragmentIndex = 0; for (let i = 0; i < buffer.byteLength; i += maxSize) { if (i !== 0) frame.reliableIndex = this.outputReliableIndex++; frame.content = buffer.subarray(i, i + maxSize); frame.fragmentIndex = fragmentIndex++; frame.fragmentId = fragmentId; frame.fragmentSize = Math.ceil(buffer.byteLength / maxSize); this.addFrameToQueue(frame, flags | 1); } } else this.addFrameToQueue(frame, flags); } addFrameToQueue(frame, priority = 0) { 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) this.sendFrameQueue(); } handlePacket(packet) { const id = packet.content[0]; if (this.state === 0) { if (id === MessageIdentifiers.CONNECTION_REQUEST) this.handleConnectionRequest(packet.content).then((encapsulated) => this.sendFrame(encapsulated, 1), () => {}); else if (id === MessageIdentifiers.NEW_INCOMING_CONNECTION) { this.state = 1; 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) 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, new Map([[frame.fragmentIndex, frame]])); else { const entry = this.fragmentsQueue.get(frame.fragmentId); entry.set(frame.fragmentIndex, frame); if (entry.size === entry.values().next().value.fragmentSize) { const stream = new BinaryStream(); for (let i = 0; i < entry.size; i++) { const splitPacket = entry.get(i); if (!splitPacket) return; 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); } /** * Kick a client * @param reason - the reason message, optional */ disconnect(reason = "client disconnect") { this.close(); this.state = 3; this.update(Date.now()); this.listener.removeSession(this, reason); } getState() { return this.state; } isActive() { return this.active; } isDisconnected() { return this.state === 3; } getListener() { return this.listener; } /** * Returns the maxmium transfer unit * for this connection. * @returns {number} the UDP adjusted. */ getMTU() { return this.mtuSize - 28; } getAddress() { return new InetAddress(this.rinfo.address, this.rinfo.port, 4); } }; //#endregion export { RakNetPriority, SessionStatus, Session as default }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2Vzc2lvbi5lcy5qcyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi9zcmMvU2Vzc2lvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgQmluYXJ5U3RyZWFtIGZyb20gJ0Bqc3ByaXNtYXJpbmUvanNiaW5hcnl1dGlscyc7XG5pbXBvcnQgYXNzZXJ0IGZyb20gJ25vZGU6YXNzZXJ0JztcbmltcG9ydCB7IE1BWF9DSEFOTkVMUywgVURQX0hFQURFUl9TSVpFIH0gZnJvbSAnLi9Db25zdGFudHMnO1xuaW1wb3J0IEFDSyBmcm9tICcuL3Byb3RvY29sL0FDSyc7XG5pbXBvcnQgQml0RmxhZ3MgZnJvbSAnLi9wcm90b2NvbC9CaXRGbGFncyc7XG5pbXBvcnQgRnJhbWUsIHsgTUFYX0ZSQU1FX0JZVEVfTEVOR1RIIH0gZnJvbSAnLi9wcm90b2NvbC9GcmFtZSc7XG5pbXBvcnQgRnJhbWVSZWxpYWJpbGl0eSBmcm9tICcuL3Byb3RvY29sL0ZyYW1lUmVsaWFiaWxpdHknO1xuaW1wb3J0IEZyYW1lU2V0LCB7IERBVEFHUkFNX0hFQURFUl9CWVRFX0xFTkdUSCB9IGZyb20gJy4vcHJvdG9jb2wvRnJhbWVTZXQnO1xuaW1wb3J0IHsgTWVzc2FnZUlkZW50aWZpZXJzIH0gZnJvbSAnLi9wcm90b2NvbC9NZXNzYWdlSWRlbnRpZmllcnMnO1xuaW1wb3J0IE5BQ0sgZnJvbSAnLi9wcm90b2NvbC9OQUNLJztcbmltcG9ydCBQYWNrZXRQb29sIGZyb20gJy4vcHJvdG9jb2wvUGFja2V0UG9vbCc7XG5pbXBvcnQgQ29ubmVjdGVkUGluZyBmcm9tICcuL3Byb3RvY29sL2Nvbm5lY3Rpb24vQ29ubmVjdGVkUGluZyc7XG5pbXBvcnQgQ29ubmVjdGVkUG9uZyBmcm9tICcuL3Byb3RvY29sL2Nvbm5lY3Rpb24vQ29ubmVjdGVkUG9uZyc7XG5pbXBvcnQgQ29ubmVjdGlvblJlcXVlc3QgZnJvbSAnLi9wcm90b2NvbC9sb2dpbi9Db25uZWN0aW9uUmVxdWVzdCc7XG5pbXBvcnQgQ29ubmVjdGlvblJlcXVlc3RBY2NlcHRlZCBmcm9tICcuL3Byb3RvY29sL2xvZ2luL0Nvbm5lY3Rpb25SZXF1ZXN0QWNjZXB0ZWQnO1xuaW1wb3J0IEluZXRBZGRyZXNzIGZyb20gJy4vdXRpbHMvSW5ldEFkZHJlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IFJlbW90ZUluZm8gfSBmcm9tICdub2RlOmRncmFtJztcbmltcG9ydCB0eXBlIFJha05ldExpc3RlbmVyIGZyb20gJy4vU2VydmVyU29ja2V0JztcbmltcG9ydCB0eXBlIFBhY2tldCBmcm9tICcuL3Byb3RvY29sL1BhY2tldCc7XG5cbmV4cG9ydCBlbnVtIFJha05ldFByaW9yaXR5IHtcbiAgICBOT1JNQUwsXG4gICAgSU1NRURJQVRFXG59XG5cbmV4cG9ydCBlbnVtIFNlc3Npb25TdGF0dXMge1xuICAgIENPTk5FQ1RJTkcsXG4gICAgQ09OTkVDVEVELFxuICAgIERJU0NPTk5FQ1RJTkcsXG4gICAgRElTQ09OTkVDVEVEXG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlc3Npb24ge1xuICAgIHByaXZhdGUgc3RhdGUgPSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RJTkc7XG5cbiAgICBwcml2YXRlIG91dHB1dEZyYW1lUXVldWUgPSBuZXcgRnJhbWVTZXQoKTtcbiAgICBwcml2YXRlIG91dHB1dFNlcXVlbmNlTnVtYmVyID0gMDsgLy8gVE9ETzogZmluZCBhIGJldHRlciBuYW1lXG4gICAgcHJpdmF0ZSBvdXRwdXRSZWxpYWJsZUluZGV4ID0gMDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IG91dHB1dEJhY2t1cFF1ZXVlOiBNYXA8bnVtYmVyLCBGcmFtZVtdPiA9IG5ldyBNYXAoKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgb3V0cHV0T3JkZXJJbmRleDogbnVtYmVyW107XG4gICAgcHJpdmF0ZSByZWFkb25seSBvdXRwdXRTZXF1ZW5jZUluZGV4OiBudW1iZXJbXTtcblxuICAgIHByaXZhdGUgcmVjZWl2ZWRGcmFtZVNlcXVlbmNlczogU2V0PG51bWJlcj4gPSBuZXcgU2V0KCk7XG4gICAgcHJpdmF0ZSBsb3N0RnJhbWVTZXF1ZW5jZXM6IFNldDxudW1iZXI+ID0gbmV3IFNldCgpO1xuXG4gICAgLy8gTWFwIGhvbGRpbmcgZnJhZ21lbnRzIG9mIGZyYWdtZW50ZWQgcGFja2V0c1xuICAgIHByaXZhdGUgcmVhZG9ubHkgZnJhZ21lbnRzUXVldWU6IE1hcDxudW1iZXIsIE1hcDxudW1iZXIsIEZyYW1lPj4gPSBuZXcgTWFwKCk7XG4gICAgcHJpdmF0ZSBvdXRwdXRGcmFnbWVudEluZGV4ID0gMDtcblxuICAgIHByaXZhdGUgbGFzdElucHV0U2VxdWVuY2VOdW1iZXIgPSAtMTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXg6IG51bWJlcltdO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5wdXRPcmRlckluZGV4OiBudW1iZXJbXTtcbiAgICBwcml2YXRlIGlucHV0T3JkZXJpbmdRdWV1ZTogTWFwPG51bWJlciwgTWFwPG51bWJlciwgRnJhbWU+PiA9IG5ldyBNYXAoKTtcblxuICAgIC8vIExhc3QgdGltZXN0YW1wIG9mIHBhY2tldCByZWNlaXZlZCwgaGVscGZ1bCBmb3IgdGltZW91dFxuICAgIHByaXZhdGUgbGFzdFVwZGF0ZTogbnVtYmVyID0gRGF0ZS5ub3coKTtcbiAgICBwcml2YXRlIGFjdGl2ZSA9IHRydWU7XG5cbiAgICAvLyBQYWNrZXQgcG9vbCBpcyB0aGUgYmVzdCBvcHRpb24gdG8gcmVkdWNlIGFsbG9jYXRpb25zXG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRQb29sID0gbmV3IFBhY2tldFBvb2woKTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBsaXN0ZW5lcjogUmFrTmV0TGlzdGVuZXIsXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgbXR1U2l6ZTogbnVtYmVyLFxuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgcmluZm86IFJlbW90ZUluZm8sXG4gICAgICAgIHB1YmxpYyByZWFkb25seSBndWlkOiBiaWdpbnRcbiAgICApIHtcbiAgICAgICAgdGhpcy5sYXN0VXBkYXRlID0gRGF0ZS5ub3coKTtcblxuICAgICAgICB0aGlzLm91dHB1dE9yZGVySW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuICAgICAgICB0aGlzLm91dHB1dFNlcXVlbmNlSW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuXG4gICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4ID0gbmV3IEFycmF5KE1BWF9DSEFOTkVMUykuZmlsbCgwKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBNQVhfQ0hBTk5FTFM7IGkrKykge1xuICAgICAgICAgICAgdGhpcy5pbnB1dE9yZGVyaW5nUXVldWUuc2V0KGksIG5ldyBNYXAoKSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuICAgIH1cblxuICAgIC8vIExvb2t1cCB0YWJsZSBmb3IgcGFja2V0IGhhbmRsZXJzLCBhbHdheXMgTygxKSBpbiBhdmVyYWdlIGFuZCB3b3JzdCBjYXNlXG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRIYW5kbGVyczogUmVjb3JkPG51bWJlciwgKGJ1ZmZlcjogQnVmZmVyKSA9PiB2b2lkPiA9IHtcbiAgICAgICAgLy8gMHg0MCB8IDB4YzAgPSBNZXNzYWdlSGVhZGVycy5BQ0tOT1dMRURHRV9QQUNLRVRcbiAgICAgICAgW01lc3NhZ2VJZGVudGlmaWVycy5BQ0tOT1dMRURHRV9QQUNLRVRdOiAoYnVmZmVyOiBCdWZmZXIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGFjayA9IHRoaXMucGFja2V0UG9vbC5nZXRBY2tJbnN0YW5jZSgpO1xuICAgICAgICAgICAgKGFjayBhcyBhbnkpLmJ1ZmZlciA9IGJ1ZmZlcjtcbiAgICAgICAgICAgIGFjay5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlQUNLKGFjayk7XG4gICAgICAgIH0sXG4gICAgICAgIFtNZXNzYWdlSWRlbnRpZmllcnMuTkFDS05PV0xFREdFX1BBQ0tFVF06IChidWZmZXI6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgICAgY29uc3QgbmFjayA9IHRoaXMucGFja2V0UG9vbC5nZXROYWNrSW5zdGFuY2UoKTtcbiAgICAgICAgICAgIChuYWNrIGFzIGFueSkuYnVmZmVyID0gYnVmZmVyO1xuICAgICAgICAgICAgbmFjay5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlTkFDSyhuYWNrKTtcbiAgICAgICAgfSxcbiAgICAgICAgW0JpdEZsYWdzLlZBTElEXTogKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBmcmFtZVNldCA9IHRoaXMucGFja2V0UG9vbC5nZXRGcmFtZVNldEluc3RhbmNlKCk7XG4gICAgICAgICAgICAoZnJhbWVTZXQgYXMgYW55KS5idWZmZXIgPSBidWZmZXI7XG4gICAgICAgICAgICBmcmFtZVNldC5kZWNvZGUoKTtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlRnJhbWVTZXQoZnJhbWVTZXQpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHB1YmxpYyB1cGRhdGUodGltZXN0YW1wOiBudW1iZXIpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzQWN0aXZlKCkgJiYgIXRoaXMuaXNEaXNjb25uZWN0ZWQoKSAmJiB0aGlzLmxhc3RVcGRhdGUgKyAxMF8wMDAgPCB0aW1lc3RhbXApIHtcbiAgICAgICAgICAgIHRoaXMuZGlzY29ubmVjdCgndGltZW91dCcpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5hY3RpdmUgPSBmYWxzZTtcblxuICAgICAgICAvLyBUT0RPOiBhIHF1ZXVlIGp1c3QgZm9yIEFDS3Mgc2VxdWVuY2VzIHRvIGF2b2lkIGR1cGxpY2F0ZWRzXG4gICAgICAgIGlmICh0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuc2l6ZSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IGFjayA9IG5ldyBBQ0soKTtcbiAgICAgICAgICAgIGFjay5zZXF1ZW5jZU51bWJlcnMgPSBBcnJheS5mcm9tKHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcykubWFwKChzZXEpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuZGVsZXRlKHNlcSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNlcTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZW5kUGFja2V0KGFjayk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuc2l6ZSA+IDApIHtcbiAgICAgICAgICAgIGNvbnN0IHBrID0gbmV3IE5BQ0soKTtcbiAgICAgICAgICAgIHBrLnNlcXVlbmNlTnVtYmVycyA9IEFycmF5LmZyb20odGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMpLm1hcCgoc2VxKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuZGVsZXRlKHNlcSk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHNlcTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5zZW5kUGFja2V0KHBrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2VuZEZyYW1lUXVldWUoKTtcbiAgICB9XG5cbiAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iLzFhMTY5ODk1YTkwMGM5ZmM0ODQxYzU1NmUxNjUxNDE4MmI3NWZhZjgvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0w2MzVcbiAgICBwdWJsaWMgaGFuZGxlKGJ1ZmZlcjogQnVmZmVyKTogdm9pZCB7XG4gICAgICAgIHRoaXMuYWN0aXZlID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5sYXN0VXBkYXRlID0gRGF0ZS5ub3coKTtcblxuICAgICAgICAvLyBNYXNrIHRoZSBsb3dlciA0IGJpdHMgb2YgdGhlIGhlYWRlclxuICAgICAgICAvLyB0byBnZXQgdGhlIHJhbmdlIG9mIGhlYWRlciB2YWx1ZXNcbiAgICAgICAgY29uc3QgaGFuZGxlciA9IHRoaXMucGFja2V0SGFuZGxlcnNbYnVmZmVyWzBdISAmIDB4ZjBdO1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgICAgaGFuZGxlcihidWZmZXIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5nZXRMb2dnZXIoKS52ZXJib3NlKGBSZWNlaXZlZCBhbiB1bmtub3duIHBhY2tldCB0eXBlPSR7YnVmZmVyWzBdfWApO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVGcmFtZVNldChmcmFtZVNldDogRnJhbWVTZXQpOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogYWRkaXRpb25hbCBzYW5pdHkgY2hlY2tzXG4gICAgICAgIGlmICh0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuaGFzKGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyKSkge1xuICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5nZXRMb2dnZXIoKS5kZWJ1ZyhgRGlzY2FyZGVkIGR1cGxpY2F0ZWQgcGFja2V0IGZyb20gY2xpZW50PSR7dGhpcy5nZXRBZGRyZXNzKCl9YCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpZiBpdCB3YXMgbWlzc2luZywgcmVtb3ZlIGZyb20gdGhlIHF1ZXVlIGJlY2F1c2Ugd2UgcmVjZWl2ZWQgaXQgbm93XG4gICAgICAgIHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzLmRlbGV0ZShmcmFtZVNldC5zZXF1ZW5jZU51bWJlcik7XG5cbiAgICAgICAgLy8gRm9yIG5vdyBpcyBnb29kIGxpa2UgdGhhdFxuICAgICAgICBpZiAoXG4gICAgICAgICAgICBmcmFtZVNldC5zZXF1ZW5jZU51bWJlciA8IHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXIgfHxcbiAgICAgICAgICAgIGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyID09PSB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyXG4gICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQWRkIHRoZSBmcmFtZSB0byB0aGUgQUNLIHF1ZXVlXG4gICAgICAgIHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5hZGQoZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIpO1xuXG4gICAgICAgIC8vIENoZWNrIGlmIHRoZXJlIGFyZSBtaXNzaW5nIHBhY2tldHMgYmV0d2VlbiB0aGUgcmVjZWl2ZWQgcGFja2V0IGFuZCB0aGUgbGFzdCByZWNlaXZlZCBvbmVcbiAgICAgICAgY29uc3QgZGlmZiA9IGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyIC0gdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlcjtcblxuICAgICAgICAvLyBDaGVjayBpZiB0aGUgc2VxdWVuY2UgaGFzIGEgaG9sZSBkdWUgdG8gYSBsb3N0IHBhY2tldFxuICAgICAgICBpZiAoZGlmZiAhPT0gMSkge1xuICAgICAgICAgICAgLy8gQXMgaSBzYWlkIGJlZm9yZSwgdGhlcmUgd2Ugc2VhcmNoIGZvciBtaXNzaW5nIHBhY2tldHMgaW4gdGhlIGxpc3Qgb2YgdGhlIHJlY2lldmVkIG9uZXNcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyICsgMTsgaSA8IGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyOyBpKyspIHtcbiAgICAgICAgICAgICAgICAvLyBBZGRpbmcgdGhlIHBhY2tldCBzZXF1ZW5jZSBudW1iZXIgdG8gdGhlIE5BQ0sgcXVldWUgYW5kIHRoZW4gc2VuZGluZyBhIE5BQ0tcbiAgICAgICAgICAgICAgICAvLyB3aWxsIG1ha2UgdGhlIENsaWVudCBzZW5kaW5nIGFnYWluIHRoZSBsb3N0IHBhY2tldFxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLmhhcyhpKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcy5hZGQoaSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgd2UgcmVjZWl2ZWQgYSBsb3N0IHBhY2tldCB3ZSBzZW50IGluIE5BQ0sgb3IgYSBub3JtYWwgc2VxdWVuY2VkIG9uZVxuICAgICAgICB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyID0gZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXI7XG5cbiAgICAgICAgLy8gSGFuZGxlIGVuY2Fwc3VsYXRlZFxuICAgICAgICBmb3IgKGNvbnN0IGZyYW1lIG9mIGZyYW1lU2V0LmZyYW1lcykge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFtZShmcmFtZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZUFDSyhhY2s6IEFDSyk6IHZvaWQge1xuICAgICAgICAvLyBUT0RPOiBwaW5nIGNhbGN1bGF0aW9uXG5cbiAgICAgICAgZm9yIChjb25zdCBzZXEgb2YgYWNrLnNlcXVlbmNlTnVtYmVycykge1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5kZWxldGUoc2VxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlTkFDSyhuYWNrOiBOQUNLKTogdm9pZCB7XG4gICAgICAgIGZvciAoY29uc3Qgc2VxIG9mIG5hY2suc2VxdWVuY2VOdW1iZXJzKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5oYXMoc2VxKSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGxvc3RGcmFtZXMgPSB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLmdldChzZXEpITtcbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGxvc3RGcmFtZSBvZiBsb3N0RnJhbWVzKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lKGxvc3RGcmFtZSwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5vdXRwdXRCYWNrdXBRdWV1ZS5kZWxldGUoc2VxKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlRnJhbWUoZnJhbWU6IEZyYW1lKTogdm9pZCB7XG4gICAgICAgIGlmIChmcmFtZS5pc0ZyYWdtZW50ZWQoKSkge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFnbWVudChmcmFtZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBIYW5kbGUgcGFja2V0cyB3aXRob3V0IG9yZGVyaW5nXG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTI0OC1MMTI1MVxuICAgICAgICBpZiAoIWZyYW1lLmlzT3JkZXJlZCgpKSB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZVBhY2tldChmcmFtZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBvcmRlckNoYW5uZWwgPSBmcmFtZS5vcmRlckNoYW5uZWwhO1xuICAgICAgICBjb25zdCBvcmRlckluZGV4ID0gZnJhbWUub3JkZXJJbmRleCE7XG4gICAgICAgIGNvbnN0IGV4cGVjdGVkT3JkZXJJbmRleCA9IHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0hO1xuXG4gICAgICAgIC8vIEZpcnN0IG9mIGFsbCBjaGVjayB0aGUgb3JkZXJpbmdJbmRleFxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDEyODNcbiAgICAgICAgaWYgKG9yZGVySW5kZXggPT09IGV4cGVjdGVkT3JkZXJJbmRleCkge1xuICAgICAgICAgICAgaWYgKGZyYW1lLmlzU2VxdWVuY2VkKCkpIHtcbiAgICAgICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDEyODYtTDEyOTBcbiAgICAgICAgICAgICAgICBjb25zdCBzZXF1ZW5jZUluZGV4ID0gZnJhbWUuc2VxdWVuY2VJbmRleCE7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuaXNPbGRlck9yZGVyZWRGcmFtZShzZXF1ZW5jZUluZGV4LCB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSEpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDEzMTdcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSA9IHNlcXVlbmNlSW5kZXggKyAxO1xuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gSGFuZGxlIG9yZGVyZWQsIG5vbiBzZXF1ZW5jZWRcbiAgICAgICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDEzNzItTDEzNzNcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdID0gb3JkZXJJbmRleCArIDE7XG4gICAgICAgICAgICAgICAgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4W29yZGVyQ2hhbm5lbF0gPSAwO1xuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcblxuICAgICAgICAgICAgICAgIHRoaXMucHJvY2Vzc09yZGVyaW5nSGVhcChvcmRlckNoYW5uZWwpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmlzT2xkZXJPcmRlcmVkRnJhbWUob3JkZXJJbmRleCwgZXhwZWN0ZWRPcmRlckluZGV4KSkge1xuICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxNDI5LUwxNDQ2XG4gICAgICAgICAgICBjb25zdCBxdWV1ZSA9IHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLmdldChvcmRlckNoYW5uZWwpITtcbiAgICAgICAgICAgIHF1ZXVlLnNldChvcmRlckluZGV4LCBmcmFtZSk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBOTy1PUDogb3V0IG9mIG9yZGVyXG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTQ2NC1MMTQ2NlxuICAgIH1cblxuICAgIHByaXZhdGUgcHJvY2Vzc09yZGVyaW5nSGVhcChvcmRlckNoYW5uZWw6IG51bWJlcik6IHZvaWQge1xuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDEzNzYtTDE0MjNcbiAgICAgICAgY29uc3QgcXVldWUgPSB0aGlzLmlucHV0T3JkZXJpbmdRdWV1ZS5nZXQob3JkZXJDaGFubmVsKSE7XG4gICAgICAgIGxldCBleHBlY3RlZEluZGV4ID0gdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSE7XG5cbiAgICAgICAgd2hpbGUgKHF1ZXVlLmhhcyh0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdISkpIHtcbiAgICAgICAgICAgIGNvbnN0IGJ1ZmZlcmVkRnJhbWUgPSBxdWV1ZS5nZXQoZXhwZWN0ZWRJbmRleCkhO1xuICAgICAgICAgICAgcXVldWUuZGVsZXRlKGV4cGVjdGVkSW5kZXgpO1xuXG4gICAgICAgICAgICBpZiAoYnVmZmVyZWRGcmFtZS5pc1NlcXVlbmNlZCgpKSB7XG4gICAgICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxNDE1LUwxNDIyXG4gICAgICAgICAgICAgICAgY29uc3Qgc2VxSW5kZXggPSBidWZmZXJlZEZyYW1lLnNlcXVlbmNlSW5kZXghO1xuICAgICAgICAgICAgICAgIGlmICghdGhpcy5pc09sZGVyT3JkZXJlZEZyYW1lKHNlcUluZGV4LCB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSEpKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdID0gc2VxSW5kZXggKyAxO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVBhY2tldChidWZmZXJlZEZyYW1lKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTQxNS1MMTQxN1xuICAgICAgICAgICAgICAgIGV4cGVjdGVkSW5kZXgrKztcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdID0gZXhwZWN0ZWRJbmRleDtcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSA9IDA7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVQYWNrZXQoYnVmZmVyZWRGcmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEZXRlcm1pbmVzIHdoZXRoZXIgYSBwYWNrZXQgaXMgb2xkZXIgdGhhbiBleHBlY3RlZCwgaGFuZGxpbmcgd3JhcGFyb3VuZC5cbiAgICAgKiBAc2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMjkwMS1MMjkyMFxuICAgICAqL1xuICAgIHB1YmxpYyBpc09sZGVyT3JkZXJlZEZyYW1lKG5ld0luZGV4OiBudW1iZXIsIGN1cnJlbnRJbmRleDogbnVtYmVyLCBtYXhWYWx1ZTogbnVtYmVyID0gMHhmZmZmZmYpOiBib29sZWFuIHtcbiAgICAgICAgY29uc3QgaGFsZlJhbmdlID0gTWF0aC5mbG9vcihtYXhWYWx1ZSAvIDIpO1xuXG4gICAgICAgIGlmIChjdXJyZW50SW5kZXggPiBoYWxmUmFuZ2UpIHtcbiAgICAgICAgICAgIHJldHVybiBuZXdJbmRleCA+PSBjdXJyZW50SW5kZXggLSBoYWxmUmFuZ2UgJiYgbmV3SW5kZXggPCBjdXJyZW50SW5kZXg7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3SW5kZXggPj0gY3VycmVudEluZGV4IC0gaGFsZlJhbmdlICsgbWF4VmFsdWUgKyAxIHx8IG5ld0luZGV4IDwgY3VycmVudEluZGV4O1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHNlbmRGcmFtZShmcmFtZTogRnJhbWUsIGZsYWdzID0gUmFrTmV0UHJpb3JpdHkuTk9STUFMKTogdm9pZCB7XG4gICAgICAgIGFzc2VydCh0eXBlb2YgZnJhbWUub3JkZXJDaGFubmVsID09PSAnbnVtYmVyJywgJ0ZyYW1lIE9yZGVyQ2hhbm5lbCBjYW5ub3QgYmUgbnVsbCcpO1xuXG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvMWExNjk4OTVhOTAwYzlmYzQ4NDFjNTU2ZTE2NTE0MTgyYjc1ZmFmOC9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDE2MjVcbiAgICAgICAgaWYgKGZyYW1lLmlzU2VxdWVuY2VkKCkpIHtcbiAgICAgICAgICAgIC8vIFNlcXVlbmNlZCBwYWNrZXRzIGRvbid0IGluY3JlYXNlIHRoZSBvcmRlcmVkIGNoYW5uZWwgaW5kZXhcbiAgICAgICAgICAgIGZyYW1lLm9yZGVySW5kZXggPSB0aGlzLm91dHB1dE9yZGVySW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSE7XG4gICAgICAgICAgICBmcmFtZS5zZXF1ZW5jZUluZGV4ID0gdGhpcy5vdXRwdXRTZXF1ZW5jZUluZGV4W2ZyYW1lLm9yZGVyQ2hhbm5lbF0hKys7XG4gICAgICAgIH0gZWxzZSBpZiAoZnJhbWUuaXNPcmRlcmVkRXhjbHVzaXZlKCkpIHtcbiAgICAgICAgICAgIC8vIGltcGxpZXMgc2VxdWVuY2VkLCBidXQgd2UgaGF2ZSB0byBkaXN0aW5jdCB0aGVtXG4gICAgICAgICAgICBmcmFtZS5vcmRlckluZGV4ID0gdGhpcy5vdXRwdXRPcmRlckluZGV4W2ZyYW1lLm9yZGVyQ2hhbm5lbF0hKys7XG4gICAgICAgICAgICB0aGlzLm91dHB1dFNlcXVlbmNlSW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSA9IDA7XG4gICAgICAgIH1cblxuICAgICAgICBmcmFtZS5yZWxpYWJsZUluZGV4ID0gdGhpcy5vdXRwdXRSZWxpYWJsZUluZGV4Kys7XG5cbiAgICAgICAgLy8gU3BsaXQgcGFja2V0IGlmIGJpZ2dlciB0aGFuIE1UVSBzaXplXG4gICAgICAgIGNvbnN0IG1heFNpemUgPSB0aGlzLmdldE1UVSgpIC0gREFUQUdSQU1fSEVBREVSX0JZVEVfTEVOR1RIIC0gTUFYX0ZSQU1FX0JZVEVfTEVOR1RIO1xuICAgICAgICBpZiAoZnJhbWUuY29udGVudC5ieXRlTGVuZ3RoID4gbWF4U2l6ZSkge1xuICAgICAgICAgICAgLy8gSWYgd2UgdXNlIHRoZSBmcmFtZSBhcyByZWZlcmVuY2UsIHdlIGhhdmUgdG8gY29weSBzb21ld2hlcmVcbiAgICAgICAgICAgIC8vIHRoZSBvcmlnaW5hbCBidWZmZXIuIFRoZW4gd2Ugd2lsbCBwb2ludCB0byB0aGlzIGJ1ZmZlciBjb250ZW50XG4gICAgICAgICAgICBjb25zdCBidWZmZXIgPSBCdWZmZXIuZnJvbShmcmFtZS5jb250ZW50KTtcbiAgICAgICAgICAgIC8vIE9yaWdpbmFsIFJha05ldCBzYXlzOiBUaGlzIGlzIHRoZSBtYXhpbXVtIG51bWJlciBvZiBzcGxpdCBtZXNzYWdlcyB3ZSBjYW4gc2VuZCBzaW11bHRhbmVvdXNseSBwZXIgY29ubmVjdGlvbi5cbiAgICAgICAgICAgIGNvbnN0IGZyYWdtZW50SWQgPSB0aGlzLm91dHB1dEZyYWdtZW50SW5kZXgrKyAlIDY1NTM2O1xuICAgICAgICAgICAgbGV0IGZyYWdtZW50SW5kZXggPSAwO1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBidWZmZXIuYnl0ZUxlbmd0aDsgaSArPSBtYXhTaXplKSB7XG4gICAgICAgICAgICAgICAgLy8gTGlrZSB0aGUgb3JpZ2luYWwgcmFrbmV0LCB3ZSBsaWtlIHRvIHVzZSBhIHBvaW50ZXIgdG8gdGhlIG9yaWdpbmFsXG4gICAgICAgICAgICAgICAgLy8gZnJhbWUsIHdlIGRvbid0IHJlYWxseSBjYXJlIGFib3V0IHNpZGUgZWZmZWN0cyBpbiB0aGlzIGNhc2UuXG4gICAgICAgICAgICAgICAgLy8gUmFrTmV0IHdpbGwgYWxsb2NhdGUgdGhlIHdob2xlIHN0cmN0dXJlIGNvbnRhaW5pbmcgc3BsaXRzLFxuICAgICAgICAgICAgICAgIC8vIGJ1dCBpIHRoaW5rIGNhY2hpbmcganVzdCB0aGUgYnVmZmVyIGlzIGVub3VnaC5cbiAgICAgICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iLzFhMTY5ODk1YTkwMGM5ZmM0ODQxYzU1NmUxNjUxNDE4MmI3NWZhZjgvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wyOTYzXG5cbiAgICAgICAgICAgICAgICAvLyBTa2lwIHRoZSBmaXJzdCBpbmRleCBhcyBpdCdzIGFscmVhZHkgaW5jcmVhc2VkIGJ5IGl0c2VsZi5cbiAgICAgICAgICAgICAgICBpZiAoaSAhPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICBmcmFtZS5yZWxpYWJsZUluZGV4ID0gdGhpcy5vdXRwdXRSZWxpYWJsZUluZGV4Kys7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgZnJhbWUuY29udGVudCA9IGJ1ZmZlci5zdWJhcnJheShpLCBpICsgbWF4U2l6ZSk7XG4gICAgICAgICAgICAgICAgZnJhbWUuZnJhZ21lbnRJbmRleCA9IGZyYWdtZW50SW5kZXgrKztcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudElkID0gZnJhZ21lbnRJZDtcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudFNpemUgPSBNYXRoLmNlaWwoYnVmZmVyLmJ5dGVMZW5ndGggLyBtYXhTaXplKTtcbiAgICAgICAgICAgICAgICB0aGlzLmFkZEZyYW1lVG9RdWV1ZShmcmFtZSwgZmxhZ3MgfCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5hZGRGcmFtZVRvUXVldWUoZnJhbWUsIGZsYWdzKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYWRkRnJhbWVUb1F1ZXVlKGZyYW1lOiBGcmFtZSwgcHJpb3JpdHkgPSBSYWtOZXRQcmlvcml0eS5OT1JNQUwpOiB2b2lkIHtcbiAgICAgICAgbGV0IGxlbmd0aCA9IDQ7IC8vIGRhdGFncmFtIGhlYWRlciBzaXplXG4gICAgICAgIGZvciAoY29uc3QgcXVldWVkRnJhbWUgb2YgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlLmZyYW1lcykge1xuICAgICAgICAgICAgbGVuZ3RoICs9IHF1ZXVlZEZyYW1lLmdldEJ5dGVMZW5ndGgoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChsZW5ndGggKyBmcmFtZS5nZXRCeXRlTGVuZ3RoKCkgPiB0aGlzLm10dVNpemUgLSAzNikge1xuICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlLmZyYW1lcy5wdXNoKGZyYW1lKTtcblxuICAgICAgICBpZiAocHJpb3JpdHkgPT09IFJha05ldFByaW9yaXR5LklNTUVESUFURSkge1xuICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVQYWNrZXQocGFja2V0OiBGcmFtZSk6IHZvaWQge1xuICAgICAgICBjb25zdCBpZCA9IHBhY2tldC5jb250ZW50WzBdO1xuXG4gICAgICAgIGlmICh0aGlzLnN0YXRlID09PSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RJTkcpIHtcbiAgICAgICAgICAgIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLkNPTk5FQ1RJT05fUkVRVUVTVCkge1xuICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlQ29ubmVjdGlvblJlcXVlc3QocGFja2V0LmNvbnRlbnQpLnRoZW4oXG4gICAgICAgICAgICAgICAgICAgIChlbmNhcHN1bGF0ZWQpID0+IHRoaXMuc2VuZEZyYW1lKGVuY2Fwc3VsYXRlZCwgUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKSxcbiAgICAgICAgICAgICAgICAgICAgKCkgPT4ge31cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfSBlbHNlIGlmIChpZCA9PT0gTWVzc2FnZUlkZW50aWZpZXJzLk5FV19JTkNPTUlOR19DT05ORUNUSU9OKSB7XG4gICAgICAgICAgICAgICAgLy8gVE9ETzogb25saW5lIG1vZGVcbiAgICAgICAgICAgICAgICB0aGlzLnN0YXRlID0gU2Vzc2lvblN0YXR1cy5DT05ORUNURUQ7XG4gICAgICAgICAgICAgICAgdGhpcy5saXN0ZW5lci5lbWl0KCdvcGVuQ29ubmVjdGlvbicsIHRoaXMpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuRElTQ09OTkVDVElPTl9OT1RJRklDQVRJT04pIHtcbiAgICAgICAgICAgIHRoaXMuZGlzY29ubmVjdCgpO1xuICAgICAgICB9IGVsc2UgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuQ09OTkVDVEVEX1BJTkcpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlQ29ubmVjdGVkUGluZyhwYWNrZXQuY29udGVudCkudGhlbihcbiAgICAgICAgICAgICAgICAoZW5jYXBzdWxhdGVkKSA9PiB0aGlzLnNlbmRGcmFtZShlbmNhcHN1bGF0ZWQpLFxuICAgICAgICAgICAgICAgICgpID0+IHt9XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2UgaWYgKHRoaXMuc3RhdGUgPT09IFNlc3Npb25TdGF0dXMuQ09OTkVDVEVEKSB7XG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmVtaXQoJ2VuY2Fwc3VsYXRlZCcsIHBhY2tldCwgdGhpcy5nZXRBZGRyZXNzKCkpOyAvLyBUbyBmaXQgaW4gc29mdHdhcmUgbmVlZHMgbGF0ZXJcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBoYW5kbGVDb25uZWN0aW9uUmVxdWVzdChidWZmZXI6IEJ1ZmZlcik6IFByb21pc2U8RnJhbWU+IHtcbiAgICAgICAgY29uc3QgZGF0YVBhY2tldCA9IG5ldyBDb25uZWN0aW9uUmVxdWVzdChidWZmZXIpO1xuICAgICAgICBkYXRhUGFja2V0LmRlY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IENvbm5lY3Rpb25SZXF1ZXN0QWNjZXB0ZWQoKTtcbiAgICAgICAgcGsuY2xpZW50QWRkcmVzcyA9IHRoaXMuZ2V0QWRkcmVzcygpO1xuICAgICAgICBway5yZXF1ZXN0VGltZXN0YW1wID0gZGF0YVBhY2tldC5yZXF1ZXN0VGltZXN0YW1wO1xuICAgICAgICBway5hY2NlcHRlZFRpbWVzdGFtcCA9IEJpZ0ludChEYXRlLm5vdygpKTtcbiAgICAgICAgcGsuZW5jb2RlKCk7XG5cbiAgICAgICAgY29uc3Qgc2VuZFBhY2tldCA9IG5ldyBGcmFtZSgpO1xuICAgICAgICBzZW5kUGFja2V0LnJlbGlhYmlsaXR5ID0gRnJhbWVSZWxpYWJpbGl0eS5VTlJFTElBQkxFO1xuICAgICAgICBzZW5kUGFja2V0Lm9yZGVyQ2hhbm5lbCA9IDA7XG4gICAgICAgIHNlbmRQYWNrZXQuY29udGVudCA9IHBrLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgIHJldHVybiBzZW5kUGFja2V0O1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBoYW5kbGVDb25uZWN0ZWRQaW5nKGJ1ZmZlcjogQnVmZmVyKTogUHJvbWlzZTxGcmFtZT4ge1xuICAgICAgICBjb25zdCBkYXRhUGFja2V0ID0gbmV3IENvbm5lY3RlZFBpbmcoYnVmZmVyKTtcbiAgICAgICAgZGF0YVBhY2tldC5kZWNvZGUoKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBDb25uZWN0ZWRQb25nKCk7XG4gICAgICAgIHBrLmNsaWVudFRpbWVzdGFtcCA9IGRhdGFQYWNrZXQuY2xpZW50VGltZXN0YW1wO1xuICAgICAgICBway5zZXJ2ZXJUaW1lc3RhbXAgPSBCaWdJbnQoRGF0ZS5ub3coKSk7XG4gICAgICAgIHBrLmVuY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHNlbmRQYWNrZXQgPSBuZXcgRnJhbWUoKTtcbiAgICAgICAgc2VuZFBhY2tldC5yZWxpYWJpbGl0eSA9IEZyYW1lUmVsaWFiaWxpdHkuVU5SRUxJQUJMRTtcbiAgICAgICAgc2VuZFBhY2tldC5vcmRlckNoYW5uZWwgPSAwO1xuICAgICAgICBzZW5kUGFja2V0LmNvbnRlbnQgPSBway5nZXRCdWZmZXIoKTtcblxuICAgICAgICByZXR1cm4gc2VuZFBhY2tldDtcbiAgICB9XG5cbiAgICBwdWJsaWMgaGFuZGxlRnJhZ21lbnQoZnJhbWU6IEZyYW1lKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5mcmFnbWVudHNRdWV1ZS5oYXMoZnJhbWUuZnJhZ21lbnRJZCkpIHtcbiAgICAgICAgICAgIHRoaXMuZnJhZ21lbnRzUXVldWUuc2V0KGZyYW1lLmZyYWdtZW50SWQsIG5ldyBNYXAoW1tmcmFtZS5mcmFnbWVudEluZGV4LCBmcmFtZV1dKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBlbnRyeSA9IHRoaXMuZnJhZ21lbnRzUXVldWUuZ2V0KGZyYW1lLmZyYWdtZW50SWQpITtcbiAgICAgICAgICAgIGVudHJ5LnNldChmcmFtZS5mcmFnbWVudEluZGV4LCBmcmFtZSk7XG5cbiAgICAgICAgICAgIC8vIElmIHdlIGhhdmUgYWxsIHBpZWNlcywgcHV0IHRoZW0gdG9nZXRoZXJcbiAgICAgICAgICAgIGlmIChlbnRyeS5zaXplID09PSBlbnRyeS52YWx1ZXMoKS5uZXh0KCkudmFsdWUhLmZyYWdtZW50U2l6ZSkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHN0cmVhbSA9IG5ldyBCaW5hcnlTdHJlYW0oKTtcbiAgICAgICAgICAgICAgICAvLyBFbnN1cmUgdGhlIGNvcnJlY3RuZXNzIG9mIHRoZSBidWZmZXIgb3JkZXJzXG4gICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBlbnRyeS5zaXplOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3Qgc3BsaXRQYWNrZXQgPSBlbnRyeS5nZXQoaSk7XG4gICAgICAgICAgICAgICAgICAgIC8vIEEgYnJva2VuIG9yIG1hbGljaW91cyBjbGllbnQgbWF5IHNlbmQgaG9sZXNcbiAgICAgICAgICAgICAgICAgICAgLy8gdGhhdCBjb3VsZCBiZSBhIHBvc3NpYmxlIGlzc3VlXG4gICAgICAgICAgICAgICAgICAgIGlmICghc3BsaXRQYWNrZXQpIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgc3RyZWFtLndyaXRlKHNwbGl0UGFja2V0LmNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IGFzc2VtYmxlZEZyYW1lID0gbmV3IEZyYW1lKCk7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUuY29udGVudCA9IHN0cmVhbS5nZXRCdWZmZXIoKTtcblxuICAgICAgICAgICAgICAgIGFzc2VtYmxlZEZyYW1lLnJlbGlhYmlsaXR5ID0gZnJhbWUucmVsaWFiaWxpdHk7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUucmVsaWFibGVJbmRleCA9IGZyYW1lLnJlbGlhYmxlSW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUuc2VxdWVuY2VJbmRleCA9IGZyYW1lLnNlcXVlbmNlSW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUub3JkZXJJbmRleCA9IGZyYW1lLm9yZGVySW5kZXg7XG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUub3JkZXJDaGFubmVsID0gZnJhbWUub3JkZXJDaGFubmVsO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5mcmFnbWVudHNRdWV1ZS5kZWxldGUoZnJhbWUuZnJhZ21lbnRJZCk7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFtZShhc3NlbWJsZWRGcmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgc2VuZEZyYW1lUXVldWUoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLm91dHB1dEZyYW1lUXVldWUuZnJhbWVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHRoaXMub3V0cHV0RnJhbWVRdWV1ZS5zZXF1ZW5jZU51bWJlciA9IHRoaXMub3V0cHV0U2VxdWVuY2VOdW1iZXIrKztcbiAgICAgICAgICAgIHRoaXMuc2VuZEZyYW1lU2V0KHRoaXMub3V0cHV0RnJhbWVRdWV1ZSk7XG4gICAgICAgICAgICB0aGlzLm91dHB1dEZyYW1lUXVldWUgPSBuZXcgRnJhbWVTZXQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgc2VuZEZyYW1lU2V0KGZyYW1lU2V0OiBGcmFtZVNldCk6IHZvaWQge1xuICAgICAgICB0aGlzLnNlbmRQYWNrZXQoZnJhbWVTZXQpO1xuICAgICAgICB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLnNldChcbiAgICAgICAgICAgIGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyLFxuICAgICAgICAgICAgZnJhbWVTZXQuZnJhbWVzLmZpbHRlcigoZnJhbWUpID0+IGZyYW1lLmlzUmVsaWFibGUoKSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNlbmRQYWNrZXQocGFja2V0OiBQYWNrZXQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5saXN0ZW5lci5zZW5kUGFja2V0KHBhY2tldCwgdGhpcy5yaW5mbyk7XG4gICAgfVxuXG4gICAgcHVibGljIGNsb3NlKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBzdHJlYW0gPSBuZXcgQmluYXJ5U3RyZWFtKEJ1ZmZlci5mcm9tKCdcXHUwMDAwXFx1MDAwMFxcdTAwMDhcXHUwMDE1JywgJ2JpbmFyeScpKTtcbiAgICAgICAgdGhpcy5hZGRGcmFtZVRvUXVldWUobmV3IEZyYW1lKCkuZnJvbUJpbmFyeShzdHJlYW0pLCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpOyAvLyBDbGllbnQgZGlzY2NvbmVjdCBwYWNrZXQgMHgxNVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEtpY2sgYSBjbGllbnRcbiAgICAgKiBAcGFyYW0gcmVhc29uIC0gdGhlIHJlYXNvbiBtZXNzYWdlLCBvcHRpb25hbFxuICAgICAqL1xuICAgIHB1YmxpYyBkaXNjb25uZWN0KHJlYXNvbiA9ICdjbGllbnQgZGlzY29ubmVjdCcpOiB2b2lkIHtcbiAgICAgICAgLy8gVE9ETzogcmV3cml0ZSwgd29ya3MgYnV0IGNhbiBiZSBpbXByb3ZlZFxuICAgICAgICB0aGlzLmNsb3NlKCk7XG4gICAgICAgIC8vIFNlbmQgZGlzY29ubmVjdCBBQ0tcbiAgICAgICAgdGhpcy5zdGF0ZSA9IFNlc3Npb25TdGF0dXMuRElTQ09OTkVDVEVEO1xuICAgICAgICB0aGlzLnVwZGF0ZShEYXRlLm5vdygpKTtcbiAgICAgICAgdGhpcy5saXN0ZW5lci5yZW1vdmVTZXNzaW9uKHRoaXMsIHJlYXNvbik7XG4gICAgfVxuXG4gICAgcHVibGljIGdldFN0YXRlKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLnN0YXRlO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc0FjdGl2ZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuYWN0aXZlO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc0Rpc2Nvbm5lY3RlZCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGUgPT09IFNlc3Npb25TdGF0dXMuRElTQ09OTkVDVEVEO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRMaXN0ZW5lcigpOiBSYWtOZXRMaXN0ZW5lciB7XG4gICAgICAgIHJldHVybiB0aGlzLmxpc3RlbmVyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIG1heG1pdW0gdHJhbnNmZXIgdW5pdFxuICAgICAqIGZvciB0aGlzIGNvbm5lY3Rpb24uXG4gICAgICogQHJldHVybnMge251bWJlcn0gdGhlIFVEUCBhZGp1c3RlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0TVRVKCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLm10dVNpemUgLSBVRFBfSEVBREVSX1NJWkU7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEFkZHJlc3MoKTogSW5ldEFkZHJlc3Mge1xuICAgICAgICByZXR1cm4gbmV3IEluZXRBZGRyZXNzKHRoaXMucmluZm8uYWRkcmVzcywgdGhpcy5yaW5mby5wb3J0LCA0KTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUJBLElBQVksaUJBQUwseUJBQUEsZ0JBQUE7Q0FDSCxlQUFBLGVBQUEsWUFBQSxLQUFBO0NBQ0EsZUFBQSxlQUFBLGVBQUEsS0FBQTs7QUFDSixFQUFBLENBQUEsQ0FBQTtBQUVBLElBQVksZ0JBQUwseUJBQUEsZUFBQTtDQUNILGNBQUEsY0FBQSxnQkFBQSxLQUFBO0NBQ0EsY0FBQSxjQUFBLGVBQUEsS0FBQTtDQUNBLGNBQUEsY0FBQSxtQkFBQSxLQUFBO0NBQ0EsY0FBQSxjQUFBLGtCQUFBLEtBQUE7O0FBQ0osRUFBQSxDQUFBLENBQUE7QUFFQSxJQUFxQixVQUFyQixNQUE2QjtDQStCSjtDQUNBO0NBQ0Q7Q0FDQTtDQWpDcEIsUUFBUTtDQUVSLG1CQUEyQixJQUFJLFNBQVM7Q0FDeEMsdUJBQStCO0NBQy9CLHNCQUE4QjtDQUM5QixvQ0FBMkQsSUFBSSxJQUFJO0NBRW5FO0NBQ0E7Q0FFQSx5Q0FBOEMsSUFBSSxJQUFJO0NBQ3RELHFDQUEwQyxJQUFJLElBQUk7Q0FHbEQsaUNBQW1FLElBQUksSUFBSTtDQUMzRSxzQkFBOEI7Q0FFOUIsMEJBQWtDO0NBQ2xDO0NBQ0E7Q0FDQSxxQ0FBOEQsSUFBSSxJQUFJO0NBR3RFLGFBQTZCLEtBQUssSUFBSTtDQUN0QyxTQUFpQjtDQUdqQixhQUE4QixJQUFJLFdBQVc7Q0FFN0MsWUFDSSxVQUNBLFNBQ0EsT0FDQSxNQUNGO0VBSm1CLEtBQUEsV0FBQTtFQUNBLEtBQUEsVUFB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