@jsprismarine/raknet
Version:
Basic RakNet implementation written in TypeScript
354 lines (353 loc) • 53.9 kB
JavaScript
"use strict";
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
const require_runtime = require("./_virtual/_rolldown/runtime.cjs.cjs");
const require_utils_InetAddress = require("./utils/InetAddress.cjs.cjs");
const require_protocol_MessageIdentifiers = require("./protocol/MessageIdentifiers.cjs.cjs");
const require_protocol_ACK = require("./protocol/ACK.cjs.cjs");
const require_protocol_BitFlags = require("./protocol/BitFlags.cjs.cjs");
const require_protocol_connection_ConnectedPing = require("./protocol/connection/ConnectedPing.cjs.cjs");
const require_protocol_connection_ConnectedPong = require("./protocol/connection/ConnectedPong.cjs.cjs");
const require_protocol_login_ConnectionRequest = require("./protocol/login/ConnectionRequest.cjs.cjs");
const require_protocol_login_ConnectionRequestAccepted = require("./protocol/login/ConnectionRequestAccepted.cjs.cjs");
const require_protocol_FrameReliability = require("./protocol/FrameReliability.cjs.cjs");
const require_protocol_Frame = require("./protocol/Frame.cjs.cjs");
const require_protocol_FrameSet = require("./protocol/FrameSet.cjs.cjs");
const require_protocol_NACK = require("./protocol/NACK.cjs.cjs");
require("./Constants.cjs.cjs");
const require_protocol_PacketPool = require("./protocol/PacketPool.cjs.cjs");
let _jsprismarine_jsbinaryutils = require("@jsprismarine/jsbinaryutils");
_jsprismarine_jsbinaryutils = require_runtime.__toESM(_jsprismarine_jsbinaryutils, 1);
let node_assert = require("node:assert");
node_assert = require_runtime.__toESM(node_assert, 1);
//#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 require_protocol_FrameSet.default();
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 require_protocol_PacketPool.default();
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 = {
[require_protocol_MessageIdentifiers.MessageIdentifiers.ACKNOWLEDGE_PACKET]: (buffer) => {
const ack = this.packetPool.getAckInstance();
ack.buffer = buffer;
ack.decode();
this.handleACK(ack);
},
[require_protocol_MessageIdentifiers.MessageIdentifiers.NACKNOWLEDGE_PACKET]: (buffer) => {
const nack = this.packetPool.getNackInstance();
nack.buffer = buffer;
nack.decode();
this.handleNACK(nack);
},
[require_protocol_BitFlags.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 require_protocol_ACK.default();
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 require_protocol_NACK.default();
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) {
(0, node_assert.default)(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 === require_protocol_MessageIdentifiers.MessageIdentifiers.CONNECTION_REQUEST) this.handleConnectionRequest(packet.content).then((encapsulated) => this.sendFrame(encapsulated, 1), () => {});
else if (id === require_protocol_MessageIdentifiers.MessageIdentifiers.NEW_INCOMING_CONNECTION) {
this.state = 1;
this.listener.emit("openConnection", this);
}
} else if (id === require_protocol_MessageIdentifiers.MessageIdentifiers.DISCONNECTION_NOTIFICATION) this.disconnect();
else if (id === require_protocol_MessageIdentifiers.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 require_protocol_login_ConnectionRequest.default(buffer);
dataPacket.decode();
const pk = new require_protocol_login_ConnectionRequestAccepted.default();
pk.clientAddress = this.getAddress();
pk.requestTimestamp = dataPacket.requestTimestamp;
pk.acceptedTimestamp = BigInt(Date.now());
pk.encode();
const sendPacket = new require_protocol_Frame.default();
sendPacket.reliability = require_protocol_FrameReliability.FrameReliability.UNRELIABLE;
sendPacket.orderChannel = 0;
sendPacket.content = pk.getBuffer();
return sendPacket;
}
async handleConnectedPing(buffer) {
const dataPacket = new require_protocol_connection_ConnectedPing.default(buffer);
dataPacket.decode();
const pk = new require_protocol_connection_ConnectedPong.default();
pk.clientTimestamp = dataPacket.clientTimestamp;
pk.serverTimestamp = BigInt(Date.now());
pk.encode();
const sendPacket = new require_protocol_Frame.default();
sendPacket.reliability = require_protocol_FrameReliability.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 _jsprismarine_jsbinaryutils.default();
for (let i = 0; i < entry.size; i++) {
const splitPacket = entry.get(i);
if (!splitPacket) return;
stream.write(splitPacket.content);
}
const assembledFrame = new require_protocol_Frame.default();
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 require_protocol_FrameSet.default();
}
}
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 _jsprismarine_jsbinaryutils.default(Buffer.from("\0\0\b", "binary"));
this.addFrameToQueue(new require_protocol_Frame.default().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 require_utils_InetAddress.default(this.rinfo.address, this.rinfo.port, 4);
}
};
//#endregion
exports.RakNetPriority = RakNetPriority;
exports.SessionStatus = SessionStatus;
exports.default = Session;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2Vzc2lvbi5janMuY2pzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXNzaW9uLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBCaW5hcnlTdHJlYW0gZnJvbSAnQGpzcHJpc21hcmluZS9qc2JpbmFyeXV0aWxzJztcbmltcG9ydCBhc3NlcnQgZnJvbSAnbm9kZTphc3NlcnQnO1xuaW1wb3J0IHsgTUFYX0NIQU5ORUxTLCBVRFBfSEVBREVSX1NJWkUgfSBmcm9tICcuL0NvbnN0YW50cyc7XG5pbXBvcnQgQUNLIGZyb20gJy4vcHJvdG9jb2wvQUNLJztcbmltcG9ydCBCaXRGbGFncyBmcm9tICcuL3Byb3RvY29sL0JpdEZsYWdzJztcbmltcG9ydCBGcmFtZSwgeyBNQVhfRlJBTUVfQllURV9MRU5HVEggfSBmcm9tICcuL3Byb3RvY29sL0ZyYW1lJztcbmltcG9ydCBGcmFtZVJlbGlhYmlsaXR5IGZyb20gJy4vcHJvdG9jb2wvRnJhbWVSZWxpYWJpbGl0eSc7XG5pbXBvcnQgRnJhbWVTZXQsIHsgREFUQUdSQU1fSEVBREVSX0JZVEVfTEVOR1RIIH0gZnJvbSAnLi9wcm90b2NvbC9GcmFtZVNldCc7XG5pbXBvcnQgeyBNZXNzYWdlSWRlbnRpZmllcnMgfSBmcm9tICcuL3Byb3RvY29sL01lc3NhZ2VJZGVudGlmaWVycyc7XG5pbXBvcnQgTkFDSyBmcm9tICcuL3Byb3RvY29sL05BQ0snO1xuaW1wb3J0IFBhY2tldFBvb2wgZnJvbSAnLi9wcm90b2NvbC9QYWNrZXRQb29sJztcbmltcG9ydCBDb25uZWN0ZWRQaW5nIGZyb20gJy4vcHJvdG9jb2wvY29ubmVjdGlvbi9Db25uZWN0ZWRQaW5nJztcbmltcG9ydCBDb25uZWN0ZWRQb25nIGZyb20gJy4vcHJvdG9jb2wvY29ubmVjdGlvbi9Db25uZWN0ZWRQb25nJztcbmltcG9ydCBDb25uZWN0aW9uUmVxdWVzdCBmcm9tICcuL3Byb3RvY29sL2xvZ2luL0Nvbm5lY3Rpb25SZXF1ZXN0JztcbmltcG9ydCBDb25uZWN0aW9uUmVxdWVzdEFjY2VwdGVkIGZyb20gJy4vcHJvdG9jb2wvbG9naW4vQ29ubmVjdGlvblJlcXVlc3RBY2NlcHRlZCc7XG5pbXBvcnQgSW5ldEFkZHJlc3MgZnJvbSAnLi91dGlscy9JbmV0QWRkcmVzcyc7XG5cbmltcG9ydCB0eXBlIHsgUmVtb3RlSW5mbyB9IGZyb20gJ25vZGU6ZGdyYW0nO1xuaW1wb3J0IHR5cGUgUmFrTmV0TGlzdGVuZXIgZnJvbSAnLi9TZXJ2ZXJTb2NrZXQnO1xuaW1wb3J0IHR5cGUgUGFja2V0IGZyb20gJy4vcHJvdG9jb2wvUGFja2V0JztcblxuZXhwb3J0IGVudW0gUmFrTmV0UHJpb3JpdHkge1xuICAgIE5PUk1BTCxcbiAgICBJTU1FRElBVEVcbn1cblxuZXhwb3J0IGVudW0gU2Vzc2lvblN0YXR1cyB7XG4gICAgQ09OTkVDVElORyxcbiAgICBDT05ORUNURUQsXG4gICAgRElTQ09OTkVDVElORyxcbiAgICBESVNDT05ORUNURURcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2Vzc2lvbiB7XG4gICAgcHJpdmF0ZSBzdGF0ZSA9IFNlc3Npb25TdGF0dXMuQ09OTkVDVElORztcblxuICAgIHByaXZhdGUgb3V0cHV0RnJhbWVRdWV1ZSA9IG5ldyBGcmFtZVNldCgpO1xuICAgIHByaXZhdGUgb3V0cHV0U2VxdWVuY2VOdW1iZXIgPSAwOyAvLyBUT0RPOiBmaW5kIGEgYmV0dGVyIG5hbWVcbiAgICBwcml2YXRlIG91dHB1dFJlbGlhYmxlSW5kZXggPSAwO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgb3V0cHV0QmFja3VwUXVldWU6IE1hcDxudW1iZXIsIEZyYW1lW10+ID0gbmV3IE1hcCgpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBvdXRwdXRPcmRlckluZGV4OiBudW1iZXJbXTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IG91dHB1dFNlcXVlbmNlSW5kZXg6IG51bWJlcltdO1xuXG4gICAgcHJpdmF0ZSByZWNlaXZlZEZyYW1lU2VxdWVuY2VzOiBTZXQ8bnVtYmVyPiA9IG5ldyBTZXQoKTtcbiAgICBwcml2YXRlIGxvc3RGcmFtZVNlcXVlbmNlczogU2V0PG51bWJlcj4gPSBuZXcgU2V0KCk7XG5cbiAgICAvLyBNYXAgaG9sZGluZyBmcmFnbWVudHMgb2YgZnJhZ21lbnRlZCBwYWNrZXRzXG4gICAgcHJpdmF0ZSByZWFkb25seSBmcmFnbWVudHNRdWV1ZTogTWFwPG51bWJlciwgTWFwPG51bWJlciwgRnJhbWU+PiA9IG5ldyBNYXAoKTtcbiAgICBwcml2YXRlIG91dHB1dEZyYWdtZW50SW5kZXggPSAwO1xuXG4gICAgcHJpdmF0ZSBsYXN0SW5wdXRTZXF1ZW5jZU51bWJlciA9IC0xO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleDogbnVtYmVyW107XG4gICAgcHJpdmF0ZSByZWFkb25seSBpbnB1dE9yZGVySW5kZXg6IG51bWJlcltdO1xuICAgIHByaXZhdGUgaW5wdXRPcmRlcmluZ1F1ZXVlOiBNYXA8bnVtYmVyLCBNYXA8bnVtYmVyLCBGcmFtZT4+ID0gbmV3IE1hcCgpO1xuXG4gICAgLy8gTGFzdCB0aW1lc3RhbXAgb2YgcGFja2V0IHJlY2VpdmVkLCBoZWxwZnVsIGZvciB0aW1lb3V0XG4gICAgcHJpdmF0ZSBsYXN0VXBkYXRlOiBudW1iZXIgPSBEYXRlLm5vdygpO1xuICAgIHByaXZhdGUgYWN0aXZlID0gdHJ1ZTtcblxuICAgIC8vIFBhY2tldCBwb29sIGlzIHRoZSBiZXN0IG9wdGlvbiB0byByZWR1Y2UgYWxsb2NhdGlvbnNcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBhY2tldFBvb2wgPSBuZXcgUGFja2V0UG9vbCgpO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGxpc3RlbmVyOiBSYWtOZXRMaXN0ZW5lcixcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBtdHVTaXplOiBudW1iZXIsXG4gICAgICAgIHB1YmxpYyByZWFkb25seSByaW5mbzogUmVtb3RlSW5mbyxcbiAgICAgICAgcHVibGljIHJlYWRvbmx5IGd1aWQ6IGJpZ2ludFxuICAgICkge1xuICAgICAgICB0aGlzLmxhc3RVcGRhdGUgPSBEYXRlLm5vdygpO1xuXG4gICAgICAgIHRoaXMub3V0cHV0T3JkZXJJbmRleCA9IG5ldyBBcnJheShNQVhfQ0hBTk5FTFMpLmZpbGwoMCk7XG4gICAgICAgIHRoaXMub3V0cHV0U2VxdWVuY2VJbmRleCA9IG5ldyBBcnJheShNQVhfQ0hBTk5FTFMpLmZpbGwoMCk7XG5cbiAgICAgICAgdGhpcy5pbnB1dE9yZGVySW5kZXggPSBuZXcgQXJyYXkoTUFYX0NIQU5ORUxTKS5maWxsKDApO1xuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IE1BWF9DSEFOTkVMUzsgaSsrKSB7XG4gICAgICAgICAgICB0aGlzLmlucHV0T3JkZXJpbmdRdWV1ZS5zZXQoaSwgbmV3IE1hcCgpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleCA9IG5ldyBBcnJheShNQVhfQ0hBTk5FTFMpLmZpbGwoMCk7XG4gICAgfVxuXG4gICAgLy8gTG9va3VwIHRhYmxlIGZvciBwYWNrZXQgaGFuZGxlcnMsIGFsd2F5cyBPKDEpIGluIGF2ZXJhZ2UgYW5kIHdvcnN0IGNhc2VcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBhY2tldEhhbmRsZXJzOiBSZWNvcmQ8bnVtYmVyLCAoYnVmZmVyOiBCdWZmZXIpID0+IHZvaWQ+ID0ge1xuICAgICAgICAvLyAweDQwIHwgMHhjMCA9IE1lc3NhZ2VIZWFkZXJzLkFDS05PV0xFREdFX1BBQ0tFVFxuICAgICAgICBbTWVzc2FnZUlkZW50aWZpZXJzLkFDS05PV0xFREdFX1BBQ0tFVF06IChidWZmZXI6IEJ1ZmZlcikgPT4ge1xuICAgICAgICAgICAgY29uc3QgYWNrID0gdGhpcy5wYWNrZXRQb29sLmdldEFja0luc3RhbmNlKCk7XG4gICAgICAgICAgICAoYWNrIGFzIGFueSkuYnVmZmVyID0gYnVmZmVyO1xuICAgICAgICAgICAgYWNrLmRlY29kZSgpO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVBQ0soYWNrKTtcbiAgICAgICAgfSxcbiAgICAgICAgW01lc3NhZ2VJZGVudGlmaWVycy5OQUNLTk9XTEVER0VfUEFDS0VUXTogKGJ1ZmZlcjogQnVmZmVyKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBuYWNrID0gdGhpcy5wYWNrZXRQb29sLmdldE5hY2tJbnN0YW5jZSgpO1xuICAgICAgICAgICAgKG5hY2sgYXMgYW55KS5idWZmZXIgPSBidWZmZXI7XG4gICAgICAgICAgICBuYWNrLmRlY29kZSgpO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVOQUNLKG5hY2spO1xuICAgICAgICB9LFxuICAgICAgICBbQml0RmxhZ3MuVkFMSURdOiAoYnVmZmVyOiBCdWZmZXIpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGZyYW1lU2V0ID0gdGhpcy5wYWNrZXRQb29sLmdldEZyYW1lU2V0SW5zdGFuY2UoKTtcbiAgICAgICAgICAgIChmcmFtZVNldCBhcyBhbnkpLmJ1ZmZlciA9IGJ1ZmZlcjtcbiAgICAgICAgICAgIGZyYW1lU2V0LmRlY29kZSgpO1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVGcmFtZVNldChmcmFtZVNldCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHVibGljIHVwZGF0ZSh0aW1lc3RhbXA6IG51bWJlcik6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuaXNBY3RpdmUoKSAmJiAhdGhpcy5pc0Rpc2Nvbm5lY3RlZCgpICYmIHRoaXMubGFzdFVwZGF0ZSArIDEwXzAwMCA8IHRpbWVzdGFtcCkge1xuICAgICAgICAgICAgdGhpcy5kaXNjb25uZWN0KCd0aW1lb3V0Jyk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmFjdGl2ZSA9IGZhbHNlO1xuXG4gICAgICAgIC8vIFRPRE86IGEgcXVldWUganVzdCBmb3IgQUNLcyBzZXF1ZW5jZXMgdG8gYXZvaWQgZHVwbGljYXRlZHNcbiAgICAgICAgaWYgKHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5zaXplID4gMCkge1xuICAgICAgICAgICAgY29uc3QgYWNrID0gbmV3IEFDSygpO1xuICAgICAgICAgICAgYWNrLnNlcXVlbmNlTnVtYmVycyA9IEFycmF5LmZyb20odGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzKS5tYXAoKHNlcSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5kZWxldGUoc2VxKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2VxO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLnNlbmRQYWNrZXQoYWNrKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcy5zaXplID4gMCkge1xuICAgICAgICAgICAgY29uc3QgcGsgPSBuZXcgTkFDSygpO1xuICAgICAgICAgICAgcGsuc2VxdWVuY2VOdW1iZXJzID0gQXJyYXkuZnJvbSh0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcykubWFwKChzZXEpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvc3RGcmFtZVNlcXVlbmNlcy5kZWxldGUoc2VxKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gc2VxO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLnNlbmRQYWNrZXQocGspO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5zZW5kRnJhbWVRdWV1ZSgpO1xuICAgIH1cblxuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvMWExNjk4OTVhOTAwYzlmYzQ4NDFjNTU2ZTE2NTE0MTgyYjc1ZmFmOC9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDYzNVxuICAgIHB1YmxpYyBoYW5kbGUoYnVmZmVyOiBCdWZmZXIpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5hY3RpdmUgPSB0cnVlO1xuICAgICAgICB0aGlzLmxhc3RVcGRhdGUgPSBEYXRlLm5vdygpO1xuXG4gICAgICAgIC8vIE1hc2sgdGhlIGxvd2VyIDQgYml0cyBvZiB0aGUgaGVhZGVyXG4gICAgICAgIC8vIHRvIGdldCB0aGUgcmFuZ2Ugb2YgaGVhZGVyIHZhbHVlc1xuICAgICAgICBjb25zdCBoYW5kbGVyID0gdGhpcy5wYWNrZXRIYW5kbGVyc1tidWZmZXJbMF0hICYgMHhmMF07XG4gICAgICAgIGlmIChoYW5kbGVyKSB7XG4gICAgICAgICAgICBoYW5kbGVyKGJ1ZmZlcik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmdldExvZ2dlcigpLnZlcmJvc2UoYFJlY2VpdmVkIGFuIHVua25vd24gcGFja2V0IHR5cGU9JHtidWZmZXJbMF19YCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZUZyYW1lU2V0KGZyYW1lU2V0OiBGcmFtZVNldCk6IHZvaWQge1xuICAgICAgICAvLyBUT0RPOiBhZGRpdGlvbmFsIHNhbml0eSBjaGVja3NcbiAgICAgICAgaWYgKHRoaXMucmVjZWl2ZWRGcmFtZVNlcXVlbmNlcy5oYXMoZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIpKSB7XG4gICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmdldExvZ2dlcigpLmRlYnVnKGBEaXNjYXJkZWQgZHVwbGljYXRlZCBwYWNrZXQgZnJvbSBjbGllbnQ9JHt0aGlzLmdldEFkZHJlc3MoKX1gKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGlmIGl0IHdhcyBtaXNzaW5nLCByZW1vdmUgZnJvbSB0aGUgcXVldWUgYmVjYXVzZSB3ZSByZWNlaXZlZCBpdCBub3dcbiAgICAgICAgdGhpcy5sb3N0RnJhbWVTZXF1ZW5jZXMuZGVsZXRlKGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyKTtcblxuICAgICAgICAvLyBGb3Igbm93IGlzIGdvb2QgbGlrZSB0aGF0XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIGZyYW1lU2V0LnNlcXVlbmNlTnVtYmVyIDwgdGhpcy5sYXN0SW5wdXRTZXF1ZW5jZU51bWJlciB8fFxuICAgICAgICAgICAgZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIgPT09IHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXJcbiAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgdGhlIGZyYW1lIHRvIHRoZSBBQ0sgcXVldWVcbiAgICAgICAgdGhpcy5yZWNlaXZlZEZyYW1lU2VxdWVuY2VzLmFkZChmcmFtZVNldC5zZXF1ZW5jZU51bWJlcik7XG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhlcmUgYXJlIG1pc3NpbmcgcGFja2V0cyBiZXR3ZWVuIHRoZSByZWNlaXZlZCBwYWNrZXQgYW5kIHRoZSBsYXN0IHJlY2VpdmVkIG9uZVxuICAgICAgICBjb25zdCBkaWZmID0gZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIgLSB0aGlzLmxhc3RJbnB1dFNlcXVlbmNlTnVtYmVyO1xuXG4gICAgICAgIC8vIENoZWNrIGlmIHRoZSBzZXF1ZW5jZSBoYXMgYSBob2xlIGR1ZSB0byBhIGxvc3QgcGFja2V0XG4gICAgICAgIGlmIChkaWZmICE9PSAxKSB7XG4gICAgICAgICAgICAvLyBBcyBpIHNhaWQgYmVmb3JlLCB0aGVyZSB3ZSBzZWFyY2ggZm9yIG1pc3NpbmcgcGFja2V0cyBpbiB0aGUgbGlzdCBvZiB0aGUgcmVjaWV2ZWQgb25lc1xuICAgICAgICAgICAgZm9yIChsZXQgaSA9IHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXIgKyAxOyBpIDwgZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXI7IGkrKykge1xuICAgICAgICAgICAgICAgIC8vIEFkZGluZyB0aGUgcGFja2V0IHNlcXVlbmNlIG51bWJlciB0byB0aGUgTkFDSyBxdWV1ZSBhbmQgdGhlbiBzZW5kaW5nIGEgTkFDS1xuICAgICAgICAgICAgICAgIC8vIHdpbGwgbWFrZSB0aGUgQ2xpZW50IHNlbmRpbmcgYWdhaW4gdGhlIGxvc3QgcGFja2V0XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLnJlY2VpdmVkRnJhbWVTZXF1ZW5jZXMuaGFzKGkpKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMubG9zdEZyYW1lU2VxdWVuY2VzLmFkZChpKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiB3ZSByZWNlaXZlZCBhIGxvc3QgcGFja2V0IHdlIHNlbnQgaW4gTkFDSyBvciBhIG5vcm1hbCBzZXF1ZW5jZWQgb25lXG4gICAgICAgIHRoaXMubGFzdElucHV0U2VxdWVuY2VOdW1iZXIgPSBmcmFtZVNldC5zZXF1ZW5jZU51bWJlcjtcblxuICAgICAgICAvLyBIYW5kbGUgZW5jYXBzdWxhdGVkXG4gICAgICAgIGZvciAoY29uc3QgZnJhbWUgb2YgZnJhbWVTZXQuZnJhbWVzKSB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUZyYW1lKGZyYW1lKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaGFuZGxlQUNLKGFjazogQUNLKTogdm9pZCB7XG4gICAgICAgIC8vIFRPRE86IHBpbmcgY2FsY3VsYXRpb25cblxuICAgICAgICBmb3IgKGNvbnN0IHNlcSBvZiBhY2suc2VxdWVuY2VOdW1iZXJzKSB7XG4gICAgICAgICAgICB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLmRlbGV0ZShzZXEpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVOQUNLKG5hY2s6IE5BQ0spOiB2b2lkIHtcbiAgICAgICAgZm9yIChjb25zdCBzZXEgb2YgbmFjay5zZXF1ZW5jZU51bWJlcnMpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLmhhcyhzZXEpKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgbG9zdEZyYW1lcyA9IHRoaXMub3V0cHV0QmFja3VwUXVldWUuZ2V0KHNlcSkhO1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgbG9zdEZyYW1lIG9mIGxvc3RGcmFtZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWUobG9zdEZyYW1lLCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLm91dHB1dEJhY2t1cFF1ZXVlLmRlbGV0ZShzZXEpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBoYW5kbGVGcmFtZShmcmFtZTogRnJhbWUpOiB2b2lkIHtcbiAgICAgICAgaWYgKGZyYW1lLmlzRnJhZ21lbnRlZCgpKSB7XG4gICAgICAgICAgICB0aGlzLmhhbmRsZUZyYWdtZW50KGZyYW1lKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEhhbmRsZSBwYWNrZXRzIHdpdGhvdXQgb3JkZXJpbmdcbiAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxMjQ4LUwxMjUxXG4gICAgICAgIGlmICghZnJhbWUuaXNPcmRlcmVkKCkpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGZyYW1lKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IG9yZGVyQ2hhbm5lbCA9IGZyYW1lLm9yZGVyQ2hhbm5lbCE7XG4gICAgICAgIGNvbnN0IG9yZGVySW5kZXggPSBmcmFtZS5vcmRlckluZGV4ITtcbiAgICAgICAgY29uc3QgZXhwZWN0ZWRPcmRlckluZGV4ID0gdGhpcy5pbnB1dE9yZGVySW5kZXhbb3JkZXJDaGFubmVsXSE7XG5cbiAgICAgICAgLy8gRmlyc3Qgb2YgYWxsIGNoZWNrIHRoZSBvcmRlcmluZ0luZGV4XG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTI4M1xuICAgICAgICBpZiAob3JkZXJJbmRleCA9PT0gZXhwZWN0ZWRPcmRlckluZGV4KSB7XG4gICAgICAgICAgICBpZiAoZnJhbWUuaXNTZXF1ZW5jZWQoKSkge1xuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTI4Ni1MMTI5MFxuICAgICAgICAgICAgICAgIGNvbnN0IHNlcXVlbmNlSW5kZXggPSBmcmFtZS5zZXF1ZW5jZUluZGV4ITtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5pc09sZGVyT3JkZXJlZEZyYW1lKHNlcXVlbmNlSW5kZXgsIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdISkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTMxN1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdID0gc2VxdWVuY2VJbmRleCArIDE7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVQYWNrZXQoZnJhbWUpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyBIYW5kbGUgb3JkZXJlZCwgbm9uIHNlcXVlbmNlZFxuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTM3Mi1MMTM3M1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0gPSBvcmRlckluZGV4ICsgMTtcbiAgICAgICAgICAgICAgICB0aGlzLmlucHV0SGlnaGVzdFNlcXVlbmNlSW5kZXhbb3JkZXJDaGFubmVsXSA9IDA7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVQYWNrZXQoZnJhbWUpO1xuXG4gICAgICAgICAgICAgICAgdGhpcy5wcm9jZXNzT3JkZXJpbmdIZWFwKG9yZGVyQ2hhbm5lbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoIXRoaXMuaXNPbGRlck9yZGVyZWRGcmFtZShvcmRlckluZGV4LCBleHBlY3RlZE9yZGVySW5kZXgpKSB7XG4gICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDE0MjktTDE0NDZcbiAgICAgICAgICAgIGNvbnN0IHF1ZXVlID0gdGhpcy5pbnB1dE9yZGVyaW5nUXVldWUuZ2V0KG9yZGVyQ2hhbm5lbCkhO1xuICAgICAgICAgICAgcXVldWUuc2V0KG9yZGVySW5kZXgsIGZyYW1lKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIE5PLU9QOiBvdXQgb2Ygb3JkZXJcbiAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxNDY0LUwxNDY2XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBwcm9jZXNzT3JkZXJpbmdIZWFwKG9yZGVyQ2hhbm5lbDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvbWFzdGVyL1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTM3Ni1MMTQyM1xuICAgICAgICBjb25zdCBxdWV1ZSA9IHRoaXMuaW5wdXRPcmRlcmluZ1F1ZXVlLmdldChvcmRlckNoYW5uZWwpITtcbiAgICAgICAgbGV0IGV4cGVjdGVkSW5kZXggPSB0aGlzLmlucHV0T3JkZXJJbmRleFtvcmRlckNoYW5uZWxdITtcblxuICAgICAgICB3aGlsZSAocXVldWUuaGFzKHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0hKSkge1xuICAgICAgICAgICAgY29uc3QgYnVmZmVyZWRGcmFtZSA9IHF1ZXVlLmdldChleHBlY3RlZEluZGV4KSE7XG4gICAgICAgICAgICBxdWV1ZS5kZWxldGUoZXhwZWN0ZWRJbmRleCk7XG5cbiAgICAgICAgICAgIGlmIChidWZmZXJlZEZyYW1lLmlzU2VxdWVuY2VkKCkpIHtcbiAgICAgICAgICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vZmFjZWJvb2thcmNoaXZlL1Jha05ldC9ibG9iL21hc3Rlci9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDE0MTUtTDE0MjJcbiAgICAgICAgICAgICAgICBjb25zdCBzZXFJbmRleCA9IGJ1ZmZlcmVkRnJhbWUuc2VxdWVuY2VJbmRleCE7XG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLmlzT2xkZXJPcmRlcmVkRnJhbWUoc2VxSW5kZXgsIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdISkpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5pbnB1dEhpZ2hlc3RTZXF1ZW5jZUluZGV4W29yZGVyQ2hhbm5lbF0gPSBzZXFJbmRleCArIDE7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGFuZGxlUGFja2V0KGJ1ZmZlcmVkRnJhbWUpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wxNDE1LUwxNDE3XG4gICAgICAgICAgICAgICAgZXhwZWN0ZWRJbmRleCsrO1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRPcmRlckluZGV4W29yZGVyQ2hhbm5lbF0gPSBleHBlY3RlZEluZGV4O1xuICAgICAgICAgICAgICAgIHRoaXMuaW5wdXRIaWdoZXN0U2VxdWVuY2VJbmRleFtvcmRlckNoYW5uZWxdID0gMDtcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZVBhY2tldChidWZmZXJlZEZyYW1lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERldGVybWluZXMgd2hldGhlciBhIHBhY2tldCBpcyBvbGRlciB0aGFuIGV4cGVjdGVkLCBoYW5kbGluZyB3cmFwYXJvdW5kLlxuICAgICAqIEBzZWUgaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi9tYXN0ZXIvU291cmNlL1JlbGlhYmlsaXR5TGF5ZXIuY3BwI0wyOTAxLUwyOTIwXG4gICAgICovXG4gICAgcHVibGljIGlzT2xkZXJPcmRlcmVkRnJhbWUobmV3SW5kZXg6IG51bWJlciwgY3VycmVudEluZGV4OiBudW1iZXIsIG1heFZhbHVlOiBudW1iZXIgPSAweGZmZmZmZik6IGJvb2xlYW4ge1xuICAgICAgICBjb25zdCBoYWxmUmFuZ2UgPSBNYXRoLmZsb29yKG1heFZhbHVlIC8gMik7XG5cbiAgICAgICAgaWYgKGN1cnJlbnRJbmRleCA+IGhhbGZSYW5nZSkge1xuICAgICAgICAgICAgcmV0dXJuIG5ld0luZGV4ID49IGN1cnJlbnRJbmRleCAtIGhhbGZSYW5nZSAmJiBuZXdJbmRleCA8IGN1cnJlbnRJbmRleDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBuZXdJbmRleCA+PSBjdXJyZW50SW5kZXggLSBoYWxmUmFuZ2UgKyBtYXhWYWx1ZSArIDEgfHwgbmV3SW5kZXggPCBjdXJyZW50SW5kZXg7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgc2VuZEZyYW1lKGZyYW1lOiBGcmFtZSwgZmxhZ3MgPSBSYWtOZXRQcmlvcml0eS5OT1JNQUwpOiB2b2lkIHtcbiAgICAgICAgYXNzZXJ0KHR5cGVvZiBmcmFtZS5vcmRlckNoYW5uZWwgPT09ICdudW1iZXInLCAnRnJhbWUgT3JkZXJDaGFubmVsIGNhbm5vdCBiZSBudWxsJyk7XG5cbiAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rYXJjaGl2ZS9SYWtOZXQvYmxvYi8xYTE2OTg5NWE5MDBjOWZjNDg0MWM1NTZlMTY1MTQxODJiNzVmYWY4L1NvdXJjZS9SZWxpYWJpbGl0eUxheWVyLmNwcCNMMTYyNVxuICAgICAgICBpZiAoZnJhbWUuaXNTZXF1ZW5jZWQoKSkge1xuICAgICAgICAgICAgLy8gU2VxdWVuY2VkIHBhY2tldHMgZG9uJ3QgaW5jcmVhc2UgdGhlIG9yZGVyZWQgY2hhbm5lbCBpbmRleFxuICAgICAgICAgICAgZnJhbWUub3JkZXJJbmRleCA9IHRoaXMub3V0cHV0T3JkZXJJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdITtcbiAgICAgICAgICAgIGZyYW1lLnNlcXVlbmNlSW5kZXggPSB0aGlzLm91dHB1dFNlcXVlbmNlSW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSErKztcbiAgICAgICAgfSBlbHNlIGlmIChmcmFtZS5pc09yZGVyZWRFeGNsdXNpdmUoKSkge1xuICAgICAgICAgICAgLy8gaW1wbGllcyBzZXF1ZW5jZWQsIGJ1dCB3ZSBoYXZlIHRvIGRpc3RpbmN0IHRoZW1cbiAgICAgICAgICAgIGZyYW1lLm9yZGVySW5kZXggPSB0aGlzLm91dHB1dE9yZGVySW5kZXhbZnJhbWUub3JkZXJDaGFubmVsXSErKztcbiAgICAgICAgICAgIHRoaXMub3V0cHV0U2VxdWVuY2VJbmRleFtmcmFtZS5vcmRlckNoYW5uZWxdID0gMDtcbiAgICAgICAgfVxuXG4gICAgICAgIGZyYW1lLnJlbGlhYmxlSW5kZXggPSB0aGlzLm91dHB1dFJlbGlhYmxlSW5kZXgrKztcblxuICAgICAgICAvLyBTcGxpdCBwYWNrZXQgaWYgYmlnZ2VyIHRoYW4gTVRVIHNpemVcbiAgICAgICAgY29uc3QgbWF4U2l6ZSA9IHRoaXMuZ2V0TVRVKCkgLSBEQVRBR1JBTV9IRUFERVJfQllURV9MRU5HVEggLSBNQVhfRlJBTUVfQllURV9MRU5HVEg7XG4gICAgICAgIGlmIChmcmFtZS5jb250ZW50LmJ5dGVMZW5ndGggPiBtYXhTaXplKSB7XG4gICAgICAgICAgICAvLyBJZiB3ZSB1c2UgdGhlIGZyYW1lIGFzIHJlZmVyZW5jZSwgd2UgaGF2ZSB0byBjb3B5IHNvbWV3aGVyZVxuICAgICAgICAgICAgLy8gdGhlIG9yaWdpbmFsIGJ1ZmZlci4gVGhlbiB3ZSB3aWxsIHBvaW50IHRvIHRoaXMgYnVmZmVyIGNvbnRlbnRcbiAgICAgICAgICAgIGNvbnN0IGJ1ZmZlciA9IEJ1ZmZlci5mcm9tKGZyYW1lLmNvbnRlbnQpO1xuICAgICAgICAgICAgLy8gT3JpZ2luYWwgUmFrTmV0IHNheXM6IFRoaXMgaXMgdGhlIG1heGltdW0gbnVtYmVyIG9mIHNwbGl0IG1lc3NhZ2VzIHdlIGNhbiBzZW5kIHNpbXVsdGFuZW91c2x5IHBlciBjb25uZWN0aW9uLlxuICAgICAgICAgICAgY29uc3QgZnJhZ21lbnRJZCA9IHRoaXMub3V0cHV0RnJhZ21lbnRJbmRleCsrICUgNjU1MzY7XG4gICAgICAgICAgICBsZXQgZnJhZ21lbnRJbmRleCA9IDA7XG4gICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJ1ZmZlci5ieXRlTGVuZ3RoOyBpICs9IG1heFNpemUpIHtcbiAgICAgICAgICAgICAgICAvLyBMaWtlIHRoZSBvcmlnaW5hbCByYWtuZXQsIHdlIGxpa2UgdG8gdXNlIGEgcG9pbnRlciB0byB0aGUgb3JpZ2luYWxcbiAgICAgICAgICAgICAgICAvLyBmcmFtZSwgd2UgZG9uJ3QgcmVhbGx5IGNhcmUgYWJvdXQgc2lkZSBlZmZlY3RzIGluIHRoaXMgY2FzZS5cbiAgICAgICAgICAgICAgICAvLyBSYWtOZXQgd2lsbCBhbGxvY2F0ZSB0aGUgd2hvbGUgc3RyY3R1cmUgY29udGFpbmluZyBzcGxpdHMsXG4gICAgICAgICAgICAgICAgLy8gYnV0IGkgdGhpbmsgY2FjaGluZyBqdXN0IHRoZSBidWZmZXIgaXMgZW5vdWdoLlxuICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9va2FyY2hpdmUvUmFrTmV0L2Jsb2IvMWExNjk4OTVhOTAwYzlmYzQ4NDFjNTU2ZTE2NTE0MTgyYjc1ZmFmOC9Tb3VyY2UvUmVsaWFiaWxpdHlMYXllci5jcHAjTDI5NjNcblxuICAgICAgICAgICAgICAgIC8vIFNraXAgdGhlIGZpcnN0IGluZGV4IGFzIGl0J3MgYWxyZWFkeSBpbmNyZWFzZWQgYnkgaXRzZWxmLlxuICAgICAgICAgICAgICAgIGlmIChpICE9PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGZyYW1lLnJlbGlhYmxlSW5kZXggPSB0aGlzLm91dHB1dFJlbGlhYmxlSW5kZXgrKztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBmcmFtZS5jb250ZW50ID0gYnVmZmVyLnN1YmFycmF5KGksIGkgKyBtYXhTaXplKTtcbiAgICAgICAgICAgICAgICBmcmFtZS5mcmFnbWVudEluZGV4ID0gZnJhZ21lbnRJbmRleCsrO1xuICAgICAgICAgICAgICAgIGZyYW1lLmZyYWdtZW50SWQgPSBmcmFnbWVudElkO1xuICAgICAgICAgICAgICAgIGZyYW1lLmZyYWdtZW50U2l6ZSA9IE1hdGguY2VpbChidWZmZXIuYnl0ZUxlbmd0aCAvIG1heFNpemUpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWRkRnJhbWVUb1F1ZXVlKGZyYW1lLCBmbGFncyB8IFJha05ldFByaW9yaXR5LklNTUVESUFURSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmFkZEZyYW1lVG9RdWV1ZShmcmFtZSwgZmxhZ3MpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhZGRGcmFtZVRvUXVldWUoZnJhbWU6IEZyYW1lLCBwcmlvcml0eSA9IFJha05ldFByaW9yaXR5Lk5PUk1BTCk6IHZvaWQge1xuICAgICAgICBsZXQgbGVuZ3RoID0gNDsgLy8gZGF0YWdyYW0gaGVhZGVyIHNpemVcbiAgICAgICAgZm9yIChjb25zdCBxdWV1ZWRGcmFtZSBvZiB0aGlzLm91dHB1dEZyYW1lUXVldWUuZnJhbWVzKSB7XG4gICAgICAgICAgICBsZW5ndGggKz0gcXVldWVkRnJhbWUuZ2V0Qnl0ZUxlbmd0aCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGxlbmd0aCArIGZyYW1lLmdldEJ5dGVMZW5ndGgoKSA+IHRoaXMubXR1U2l6ZSAtIDM2KSB7XG4gICAgICAgICAgICB0aGlzLnNlbmRGcmFtZVF1ZXVlKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLm91dHB1dEZyYW1lUXVldWUuZnJhbWVzLnB1c2goZnJhbWUpO1xuXG4gICAgICAgIGlmIChwcmlvcml0eSA9PT0gUmFrTmV0UHJpb3JpdHkuSU1NRURJQVRFKSB7XG4gICAgICAgICAgICB0aGlzLnNlbmRGcmFtZVF1ZXVlKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZVBhY2tldChwYWNrZXQ6IEZyYW1lKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IGlkID0gcGFja2V0LmNvbnRlbnRbMF07XG5cbiAgICAgICAgaWYgKHRoaXMuc3RhdGUgPT09IFNlc3Npb25TdGF0dXMuQ09OTkVDVElORykge1xuICAgICAgICAgICAgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuQ09OTkVDVElPTl9SRVFVRVNUKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oYW5kbGVDb25uZWN0aW9uUmVxdWVzdChwYWNrZXQuY29udGVudCkudGhlbihcbiAgICAgICAgICAgICAgICAgICAgKGVuY2Fwc3VsYXRlZCkgPT4gdGhpcy5zZW5kRnJhbWUoZW5jYXBzdWxhdGVkLCBSYWtOZXRQcmlvcml0eS5JTU1FRElBVEUpLFxuICAgICAgICAgICAgICAgICAgICAoKSA9PiB7fVxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGlkID09PSBNZXNzYWdlSWRlbnRpZmllcnMuTkVXX0lOQ09NSU5HX0NPTk5FQ1RJT04pIHtcbiAgICAgICAgICAgICAgICAvLyBUT0RPOiBvbmxpbmUgbW9kZVxuICAgICAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBTZXNzaW9uU3RhdHVzLkNPTk5FQ1RFRDtcbiAgICAgICAgICAgICAgICB0aGlzLmxpc3RlbmVyLmVtaXQoJ29wZW5Db25uZWN0aW9uJywgdGhpcyk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSBpZiAoaWQgPT09IE1lc3NhZ2VJZGVudGlmaWVycy5ESVNDT05ORUNUSU9OX05PVElGSUNBVElPTikge1xuICAgICAgICAgICAgdGhpcy5kaXNjb25uZWN0KCk7XG4gICAgICAgIH0gZWxzZSBpZiAoaWQgPT09IE1lc3NhZ2VJZGVudGlmaWVycy5DT05ORUNURURfUElORykge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVDb25uZWN0ZWRQaW5nKHBhY2tldC5jb250ZW50KS50aGVuKFxuICAgICAgICAgICAgICAgIChlbmNhcHN1bGF0ZWQpID0+IHRoaXMuc2VuZEZyYW1lKGVuY2Fwc3VsYXRlZCksXG4gICAgICAgICAgICAgICAgKCkgPT4ge31cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAodGhpcy5zdGF0ZSA9PT0gU2Vzc2lvblN0YXR1cy5DT05ORUNURUQpIHtcbiAgICAgICAgICAgIHRoaXMubGlzdGVuZXIuZW1pdCgnZW5jYXBzdWxhdGVkJywgcGFja2V0LCB0aGlzLmdldEFkZHJlc3MoKSk7IC8vIFRvIGZpdCBpbiBzb2Z0d2FyZSBuZWVkcyBsYXRlclxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGhhbmRsZUNvbm5lY3Rpb25SZXF1ZXN0KGJ1ZmZlcjogQnVmZmVyKTogUHJvbWlzZTxGcmFtZT4ge1xuICAgICAgICBjb25zdCBkYXRhUGFja2V0ID0gbmV3IENvbm5lY3Rpb25SZXF1ZXN0KGJ1ZmZlcik7XG4gICAgICAgIGRhdGFQYWNrZXQuZGVjb2RlKCk7XG5cbiAgICAgICAgY29uc3QgcGsgPSBuZXcgQ29ubmVjdGlvblJlcXVlc3RBY2NlcHRlZCgpO1xuICAgICAgICBway5jbGllbnRBZGRyZXNzID0gdGhpcy5nZXRBZGRyZXNzKCk7XG4gICAgICAgIHBrLnJlcXVlc3RUaW1lc3RhbXAgPSBkYXRhUGFja2V0LnJlcXVlc3RUaW1lc3RhbXA7XG4gICAgICAgIHBrLmFjY2VwdGVkVGltZXN0YW1wID0gQmlnSW50KERhdGUubm93KCkpO1xuICAgICAgICBway5lbmNvZGUoKTtcblxuICAgICAgICBjb25zdCBzZW5kUGFja2V0ID0gbmV3IEZyYW1lKCk7XG4gICAgICAgIHNlbmRQYWNrZXQucmVsaWFiaWxpdHkgPSBGcmFtZVJlbGlhYmlsaXR5LlVOUkVMSUFCTEU7XG4gICAgICAgIHNlbmRQYWNrZXQub3JkZXJDaGFubmVsID0gMDtcbiAgICAgICAgc2VuZFBhY2tldC5jb250ZW50ID0gcGsuZ2V0QnVmZmVyKCk7XG5cbiAgICAgICAgcmV0dXJuIHNlbmRQYWNrZXQ7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIGhhbmRsZUNvbm5lY3RlZFBpbmcoYnVmZmVyOiBCdWZmZXIpOiBQcm9taXNlPEZyYW1lPiB7XG4gICAgICAgIGNvbnN0IGRhdGFQYWNrZXQgPSBuZXcgQ29ubmVjdGVkUGluZyhidWZmZXIpO1xuICAgICAgICBkYXRhUGFja2V0LmRlY29kZSgpO1xuXG4gICAgICAgIGNvbnN0IHBrID0gbmV3IENvbm5lY3RlZFBvbmcoKTtcbiAgICAgICAgcGsuY2xpZW50VGltZXN0YW1wID0gZGF0YVBhY2tldC5jbGllbnRUaW1lc3RhbXA7XG4gICAgICAgIHBrLnNlcnZlclRpbWVzdGFtcCA9IEJpZ0ludChEYXRlLm5vdygpKTtcbiAgICAgICAgcGsuZW5jb2RlKCk7XG5cbiAgICAgICAgY29uc3Qgc2VuZFBhY2tldCA9IG5ldyBGcmFtZSgpO1xuICAgICAgICBzZW5kUGFja2V0LnJlbGlhYmlsaXR5ID0gRnJhbWVSZWxpYWJpbGl0eS5VTlJFTElBQkxFO1xuICAgICAgICBzZW5kUGFja2V0Lm9yZGVyQ2hhbm5lbCA9IDA7XG4gICAgICAgIHNlbmRQYWNrZXQuY29udGVudCA9IHBrLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgIHJldHVybiBzZW5kUGFja2V0O1xuICAgIH1cblxuICAgIHB1YmxpYyBoYW5kbGVGcmFnbWVudChmcmFtZTogRnJhbWUpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmZyYWdtZW50c1F1ZXVlLmhhcyhmcmFtZS5mcmFnbWVudElkKSkge1xuICAgICAgICAgICAgdGhpcy5mcmFnbWVudHNRdWV1ZS5zZXQoZnJhbWUuZnJhZ21lbnRJZCwgbmV3IE1hcChbW2ZyYW1lLmZyYWdtZW50SW5kZXgsIGZyYW1lXV0pKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IGVudHJ5ID0gdGhpcy5mcmFnbWVudHNRdWV1ZS5nZXQoZnJhbWUuZnJhZ21lbnRJZCkhO1xuICAgICAgICAgICAgZW50cnkuc2V0KGZyYW1lLmZyYWdtZW50SW5kZXgsIGZyYW1lKTtcblxuICAgICAgICAgICAgLy8gSWYgd2UgaGF2ZSBhbGwgcGllY2VzLCBwdXQgdGhlbSB0b2dldGhlclxuICAgICAgICAgICAgaWYgKGVudHJ5LnNpemUgPT09IGVudHJ5LnZhbHVlcygpLm5leHQoKS52YWx1ZSEuZnJhZ21lbnRTaXplKSB7XG4gICAgICAgICAgICAgICAgY29uc3Qgc3RyZWFtID0gbmV3IEJpbmFyeVN0cmVhbSgpO1xuICAgICAgICAgICAgICAgIC8vIEVuc3VyZSB0aGUgY29ycmVjdG5lc3Mgb2YgdGhlIGJ1ZmZlciBvcmRlcnNcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IGVudHJ5LnNpemU7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBzcGxpdFBhY2tldCA9IGVudHJ5LmdldChpKTtcbiAgICAgICAgICAgICAgICAgICAgLy8gQSBicm9rZW4gb3IgbWFsaWNpb3VzIGNsaWVudCBtYXkgc2VuZCBob2xlc1xuICAgICAgICAgICAgICAgICAgICAvLyB0aGF0IGNvdWxkIGJlIGEgcG9zc2libGUgaXNzdWVcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFzcGxpdFBhY2tldCkgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICBzdHJlYW0ud3JpdGUoc3BsaXRQYWNrZXQuY29udGVudCk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY29uc3QgYXNzZW1ibGVkRnJhbWUgPSBuZXcgRnJhbWUoKTtcbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5jb250ZW50ID0gc3RyZWFtLmdldEJ1ZmZlcigpO1xuXG4gICAgICAgICAgICAgICAgYXNzZW1ibGVkRnJhbWUucmVsaWFiaWxpdHkgPSBmcmFtZS5yZWxpYWJpbGl0eTtcbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5yZWxpYWJsZUluZGV4ID0gZnJhbWUucmVsaWFibGVJbmRleDtcbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5zZXF1ZW5jZUluZGV4ID0gZnJhbWUuc2VxdWVuY2VJbmRleDtcbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5vcmRlckluZGV4ID0gZnJhbWUub3JkZXJJbmRleDtcbiAgICAgICAgICAgICAgICBhc3NlbWJsZWRGcmFtZS5vcmRlckNoYW5uZWwgPSBmcmFtZS5vcmRlckNoYW5uZWw7XG5cbiAgICAgICAgICAgICAgICB0aGlzLmZyYWdtZW50c1F1ZXVlLmRlbGV0ZShmcmFtZS5mcmFnbWVudElkKTtcbiAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUZyYW1lKGFzc2VtYmxlZEZyYW1lKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzZW5kRnJhbWVRdWV1ZSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMub3V0cHV0RnJhbWVRdWV1ZS5mcmFtZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgdGhpcy5vdXRwdXRGcmFtZVF1ZXVlLnNlcXVlbmNlTnVtYmVyID0gdGhpcy5vdXRwdXRTZXF1ZW5jZU51bWJlcisrO1xuICAgICAgICAgICAgdGhpcy5zZW5kRnJhbWVTZXQodGhpcy5vdXRwdXRGcmFtZVF1ZXVlKTtcbiAgICAgICAgICAgIHRoaXMub3V0cHV0RnJhbWVRdWV1ZSA9IG5ldyBGcmFtZVNldCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZW5kRnJhbWVTZXQoZnJhbWVTZXQ6IEZyYW1lU2V0KTogdm9pZCB7XG4gICAgICAgIHRoaXMuc2VuZFBhY2tldChmcmFtZVNldCk7XG4gICAgICAgIHRoaXMub3V0cHV0QmFja3VwUXVldWUuc2V0KFxuICAgICAgICAgICAgZnJhbWVTZXQuc2VxdWVuY2VOdW1iZXIsXG4gICAgICAgICAgICBmcmFtZVNldC5mcmFtZXMuZmlsdGVyKChmcmFtZSkgPT4gZnJhbWUuaXNSZWxpYWJsZSgpKVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2VuZFBhY2tldChwYWNrZXQ6IFBhY2tldCk6IHZvaWQge1xuICAgICAgICB0aGlzLmxpc3RlbmVyLnNlbmRQYWNrZXQocGFja2V0LCB0aGlzLnJpbmZvKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY2xvc2UoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHN0cmVhbSA9IG5ldyBCaW5hcnlTdHJlYW0oQnVmZmVyLmZyb20oJ1xcdTAwMDBcXHUwMDAwXFx1MDAwOFxcdTAwMTUnLCAnYmluYXJ5JykpO1xuICAgICAgICB0aGlzLmFkZEZyYW1lVG9RdWV1ZShuZXcgRnJhbWUoKS5mcm9tQmluYXJ5KHN0cmVhbSksIFJha05ldFByaW9yaXR5LklNTUVESUFURSk7IC8vIENsaWVudCBkaXNjY29uZWN0IHBhY2tldCAweDE1XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogS2ljayBhIGNsaWVudFxuICAgICAqIEBwYXJhbSByZWFzb24gLSB0aGUgcmVhc29uIG1lc3NhZ2UsIG9wdGlvbmFsXG4gICAgICovXG4gICAgcHVibGljIGRpc2Nvbm5lY3QocmVhc29uID0gJ2NsaWVudCBkaXNjb25uZWN0Jyk6IHZvaWQge1xuICAgICAgICAvLyBUT0RPOiByZXdyaXRlLCB3b3JrcyBidXQgY2FuIGJlIGltcHJvdmVkXG4gICAgICAgIHRoaXMuY2xvc2UoKTtcbiAgICAgICAgLy8gU2VuZCBkaXNjb25uZWN0IEFDS1xuICAgICAgICB0aGlzLnN0YXRlID0gU2Vzc2lvblN0YXR1cy5ESVNDT05ORUNURUQ7XG4gICAgICAgIHRoaXMudXBkYXRlKERhdGUubm93KCkpO1xuICAgICAgICB0aGlzLmxpc3RlbmVyLnJlbW92ZVNlc3Npb24odGhpcywgcmVhc29uKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0U3RhdGUoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGU7XG4gICAgfVxuXG4gICAgcHVibGljIGlzQWN0aXZlKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5hY3RpdmU7XG4gICAgfVxuXG4gICAgcHVibGljIGlzRGlzY29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5zdGF0ZSA9PT0gU2Vzc2lvblN0YXR1cy5ESVNDT05ORUNURUQ7XG4gICAgfVxuXG4gICAgcHVibGljIGdldExpc3RlbmVyKCk6IFJha05ldExpc3RlbmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubGlzdGVuZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbWF4bWl1bSB0cmFuc2ZlciB1bml0XG4gICAgICogZm9yIHRoaXMgY29ubmVjdGlvbi5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSB0aGUgVURQIGFkanVzdGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRNVFUoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubXR1U2l6ZSAtIFVEUF9IRUFERVJfU0laRTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0QWRkcmVzcygpOiBJbmV0QWRkcmVzcyB7XG4gICAgICAgIHJldHVybiBuZXcgSW5ldEFkZHJlc3ModGhpcy5yaW5mby5hZGRyZXNzLCB0aGlzLnJpbmZvLnBvcnQsIDQpO1xuICAgIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXFCQSxJQUFZLGlCQUFMLHlCQUFBLGdCQUFBO0NBQ0gsZUFBQSxlQUFBLFlBQUEsS0FBQTtDQUNBLGVBQUEsZUFBQSxlQUFBLEtBQUE7O0FBQ0osRUFBQSxDQUFBLENBQUE7QUFFQSxJQUFZLGdCQUFMLHlCQUFBLGVBQUE7Q0FDSCxjQUFBLGNBQUEsZ0JBQUEsS0FBQTtDQUNBLGNBQUEsY0FBQSxlQUFBLEtBQUE7Q0FDQSxjQUFBLGNBQUEsbUJBQUEsS0FBQTtDQUNBLGNBQUEsY0FBQSxrQkFBQSxLQUFBOztBQUNKLEVBQUEsQ0FBQSxDQUFBO0FBRUEsSUFBcUIsVUFBckIsTUFBNkI7Q0ErQko7Q0FDQTtDQUNEO0NBQ0E7Q0FqQ3BCLFFBQVE7Q0FFUixtQkFBMkIsSUFBSSwwQkFBQSxRQUFTO0NBQ3hDLHVCQUErQjtDQUMvQixzQkFBOEI7Q0FDOUIsb0NBQTJELElBQUksSUFBSTtDQUVuRTtDQUNBO0NBRUEseUNBQThDLElBQUksSUFBSTtDQUN0RCxxQ0FBMEMsSUFBSSxJQUFJO0NBR2xELGlDQUFtRSxJQUFJLElBQUk7Q0FDM0Usc0JBQThCO0NBRTlCLDBCQUFrQztDQUNsQztDQUNBO0NBQ0EscUNBQThELElBQUksSUFBSTtDQUd0RSxhQUE2QixLQUFLLElBQUk7Q0FDdEMsU0FBaUI7Q0FHakIsYUFBOEIsSUFBSSw0QkFBQSxRQUFXO0NBRTdDLFlBQ0ksVUFDQSxTQUNBLE9BQ0EsTUFDRjtFQUptQixL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