@jsprismarine/raknet
Version:
Basic RakNet implementation written in TypeScript
344 lines (343 loc) • 52.2 kB
JavaScript
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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