@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
467 lines (463 loc) • 58.5 kB
JavaScript
'use strict';
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
const raknet = require('@jsprismarine/raknet');
const Console = require('./Console.cjs.cjs');
const SessionManager = require('./SessionManager.cjs.cjs');
const ban_BanManager = require('./ban/BanManager.cjs.cjs');
const block_BlockManager = require('./block/BlockManager.cjs.cjs');
const block_BlockMappings = require('./block/BlockMappings.cjs.cjs');
const chat_ChatManager = require('./chat/ChatManager.cjs.cjs');
const command_CommandManager = require('./command/CommandManager.cjs.cjs');
const events_EventEmitter = require('./events/EventEmitter.cjs.cjs');
require('@jsprismarine/math');
const events_raknet_RaknetConnectEvent = require('./events/raknet/RaknetConnectEvent.cjs.cjs');
const events_raknet_RaknetDisconnectEvent = require('./events/raknet/RaknetDisconnectEvent.cjs.cjs');
const events_raknet_RaknetEncapsulatedPacketEvent = require('./events/raknet/RaknetEncapsulatedPacketEvent.cjs.cjs');
const events_other_TickEvent = require('./events/other/TickEvent.cjs.cjs');
const item_ItemManager = require('./item/ItemManager.cjs.cjs');
const network_ClientConnection = require('./network/ClientConnection.cjs.cjs');
const network_Identifiers = require('./network/Identifiers.cjs.cjs');
const network_PacketRegistry = require('./network/PacketRegistry.cjs.cjs');
const network_packet_BatchPacket = require('./network/packet/BatchPacket.cjs.cjs');
const permission_PermissionManager = require('./permission/PermissionManager.cjs.cjs');
const query_QueryManager = require('./query/QueryManager.cjs.cjs');
const utils_Timer = require('./utils/Timer.cjs.cjs');
const world_WorldManager = require('./world/WorldManager.cjs.cjs');
const _package = require('./package.json.cjs.cjs');
class Server extends events_EventEmitter.EventEmitter {
raknet;
logger;
config;
console;
packetRegistry;
sessionManager = new SessionManager.default();
commandManager;
worldManager;
itemManager;
blockManager;
queryManager;
chatManager;
permissionManager;
banManager;
/**
* If the server is stopping.
* @internal
*/
stopping = false;
/**
* The current ticker timer.
* @internal
*/
tickerTimer;
/**
* The current TPS.
* @internal
*/
tps = 20;
/**
* The current tick.
* @internal
*/
currentTick = 0n;
/**
* If the server is headless.
* @internal
*/
headless;
// TODO: Move this somewhere else.
static MINECRAFT_TICK_TIME_MS = 1e3 / 20;
/**
* Creates a new server instance.
* @param {object} options - The options.
* @param {LoggerBuilder} options.logger - The logger.
* @param {Config} options.config - The config.
* @returns {Server} The server instance.
*/
constructor({ logger, config, headless = false }) {
super();
this.headless = headless;
logger.info(
`Starting JSPrismarine server version §ev${_package.version}§r for Minecraft: Bedrock Edition ${network_Identifiers.default.MinecraftVersions.at(-1)} (protocol version §e${network_Identifiers.default.Protocol}§r)`
);
this.logger = logger;
this.config = config;
this.packetRegistry = new network_PacketRegistry.default(this);
this.itemManager = new item_ItemManager.default(this);
this.blockManager = new block_BlockManager.default(this);
this.worldManager = new world_WorldManager.default(this);
if (!this.headless) this.console = new Console.default(this);
this.commandManager = new command_CommandManager.CommandManager(this);
this.queryManager = new query_QueryManager.QueryManager(this);
this.chatManager = new chat_ChatManager.ChatManager(this);
this.permissionManager = new permission_PermissionManager.PermissionManager(this);
this.banManager = new ban_BanManager.default(this);
}
/**
* Enables the server.
* @returns {Promise<void>} A promise that resolves when the server is enabled.
* @internal
*/
async enable() {
await block_BlockMappings.BlockMappings.initMappings(this);
await this.config.enable();
await this.console?.enable();
await this.logger.enable();
await this.permissionManager.enable();
await this.packetRegistry.enable();
await this.itemManager.enable();
await this.blockManager.enable();
await this.banManager.enable();
await this.commandManager.enable();
await this.worldManager.enable();
this.logger.setConsole(this.console);
}
/**
* Disables the server.
* @returns {Promise<void>} A promise that resolves when the server is disabled.
* @internal
*/
async disable() {
await this.worldManager.disable();
await this.commandManager.disable();
await this.banManager.disable();
await this.blockManager.disable();
await this.itemManager.disable();
await this.permissionManager.disable();
await this.packetRegistry.disable();
await this.config.disable();
await this.logger.disable();
block_BlockMappings.BlockMappings.reset();
}
getMetadata() {
if (!this.raknet) throw new Error("Server is not started");
return this.raknet.serverName;
}
/**
* Reloads the server.
* @returns {Promise<void>} A promise that resolves when the server is reloaded.
* @remarks This method is equivalent to calling {@link Server#disable} and {@link Server#enable}.
* @remarks This method and functionality is unsupported and should ideally be completely avoided.
*/
async reload() {
await this.disable();
await this.enable();
}
/**
* Starts the server.
* @param {string} [serverIp='0.0.0.0'] - The server IP.
* @param {number} [port=19132] - The server port.
* @returns {Promise<void>} A promise that resolves when the server is started.
*/
async bootstrap(serverIp = "0.0.0.0", port = 19132) {
await this.enable();
this.raknet = new raknet.RakNetListener(
this.getConfig().getMaxPlayers(),
this.getConfig().getOnlineMode(),
new raknet.ServerName(this),
this.getLogger()
);
this.raknet.start(serverIp, port);
this.raknet.on("openConnection", async (session) => {
const event = new events_raknet_RaknetConnectEvent.default(session);
await this.emit("raknetConnect", event);
if (event.isCancelled()) {
session.disconnect();
return;
}
const token = session.getAddress().toToken();
if (this.sessionManager.has(token)) {
this.logger.error(`Another client with token (${token}) is already connected!`);
session.disconnect("Already connected from another location");
return;
}
const timer = new utils_Timer.default();
this.logger.debug(`${token} is attempting to connect`);
this.sessionManager.add(token, new network_ClientConnection.default(session, this.logger));
this.logger.verbose(`New connection handling took §e${timer.stop()} ms§r`);
});
this.raknet.on("closeConnection", async (inetAddr, reason) => {
const event = new events_raknet_RaknetDisconnectEvent.default(inetAddr, reason);
await this.emit("raknetDisconnect", event);
const time = Date.now();
const token = inetAddr.toToken();
const session = this.sessionManager.get(token);
if (!session) {
this.logger.debug(`Cannot remove connection from non-existing player (${token})`);
return;
}
await session.closePlayerSession();
this.sessionManager.remove(token);
this.logger.debug(`${token} disconnected due to ${reason}`);
this.logger.debug(`Player destruction took about ${Date.now() - time} ms`);
});
this.raknet.on("encapsulated", async (packet, inetAddr) => {
const event = new events_raknet_RaknetEncapsulatedPacketEvent.default(inetAddr, packet);
await this.emit("raknetEncapsulatedPacket", event);
let connection;
if ((connection = this.sessionManager.get(inetAddr.toToken()) ?? null) === null) {
this.logger.error(`Got a packet from a closed connection (${inetAddr.toToken()})`);
return;
}
try {
const batched = new network_packet_BatchPacket.default(packet.content);
batched.compressed = connection.hasCompression;
for (const buf of await batched.asyncDecode()) {
const pid = buf[0];
if (!this.packetRegistry.getPackets().has(pid)) {
this.logger.warn(`Packet 0x${pid.toString(16)} isn't implemented`);
continue;
}
const packet2 = new (this.packetRegistry.getPackets().get(pid))(buf);
try {
packet2.decode();
} catch (error) {
this.logger.error(error);
this.logger.error(`Error while decoding packet: ${packet2.constructor.name}: ${error}`);
continue;
}
try {
const handler = this.packetRegistry.getHandler(pid);
this.logger.silly(`Received §b${packet2.constructor.name}§r packet`);
await handler.handle(packet2, this, connection.getPlayerSession() ?? connection);
} catch (error) {
this.logger.error(`Handler error ${packet2.constructor.name}-handler: (${error})`);
this.logger.error(error);
}
}
} catch (error) {
this.logger.error(error);
}
});
this.raknet.on("raw", async (buffer, inetAddr) => {
if (!this.config.getEnableQuery()) return;
try {
await this.queryManager.onRaw(buffer, inetAddr);
} catch (error) {
this.logger.verbose(`QueryManager encountered an error`);
this.logger.error(error);
}
});
if (this.config.getEnableTicking()) {
let startTime = Date.now();
let tpsStartTime = Date.now();
let lastTickTime = Date.now();
let tpsStartTick = this.getTick();
const tick = async () => {
if (this.stopping) return;
const event = new events_other_TickEvent.default(this.getTick());
void this.emit("tick", event);
const ticksPerSecond = 1e3 / Server.MINECRAFT_TICK_TIME_MS;
await Promise.all(this.worldManager.getWorlds().map((world) => world.update(event.getTick())));
if (this.config.getEnableProcessTitle() && this.getTick() % ticksPerSecond === 0 && !this.headless) {
process.title = `TPS: ${this.getTPS().toFixed(2)} | Tick: ${this.getTick()} | ${process.title.split("| ").at(-1)}`;
}
this.currentTick++;
const endTime = Date.now();
const elapsedTime = endTime - startTime;
const expectedElapsedTime = this.getTick() * Server.MINECRAFT_TICK_TIME_MS;
const executionTime = endTime - lastTickTime;
let sleepTime = Server.MINECRAFT_TICK_TIME_MS - executionTime;
if (elapsedTime < expectedElapsedTime) {
sleepTime += expectedElapsedTime - elapsedTime;
} else if (elapsedTime > expectedElapsedTime) {
sleepTime = Math.max(0, sleepTime - (elapsedTime - expectedElapsedTime));
}
if (tpsStartTime !== endTime) {
this.tps = (this.getTick() - tpsStartTick) * 1e3 / (endTime - tpsStartTime);
}
if (endTime - tpsStartTime >= 1e3) {
tpsStartTick = this.getTick();
tpsStartTime = endTime;
}
this.tps = Math.min(this.tps, 20);
lastTickTime = endTime;
this.tickerTimer = setTimeout(tick, sleepTime);
this.tickerTimer.unref();
};
void tick();
}
this.logger.info(`JSPrismarine is now listening on port §b${port}`);
}
/**
* Kills the server asynchronously.
* @param {object} [options] - The options.
* @param {boolean} [options.crash] - If the server should crash.
* @param {boolean} [options.stayAlive] - If we should let the process stay alive.
* @returns {Promise<void>} A promise that resolves when the server is killed.
*/
async shutdown(options) {
if (this.stopping) return;
this.stopping = true;
this.logger.info("Stopping server", "Server/kill");
await this.console?.disable();
clearInterval(this.tickerTimer);
try {
await this.sessionManager.kickAllPlayers("Server closed.");
await this.disable();
this.raknet?.kill();
this.removeAllListeners();
console.debug("Server stopped, Goodbye!\n");
if (!options?.stayAlive) process.exit(options?.crash ? 1 : 0);
} catch (error) {
console.error(error);
if (!options?.stayAlive) process.exit(1);
}
}
async broadcastPacket(dataPacket) {
for (const onlinePlayer of this.sessionManager.getAllPlayers()) {
await onlinePlayer.getNetworkSession().getConnection().sendDataPacket(dataPacket);
}
}
/**
* Returns the server version.
* @returns {string} The server version.
* @example
* ```typescript
* console.log(server.getVersion());
* ```
*/
getVersion() {
return _package.version;
}
/**
* Returns the identifiers.
* @returns {Identifiers} The identifiers.
*/
getIdentifiers() {
return network_Identifiers.default;
}
/**
* Returns the query manager.
* @returns {QueryManager} The query manager.
*/
getQueryManager() {
return this.queryManager;
}
/**
* Returns the command manager.
* @returns {CommandManager} The command manager.
*/
getCommandManager() {
return this.commandManager;
}
/**
* Returns the player manager.
* @returns {SessionManager} The player manager.
*/
getSessionManager() {
return this.sessionManager;
}
/**
* Returns the world manager.
* @returns {WorldManager} The world manager.
*/
getWorldManager() {
return this.worldManager;
}
/**
* Returns the item manager.
* @returns {ItemManager} The item manager.
*/
getItemManager() {
return this.itemManager;
}
/**
* Returns the block manager.
* @returns {BlockManager} The block manager.
*/
getBlockManager() {
return this.blockManager;
}
/**
* Returns the logger.
* @returns {LoggerBuilder} The logger.
* @example
* ```typescript
* // Normal log:
* server.getLogger().info('Hello, world!');
* // Debug log:
* server.getLogger().debug('Hello, world!');
* // Error log:
* server.getLogger().error(new Error('Hello World'));
* ```
*/
getLogger() {
return this.logger;
}
/**
* Returns the packet registry.
* @returns {PacketRegistry} The packet registry.
*/
getPacketRegistry() {
return this.packetRegistry;
}
/**
* Returns the raknet instance.
* @returns {RakNetListener | undefined} The raknet instance.
*/
getRaknet() {
return this.raknet;
}
/**
* Returns the chat manager.
* @returns {ChatManager} The chat manager.
*/
getChatManager() {
return this.chatManager;
}
/**
* Returns the config.
* @returns {Config} The config.
* @example
* ```typescript
* console.log(server.getConfig().getMaxPlayers()); // 20
* ```
*/
getConfig() {
return this.config;
}
/**
* Returns the console instance.
* @returns {Console | undefined} The console instance.
*/
getConsole() {
return this.console;
}
/**
* Returns the permission manager.
* @returns {PermissionManager} The permission manager.
*/
getPermissionManager() {
return this.permissionManager;
}
/**
* Returns the ban manager.
* @returns {BanManager} The ban manager.
*/
getBanManager() {
return this.banManager;
}
/**
* Returns this Prismarine instance.
* @returns {Server} The Prismarine instance.
*/
getServer() {
return this;
}
/**
* Returns the current Tick.
* @returns {number} The current Tick.
*/
getTick() {
return Number(this.currentTick);
}
/**
* Returns the current TPS.
* @returns {number} The current TPS.
*/
getTPS() {
return Number.parseFloat(this.tps.toFixed(2));
}
}
exports.default = Server;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmNqcy5janMiLCJzb3VyY2VzIjpbIi4uL3NyYy9TZXJ2ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgUmFrTmV0TGlzdGVuZXIsIFNlcnZlck5hbWUgfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgQ29uc29sZSBmcm9tICcuL0NvbnNvbGUnO1xuaW1wb3J0IFNlc3Npb25NYW5hZ2VyIGZyb20gJy4vU2Vzc2lvbk1hbmFnZXInO1xuaW1wb3J0IEJhbk1hbmFnZXIgZnJvbSAnLi9iYW4vQmFuTWFuYWdlcic7XG5pbXBvcnQgQmxvY2tNYW5hZ2VyIGZyb20gJy4vYmxvY2svQmxvY2tNYW5hZ2VyJztcbmltcG9ydCB7IEJsb2NrTWFwcGluZ3MgfSBmcm9tICcuL2Jsb2NrL0Jsb2NrTWFwcGluZ3MnO1xuaW1wb3J0IHsgQ2hhdE1hbmFnZXIgfSBmcm9tICcuL2NoYXQvQ2hhdE1hbmFnZXInO1xuaW1wb3J0IHsgQ29tbWFuZE1hbmFnZXIgfSBmcm9tICcuL2NvbW1hbmQvQ29tbWFuZE1hbmFnZXInO1xuaW1wb3J0IHsgRXZlbnRFbWl0dGVyIH0gZnJvbSAnLi9ldmVudHMvRXZlbnRFbWl0dGVyJztcbmltcG9ydCB7IFRpY2tFdmVudCB9IGZyb20gJy4vZXZlbnRzL0V2ZW50cyc7XG5pbXBvcnQgUmFrbmV0Q29ubmVjdEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXRDb25uZWN0RXZlbnQnO1xuaW1wb3J0IFJha25ldERpc2Nvbm5lY3RFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RGlzY29ubmVjdEV2ZW50JztcbmltcG9ydCBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudCBmcm9tICcuL2V2ZW50cy9yYWtuZXQvUmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0RXZlbnQnO1xuaW1wb3J0IEl0ZW1NYW5hZ2VyIGZyb20gJy4vaXRlbS9JdGVtTWFuYWdlcic7XG5pbXBvcnQgQ2xpZW50Q29ubmVjdGlvbiBmcm9tICcuL25ldHdvcmsvQ2xpZW50Q29ubmVjdGlvbic7XG5pbXBvcnQgSWRlbnRpZmllcnMgZnJvbSAnLi9uZXR3b3JrL0lkZW50aWZpZXJzJztcbmltcG9ydCBQYWNrZXRSZWdpc3RyeSBmcm9tICcuL25ldHdvcmsvUGFja2V0UmVnaXN0cnknO1xuaW1wb3J0IHR5cGUgeyBEYXRhUGFja2V0IH0gZnJvbSAnLi9uZXR3b3JrL1BhY2tldHMnO1xuaW1wb3J0IEJhdGNoUGFja2V0IGZyb20gJy4vbmV0d29yay9wYWNrZXQvQmF0Y2hQYWNrZXQnO1xuaW1wb3J0IHsgUGVybWlzc2lvbk1hbmFnZXIgfSBmcm9tICcuL3Blcm1pc3Npb24vUGVybWlzc2lvbk1hbmFnZXInO1xuaW1wb3J0IHsgUXVlcnlNYW5hZ2VyIH0gZnJvbSAnLi9xdWVyeS9RdWVyeU1hbmFnZXInO1xuaW1wb3J0IFRpbWVyIGZyb20gJy4vdXRpbHMvVGltZXInO1xuaW1wb3J0IFdvcmxkTWFuYWdlciBmcm9tICcuL3dvcmxkL1dvcmxkTWFuYWdlcic7XG5cbmltcG9ydCB0eXBlIHsgSW5ldEFkZHJlc3MsIFJha05ldFNlc3Npb24gfSBmcm9tICdAanNwcmlzbWFyaW5lL3Jha25ldCc7XG5pbXBvcnQgdHlwZSB7IENvbmZpZyB9IGZyb20gJy4vY29uZmlnL0NvbmZpZyc7XG5cbmltcG9ydCB0eXBlIHsgTG9nZ2VyIH0gZnJvbSAnQGpzcHJpc21hcmluZS9sb2dnZXInO1xuaW1wb3J0IHsgdmVyc2lvbiB9IGZyb20gJy4uL3BhY2thZ2UuanNvbicgd2l0aCB7IHR5cGU6ICdqc29uJyB9O1xuXG4vKipcbiAqIEpTUHJpc21hcmluZSdzIG1haW4gc2VydmVyIGNsYXNzLlxuICogQHB1YmxpY1xuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTZXJ2ZXIgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICAgIHByaXZhdGUgcmFrbmV0OiBSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZDtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGxvZ2dlcjogTG9nZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29uZmlnOiBDb25maWc7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25zb2xlOiBDb25zb2xlIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGFja2V0UmVnaXN0cnk6IFBhY2tldFJlZ2lzdHJ5O1xuICAgIHByaXZhdGUgcmVhZG9ubHkgc2Vzc2lvbk1hbmFnZXIgPSBuZXcgU2Vzc2lvbk1hbmFnZXIoKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbW1hbmRNYW5hZ2VyOiBDb21tYW5kTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHdvcmxkTWFuYWdlcjogV29ybGRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaXRlbU1hbmFnZXI6IEl0ZW1NYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYmxvY2tNYW5hZ2VyOiBCbG9ja01hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBxdWVyeU1hbmFnZXI6IFF1ZXJ5TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNoYXRNYW5hZ2VyOiBDaGF0TWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBlcm1pc3Npb25NYW5hZ2VyOiBQZXJtaXNzaW9uTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IGJhbk1hbmFnZXI6IEJhbk1hbmFnZXI7XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIHN0b3BwaW5nLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgc3RvcHBpbmcgPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IHRpY2tlciB0aW1lci5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRpY2tlclRpbWVyOiBOb2RlSlMuVGltZW91dCB8IHVuZGVmaW5lZDtcblxuICAgIC8qKlxuICAgICAqIFRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIHRwcyA9IDIwO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGljay5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGN1cnJlbnRUaWNrID0gMG47XG5cbiAgICAvKipcbiAgICAgKiBJZiB0aGUgc2VydmVyIGlzIGhlYWRsZXNzLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaGVhZGxlc3M6IGJvb2xlYW47XG5cbiAgICAvLyBUT0RPOiBNb3ZlIHRoaXMgc29tZXdoZXJlIGVsc2UuXG4gICAgcHJpdmF0ZSBzdGF0aWMgcmVhZG9ubHkgTUlORUNSQUZUX1RJQ0tfVElNRV9NUyA9IDEwMDAgLyAyMDtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBuZXcgc2VydmVyIGluc3RhbmNlLlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBvcHRpb25zIC0gVGhlIG9wdGlvbnMuXG4gICAgICogQHBhcmFtIHtMb2dnZXJCdWlsZGVyfSBvcHRpb25zLmxvZ2dlciAtIFRoZSBsb2dnZXIuXG4gICAgICogQHBhcmFtIHtDb25maWd9IG9wdGlvbnMuY29uZmlnIC0gVGhlIGNvbmZpZy5cbiAgICAgKiBAcmV0dXJucyB7U2VydmVyfSBUaGUgc2VydmVyIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcih7IGxvZ2dlciwgY29uZmlnLCBoZWFkbGVzcyA9IGZhbHNlIH06IHsgbG9nZ2VyOiBMb2dnZXI7IGNvbmZpZzogQ29uZmlnOyBoZWFkbGVzcz86IGJvb2xlYW4gfSkge1xuICAgICAgICBzdXBlcigpO1xuXG4gICAgICAgIHRoaXMuaGVhZGxlc3MgPSBoZWFkbGVzcztcblxuICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICAgIGBTdGFydGluZyBKU1ByaXNtYXJpbmUgc2VydmVyIHZlcnNpb24gwqdldiR7dmVyc2lvbn3Cp3IgZm9yIE1pbmVjcmFmdDogQmVkcm9jayBFZGl0aW9uICR7SWRlbnRpZmllcnMuTWluZWNyYWZ0VmVyc2lvbnMuYXQoLTEpfSAocHJvdG9jb2wgdmVyc2lvbiDCp2Uke0lkZW50aWZpZXJzLlByb3RvY29sfcKncilgXG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIgPSBsb2dnZXI7XG4gICAgICAgIHRoaXMuY29uZmlnID0gY29uZmlnO1xuICAgICAgICB0aGlzLnBhY2tldFJlZ2lzdHJ5ID0gbmV3IFBhY2tldFJlZ2lzdHJ5KHRoaXMpO1xuICAgICAgICB0aGlzLml0ZW1NYW5hZ2VyID0gbmV3IEl0ZW1NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJsb2NrTWFuYWdlciA9IG5ldyBCbG9ja01hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMud29ybGRNYW5hZ2VyID0gbmV3IFdvcmxkTWFuYWdlcih0aGlzKTtcbiAgICAgICAgaWYgKCF0aGlzLmhlYWRsZXNzKSB0aGlzLmNvbnNvbGUgPSBuZXcgQ29uc29sZSh0aGlzKTtcbiAgICAgICAgdGhpcy5jb21tYW5kTWFuYWdlciA9IG5ldyBDb21tYW5kTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy5xdWVyeU1hbmFnZXIgPSBuZXcgUXVlcnlNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmNoYXRNYW5hZ2VyID0gbmV3IENoYXRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyID0gbmV3IFBlcm1pc3Npb25NYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLmJhbk1hbmFnZXIgPSBuZXcgQmFuTWFuYWdlcih0aGlzKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbmFibGVzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBlbmFibGVkLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgYXN5bmMgZW5hYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCBCbG9ja01hcHBpbmdzLmluaXRNYXBwaW5ncyh0aGlzKTtcblxuICAgICAgICBhd2FpdCB0aGlzLmNvbmZpZy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25zb2xlPy5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGVybWlzc2lvbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGFja2V0UmVnaXN0cnkuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmxvY2tNYW5hZ2VyLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMud29ybGRNYW5hZ2VyLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMubG9nZ2VyLnNldENvbnNvbGUodGhpcy5jb25zb2xlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgZGlzYWJsZWQuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBkaXNhYmxlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLndvcmxkTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29tbWFuZE1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJhbk1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmJsb2NrTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuaXRlbU1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBlcm1pc3Npb25NYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wYWNrZXRSZWdpc3RyeS5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uZmlnLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZGlzYWJsZSgpO1xuXG4gICAgICAgIEJsb2NrTWFwcGluZ3MucmVzZXQoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0TWV0YWRhdGEoKSB7XG4gICAgICAgIGlmICghdGhpcy5yYWtuZXQpIHRocm93IG5ldyBFcnJvcignU2VydmVyIGlzIG5vdCBzdGFydGVkJyk7XG4gICAgICAgIHJldHVybiB0aGlzLnJha25ldC5zZXJ2ZXJOYW1lO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIHJlbG9hZGVkLlxuICAgICAqIEByZW1hcmtzIFRoaXMgbWV0aG9kIGlzIGVxdWl2YWxlbnQgdG8gY2FsbGluZyB7QGxpbmsgU2VydmVyI2Rpc2FibGV9IGFuZCB7QGxpbmsgU2VydmVyI2VuYWJsZX0uXG4gICAgICogQHJlbWFya3MgVGhpcyBtZXRob2QgYW5kIGZ1bmN0aW9uYWxpdHkgaXMgdW5zdXBwb3J0ZWQgYW5kIHNob3VsZCBpZGVhbGx5IGJlIGNvbXBsZXRlbHkgYXZvaWRlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgcmVsb2FkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5lbmFibGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTdGFydHMgdGhlIHNlcnZlci5cbiAgICAgKiBAcGFyYW0ge3N0cmluZ30gW3NlcnZlcklwPScwLjAuMC4wJ10gLSBUaGUgc2VydmVyIElQLlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBbcG9ydD0xOTEzMl0gLSBUaGUgc2VydmVyIHBvcnQuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBzdGFydGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBib290c3RyYXAoc2VydmVySXAgPSAnMC4wLjAuMCcsIHBvcnQgPSAxOTEzMik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBhd2FpdCB0aGlzLmVuYWJsZSgpO1xuXG4gICAgICAgIHRoaXMucmFrbmV0ID0gbmV3IFJha05ldExpc3RlbmVyKFxuICAgICAgICAgICAgdGhpcy5nZXRDb25maWcoKS5nZXRNYXhQbGF5ZXJzKCksXG4gICAgICAgICAgICB0aGlzLmdldENvbmZpZygpLmdldE9ubGluZU1vZGUoKSxcbiAgICAgICAgICAgIG5ldyBTZXJ2ZXJOYW1lKHRoaXMpLFxuICAgICAgICAgICAgdGhpcy5nZXRMb2dnZXIoKVxuICAgICAgICApO1xuICAgICAgICB0aGlzLnJha25ldC5zdGFydChzZXJ2ZXJJcCwgcG9ydCk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ29wZW5Db25uZWN0aW9uJywgYXN5bmMgKHNlc3Npb246IFJha05ldFNlc3Npb24pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldENvbm5lY3RFdmVudChzZXNzaW9uKTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0Q29ubmVjdCcsIGV2ZW50KTtcblxuICAgICAgICAgICAgaWYgKGV2ZW50LmlzQ2FuY2VsbGVkKCkpIHtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmRpc2Nvbm5lY3QoKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gc2Vzc2lvbi5nZXRBZGRyZXNzKCkudG9Ub2tlbigpO1xuICAgICAgICAgICAgaWYgKHRoaXMuc2Vzc2lvbk1hbmFnZXIuaGFzKHRva2VuKSkge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGBBbm90aGVyIGNsaWVudCB3aXRoIHRva2VuICgke3Rva2VufSkgaXMgYWxyZWFkeSBjb25uZWN0ZWQhYCk7XG4gICAgICAgICAgICAgICAgc2Vzc2lvbi5kaXNjb25uZWN0KCdBbHJlYWR5IGNvbm5lY3RlZCBmcm9tIGFub3RoZXIgbG9jYXRpb24nKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWVyID0gbmV3IFRpbWVyKCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0b2tlbn0gaXMgYXR0ZW1wdGluZyB0byBjb25uZWN0YCk7XG4gICAgICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLmFkZCh0b2tlbiwgbmV3IENsaWVudENvbm5lY3Rpb24oc2Vzc2lvbiwgdGhpcy5sb2dnZXIpKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYE5ldyBjb25uZWN0aW9uIGhhbmRsaW5nIHRvb2sgwqdlJHt0aW1lci5zdG9wKCl9IG1zwqdyYCk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdjbG9zZUNvbm5lY3Rpb24nLCBhc3luYyAoaW5ldEFkZHI6IEluZXRBZGRyZXNzLCByZWFzb246IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0RGlzY29ubmVjdEV2ZW50KGluZXRBZGRyLCByZWFzb24pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXREaXNjb25uZWN0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBjb25zdCB0aW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGNvbnN0IHRva2VuID0gaW5ldEFkZHIudG9Ub2tlbigpO1xuXG4gICAgICAgICAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5zZXNzaW9uTWFuYWdlci5nZXQodG9rZW4pO1xuICAgICAgICAgICAgaWYgKCFzZXNzaW9uKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYENhbm5vdCByZW1vdmUgY29ubmVjdGlvbiBmcm9tIG5vbi1leGlzdGluZyBwbGF5ZXIgKCR7dG9rZW59KWApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgYXdhaXQgc2Vzc2lvbi5jbG9zZVBsYXllclNlc3Npb24oKTtcblxuICAgICAgICAgICAgdGhpcy5zZXNzaW9uTWFuYWdlci5yZW1vdmUodG9rZW4pO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dG9rZW59IGRpc2Nvbm5lY3RlZCBkdWUgdG8gJHtyZWFzb259YCk7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgUGxheWVyIGRlc3RydWN0aW9uIHRvb2sgYWJvdXQgJHtEYXRlLm5vdygpIC0gdGltZX0gbXNgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ2VuY2Fwc3VsYXRlZCcsIGFzeW5jIChwYWNrZXQ6IGFueSwgaW5ldEFkZHI6IEluZXRBZGRyZXNzKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudChpbmV0QWRkciwgcGFja2V0KTtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuZW1pdCgncmFrbmV0RW5jYXBzdWxhdGVkUGFja2V0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBsZXQgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbiB8IG51bGw7XG4gICAgICAgICAgICBpZiAoKGNvbm5lY3Rpb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldChpbmV0QWRkci50b1Rva2VuKCkpID8/IG51bGwpID09PSBudWxsKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEdvdCBhIHBhY2tldCBmcm9tIGEgY2xvc2VkIGNvbm5lY3Rpb24gKCR7aW5ldEFkZHIudG9Ub2tlbigpfSlgKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgLy8gUmVhZCBiYXRjaCBjb250ZW50IGFuZCBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGNvbnN0IGJhdGNoZWQgPSBuZXcgQmF0Y2hQYWNrZXQocGFja2V0LmNvbnRlbnQpO1xuICAgICAgICAgICAgICAgIGJhdGNoZWQuY29tcHJlc3NlZCA9IGNvbm5lY3Rpb24uaGFzQ29tcHJlc3Npb247XG5cbiAgICAgICAgICAgICAgICAvLyBSZWFkIGFsbCBwYWNrZXRzIGluc2lkZSBiYXRjaCBhbmQgaGFuZGxlIHRoZW1cbiAgICAgICAgICAgICAgICBmb3IgKGNvbnN0IGJ1ZiBvZiBhd2FpdCBiYXRjaGVkLmFzeW5jRGVjb2RlKCkpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcGlkID0gYnVmWzBdITtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIXRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmhhcyhwaWQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci53YXJuKGBQYWNrZXQgMHgke3BpZC50b1N0cmluZygxNil9IGlzbid0IGltcGxlbWVudGVkYCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIC8vIEdldCBwYWNrZXQgZnJvbSByZWdpc3RyeVxuICAgICAgICAgICAgICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgKHRoaXMucGFja2V0UmVnaXN0cnkuZ2V0UGFja2V0cygpLmdldChwaWQpISkoYnVmKTtcblxuICAgICAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGFja2V0LmRlY29kZSgpO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEVycm9yIHdoaWxlIGRlY29kaW5nIHBhY2tldDogJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX06ICR7ZXJyb3J9YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBoYW5kbGVyID0gdGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRIYW5kbGVyKHBpZCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5zaWxseShgUmVjZWl2ZWQgwqdiJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX3Cp3IgcGFja2V0YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCAoaGFuZGxlciBhcyBhbnkpLmhhbmRsZShwYWNrZXQsIHRoaXMsIGNvbm5lY3Rpb24uZ2V0UGxheWVyU2Vzc2lvbigpID8/IGNvbm5lY3Rpb24pO1xuICAgICAgICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEhhbmRsZXIgZXJyb3IgJHtwYWNrZXQuY29uc3RydWN0b3IubmFtZX0taGFuZGxlcjogKCR7ZXJyb3J9KWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMucmFrbmV0Lm9uKCdyYXcnLCBhc3luYyAoYnVmZmVyOiBCdWZmZXIsIGluZXRBZGRyOiBJbmV0QWRkcmVzcykgPT4ge1xuICAgICAgICAgICAgaWYgKCF0aGlzLmNvbmZpZy5nZXRFbmFibGVRdWVyeSgpKSByZXR1cm47XG5cbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5xdWVyeU1hbmFnZXIub25SYXcoYnVmZmVyLCBpbmV0QWRkcik7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLnZlcmJvc2UoYFF1ZXJ5TWFuYWdlciBlbmNvdW50ZXJlZCBhbiBlcnJvcmApO1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmdldEVuYWJsZVRpY2tpbmcoKSkge1xuICAgICAgICAgICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgdHBzU3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCBsYXN0VGlja1RpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IHRwc1N0YXJ0VGljayA9IHRoaXMuZ2V0VGljaygpO1xuICAgICAgICAgICAgY29uc3QgdGljayA9IGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5zdG9wcGluZykgcmV0dXJuO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGlja0V2ZW50KHRoaXMuZ2V0VGljaygpKTtcbiAgICAgICAgICAgICAgICB2b2lkIHRoaXMuZW1pdCgndGljaycsIGV2ZW50KTtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHRpY2tzUGVyU2Vjb25kID0gMTAwMCAvIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuXG4gICAgICAgICAgICAgICAgLy8gVXBkYXRlIGFsbCB3b3JsZHMuXG4gICAgICAgICAgICAgICAgYXdhaXQgUHJvbWlzZS5hbGwodGhpcy53b3JsZE1hbmFnZXIuZ2V0V29ybGRzKCkubWFwKCh3b3JsZCkgPT4gd29ybGQudXBkYXRlKGV2ZW50LmdldFRpY2soKSkpKTtcblxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmNvbmZpZy5nZXRFbmFibGVQcm9jZXNzVGl0bGUoKSAmJiB0aGlzLmdldFRpY2soKSAlIHRpY2tzUGVyU2Vjb25kID09PSAwICYmICF0aGlzLmhlYWRsZXNzKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIFVwZGF0ZSB0aGUgcHJvY2VzcyB0aXRsZSB3aXRoIFRQUyBhbmQgdGljay5cbiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzcy50aXRsZSA9IGBUUFM6ICR7dGhpcy5nZXRUUFMoKS50b0ZpeGVkKDIpfSB8IFRpY2s6ICR7dGhpcy5nZXRUaWNrKCl9IHwgJHtwcm9jZXNzLnRpdGxlLnNwbGl0KCd8ICcpLmF0KC0xKSF9YDtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLmN1cnJlbnRUaWNrKys7XG4gICAgICAgICAgICAgICAgY29uc3QgZW5kVGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICAgICAgY29uc3QgZWxhcHNlZFRpbWUgPSBlbmRUaW1lIC0gc3RhcnRUaW1lO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4cGVjdGVkRWxhcHNlZFRpbWUgPSB0aGlzLmdldFRpY2soKSAqIFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TO1xuICAgICAgICAgICAgICAgIGNvbnN0IGV4ZWN1dGlvblRpbWUgPSBlbmRUaW1lIC0gbGFzdFRpY2tUaW1lO1xuXG4gICAgICAgICAgICAgICAgLy8gQWRqdXN0IHNsZWVwVGltZSBiYXNlZCBvbiBleGVjdXRpb24gc3BlZWQuXG4gICAgICAgICAgICAgICAgbGV0IHNsZWVwVGltZSA9IFNlcnZlci5NSU5FQ1JBRlRfVElDS19USU1FX01TIC0gZXhlY3V0aW9uVGltZTtcbiAgICAgICAgICAgICAgICBpZiAoZWxhcHNlZFRpbWUgPCBleHBlY3RlZEVsYXBzZWRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHdlJ3JlIHJ1bm5pbmcgZmFzdGVyIHRoYW4gZXhwZWN0ZWQsIGluY3JlYXNlIHNsZWVwVGltZS5cbiAgICAgICAgICAgICAgICAgICAgc2xlZXBUaW1lICs9IGV4cGVjdGVkRWxhcHNlZFRpbWUgLSBlbGFwc2VkVGltZTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKGVsYXBzZWRUaW1lID4gZXhwZWN0ZWRFbGFwc2VkVGltZSkge1xuICAgICAgICAgICAgICAgICAgICAvLyBJZiB3ZSdyZSBydW5uaW5nIHNsb3dlciB0aGFuIGV4cGVjdGVkLCBkZWNyZWFzZSBzbGVlcFRpbWUgYnV0IGRvbid0IGxldCBpdCBnbyBiZWxvdyAwLlxuICAgICAgICAgICAgICAgICAgICBzbGVlcFRpbWUgPSBNYXRoLm1heCgwLCBzbGVlcFRpbWUgLSAoZWxhcHNlZFRpbWUgLSBleHBlY3RlZEVsYXBzZWRUaW1lKSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gQ2FsY3VsYXRlIHRwcyBiYXNlZCBvbiB0aGUgYWN0dWFsIGVsYXBzZWQgdGltZSBzaW5jZSB0aGUgc3RhcnQgb2YgdGhlIHRpY2suXG4gICAgICAgICAgICAgICAgaWYgKHRwc1N0YXJ0VGltZSAhPT0gZW5kVGltZSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnRwcyA9ICgodGhpcy5nZXRUaWNrKCkgLSB0cHNTdGFydFRpY2spICogMTAwMCkgLyAoZW5kVGltZSAtIHRwc1N0YXJ0VGltZSk7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgaWYgKGVuZFRpbWUgLSB0cHNTdGFydFRpbWUgPj0gMTAwMCkge1xuICAgICAgICAgICAgICAgICAgICB0cHNTdGFydFRpY2sgPSB0aGlzLmdldFRpY2soKTtcbiAgICAgICAgICAgICAgICAgICAgdHBzU3RhcnRUaW1lID0gZW5kVGltZTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICB0aGlzLnRwcyA9IE1hdGgubWluKHRoaXMudHBzLCAyMCk7IC8vIEVuc3VyZSB0cHMgZG9lcyBub3QgZXhjZWVkIDIwXG5cbiAgICAgICAgICAgICAgICBsYXN0VGlja1RpbWUgPSBlbmRUaW1lO1xuICAgICAgICAgICAgICAgIHRoaXMudGlja2VyVGltZXIgPSBzZXRUaW1lb3V0KHRpY2ssIHNsZWVwVGltZSk7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lci51bnJlZigpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gU3RhcnQgdGlja2luZ1xuICAgICAgICAgICAgdm9pZCB0aWNrKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxvZ2dlci5pbmZvKGBKU1ByaXNtYXJpbmUgaXMgbm93IGxpc3RlbmluZyBvbiBwb3J0IMKnYiR7cG9ydH1gKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBLaWxscyB0aGUgc2VydmVyIGFzeW5jaHJvbm91c2x5LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0aW9uc10gLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmNyYXNoXSAtIElmIHRoZSBzZXJ2ZXIgc2hvdWxkIGNyYXNoLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMuc3RheUFsaXZlXSAtIElmIHdlIHNob3VsZCBsZXQgdGhlIHByb2Nlc3Mgc3RheSBhbGl2ZS5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGtpbGxlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2h1dGRvd24ob3B0aW9ucz86IHsgY3Jhc2g/OiBib29sZWFuOyBzdGF5QWxpdmU/OiBib29sZWFuIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuc3RvcHBpbmcpIHJldHVybjtcbiAgICAgICAgdGhpcy5zdG9wcGluZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnU3RvcHBpbmcgc2VydmVyJywgJ1NlcnZlci9raWxsJyk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uc29sZT8uZGlzYWJsZSgpO1xuXG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aWNrZXJUaW1lcik7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEtpY2sgYWxsIG9ubGluZSBwbGF5ZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXNzaW9uTWFuYWdlci5raWNrQWxsUGxheWVycygnU2VydmVyIGNsb3NlZC4nKTtcblxuICAgICAgICAgICAgLy8gRGlzYWJsZSBhbGwgbWFuYWdlcnMuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcblxuICAgICAgICAgICAgLy8gYHRoaXMucmFrbmV0YCBtaWdodCBiZSB1bmRlZmluZWQgaWYgd2Uga2lsbCB0aGUgc2VydmVyIHJlYWxseSBlYXJseS5cbiAgICAgICAgICAgIHRoaXMucmFrbmV0Py5raWxsKCk7XG5cbiAgICAgICAgICAgIC8vIEZpbmFsbHksIHJlbW92ZSBhbGwgbGlzdGVuZXJzLlxuICAgICAgICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcblxuICAgICAgICAgICAgLy8gTG9nZ2VyIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuXG4gICAgICAgICAgICBjb25zb2xlLmRlYnVnKCdTZXJ2ZXIgc3RvcHBlZCwgR29vZGJ5ZSFcXG4nKTtcblxuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdChvcHRpb25zPy5jcmFzaCA/IDEgOiAwKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBicm9hZGNhc3RQYWNrZXQ8VCBleHRlbmRzIERhdGFQYWNrZXQ+KGRhdGFQYWNrZXQ6IFQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gTWF5YmUgaSBjYW4gaW1wcm92ZSB0aGlzIGJ5IHVzaW5nIHRoZSBVRFAgYnJvYWRjYXN0LCBhbGwgdW5jb25uZWN0ZWQgY2xpZW50c1xuICAgICAgICAvLyB3aWxsIGlnbm9yZSB0aGUgY29ubmVjdGVkIHBhY2tldCBwcm9iYWJseSwgYnV0IG1heSBjYXVzZSBpc3N1ZXMuXG4gICAgICAgIGZvciAoY29uc3Qgb25saW5lUGxheWVyIG9mIHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0QWxsUGxheWVycygpKSB7XG4gICAgICAgICAgICBhd2FpdCBvbmxpbmVQbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQoZGF0YVBhY2tldCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzZXJ2ZXIgdmVyc2lvbi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldFZlcnNpb24oKSk7XG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldFZlcnNpb24oKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaWRlbnRpZmllcnMuXG4gICAgICogQHJldHVybnMge0lkZW50aWZpZXJzfSBUaGUgaWRlbnRpZmllcnMuXG4gICAgICovXG4gICAgcHVibGljIGdldElkZW50aWZpZXJzKCk6IHR5cGVvZiBJZGVudGlmaWVycyB7XG4gICAgICAgIHJldHVybiBJZGVudGlmaWVycztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtRdWVyeU1hbmFnZXJ9IFRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRRdWVyeU1hbmFnZXIoKTogUXVlcnlNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucXVlcnlNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbW1hbmQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q29tbWFuZE1hbmFnZXJ9IFRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbW1hbmRNYW5hZ2VyKCk6IENvbW1hbmRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29tbWFuZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1Nlc3Npb25NYW5hZ2VyfSBUaGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFNlc3Npb25NYW5hZ2VyKCk6IFNlc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7V29ybGRNYW5hZ2VyfSBUaGUgd29ybGQgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0V29ybGRNYW5hZ2VyKCk6IFdvcmxkTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLndvcmxkTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBpdGVtIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0l0ZW1NYW5hZ2VyfSBUaGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJdGVtTWFuYWdlcigpOiBJdGVtTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jsb2NrTWFuYWdlcn0gVGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldEJsb2NrTWFuYWdlcigpOiBCbG9ja01hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5ibG9ja01hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbG9nZ2VyLlxuICAgICAqIEByZXR1cm5zIHtMb2dnZXJCdWlsZGVyfSBUaGUgbG9nZ2VyLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIC8vIE5vcm1hbCBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmluZm8oJ0hlbGxvLCB3b3JsZCEnKTtcbiAgICAgKiAvLyBEZWJ1ZyBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRXJyb3IgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihuZXcgRXJyb3IoJ0hlbGxvIFdvcmxkJykpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRMb2dnZXIoKTogTG9nZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9nZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBhY2tldCByZWdpc3RyeS5cbiAgICAgKiBAcmV0dXJucyB7UGFja2V0UmVnaXN0cnl9IFRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICovXG4gICAgcHVibGljIGdldFBhY2tldFJlZ2lzdHJ5KCk6IFBhY2tldFJlZ2lzdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFja2V0UmVnaXN0cnk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcmFrbmV0IGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZH0gVGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UmFrbmV0KCk6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNoYXQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q2hhdE1hbmFnZXJ9IFRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENoYXRNYW5hZ2VyKCk6IENoYXRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2hhdE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtDb25maWd9IFRoZSBjb25maWcuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSk7IC8vIDIwXG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldENvbmZpZygpOiBDb25maWcge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25maWc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uc29sZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7Q29uc29sZSB8IHVuZGVmaW5lZH0gVGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbnNvbGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnNvbGU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGVybWlzc2lvbiBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtQZXJtaXNzaW9uTWFuYWdlcn0gVGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UGVybWlzc2lvbk1hbmFnZXIoKTogUGVybWlzc2lvbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5wZXJtaXNzaW9uTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBiYW4gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7QmFuTWFuYWdlcn0gVGhlIGJhbiBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCYW5NYW5hZ2VyKCk6IEJhbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5iYW5NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhpcyBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUaWNrLlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBjdXJyZW50IFRpY2suXG4gICAgICovXG4gICAgcHVibGljIGdldFRpY2soKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlcih0aGlzLmN1cnJlbnRUaWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUUFMuXG4gICAgICovXG4gICAgcHVibGljIGdldFRQUygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gTnVtYmVyLnBhcnNlRmxvYXQodGhpcy50cHMudG9GaXhlZCgyKSk7XG4gICAgfVxufVxuIl0sIm5hbWVzIjpbIkV2ZW50RW1pdHRlciIsIlNlc3Npb25NYW5hZ2VyIiwidmVyc2lvbiIsIklkZW50aWZpZXJzIiwiUGFja2V0UmVnaXN0cnkiLCJJdGVtTWFuYWdlciIsIkJsb2NrTWFuYWdlciIsIldvcmxkTWFuYWdlciIsIkNvbnNvbGUiLCJDb21tYW5kTWFuYWdlciIsIlF1ZXJ5TWFuYWdlciIsIkNoYXRNYW5hZ2VyIiwiUGVybWlzc2lvbk1hbmFnZXIiLCJCYW5NYW5hZ2VyIiwiQmxvY2tNYXBwaW5ncyIsIlJha05ldExpc3RlbmVyIiwiU2VydmVyTmFtZSIsIlJha25ldENvbm5lY3RFdmVudCIsIlRpbWVyIiwiQ2xpZW50Q29ubmVjdGlvbiIsIlJha25ldERpc2Nvbm5lY3RFdmVudCIsIlJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50IiwiQmF0Y2hQYWNrZXQiLCJwYWNrZXQiLCJUaWNrRXZlbnQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0NBLE1BQXFCLGVBQWVBLGdDQUFhLENBQUE7QUFBQSxFQUNyQyxNQUFBO0FBQUEsRUFDUyxNQUFBO0FBQUEsRUFDQSxNQUFBO0FBQUEsRUFDQSxPQUFBO0FBQUEsRUFDQSxjQUFBO0FBQUEsRUFDQSxjQUFB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