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