@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
456 lines (455 loc) • 50.9 kB
JavaScript
"use strict";
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
require("./_virtual/_rolldown/runtime.cjs.cjs");
const require_chat_ChatManager = require("./chat/ChatManager.cjs.cjs");
const require_permission_PermissionManager = require("./permission/PermissionManager.cjs.cjs");
const require_query_QueryManager = require("./query/QueryManager.cjs.cjs");
const require_SessionManager = require("./SessionManager.cjs.cjs");
const require_ban_BanManager = require("./ban/BanManager.cjs.cjs");
const require_utils_Timer = require("./utils/Timer.cjs.cjs");
const require_block_BlockManager = require("./block/BlockManager.cjs.cjs");
const require_block_BlockMappings = require("./block/BlockMappings.cjs.cjs");
const require_item_ItemManager = require("./item/ItemManager.cjs.cjs");
const require_network_Identifiers = require("./network/Identifiers.cjs.cjs");
const require_network_packet_BatchPacket = require("./network/packet/BatchPacket.cjs.cjs");
const require_world_WorldManager = require("./world/WorldManager.cjs.cjs");
const require_command_CommandManager = require("./command/CommandManager.cjs.cjs");
const require_events_raknet_RaknetConnectEvent = require("./events/raknet/RaknetConnectEvent.cjs.cjs");
const require_events_raknet_RaknetDisconnectEvent = require("./events/raknet/RaknetDisconnectEvent.cjs.cjs");
const require_events_raknet_RaknetEncapsulatedPacketEvent = require("./events/raknet/RaknetEncapsulatedPacketEvent.cjs.cjs");
const require_events_other_TickEvent = require("./events/other/TickEvent.cjs.cjs");
const require_Console = require("./Console.cjs.cjs");
const require_events_EventEmitter = require("./events/EventEmitter.cjs.cjs");
const require_network_ClientConnection = require("./network/ClientConnection.cjs.cjs");
const require_network_PacketRegistry = require("./network/PacketRegistry.cjs.cjs");
const require_package = require("./package.json.cjs.cjs");
let _jsprismarine_raknet = require("@jsprismarine/raknet");
//#region src/Server.ts
/**
* JSPrismarine's main server class.
* @public
*/
var Server = class Server extends require_events_EventEmitter.EventEmitter {
raknet;
logger;
config;
console;
packetRegistry;
sessionManager = new require_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;
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${require_package.version}§r for Minecraft: Bedrock Edition ${require_network_Identifiers.default.MinecraftVersions.at(-1)} (protocol version §e${require_network_Identifiers.default.Protocol}§r)`);
this.logger = logger;
this.config = config;
this.packetRegistry = new require_network_PacketRegistry.default(this);
this.itemManager = new require_item_ItemManager.default(this);
this.blockManager = new require_block_BlockManager.default(this);
this.worldManager = new require_world_WorldManager.default(this);
if (!this.headless) this.console = new require_Console.default(this);
this.commandManager = new require_command_CommandManager.CommandManager(this);
this.queryManager = new require_query_QueryManager.QueryManager(this);
this.chatManager = new require_chat_ChatManager.ChatManager(this);
this.permissionManager = new require_permission_PermissionManager.PermissionManager(this);
this.banManager = new require_ban_BanManager.default(this);
}
/**
* Enables the server.
* @returns {Promise<void>} A promise that resolves when the server is enabled.
* @internal
*/
async enable() {
await require_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();
require_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 _jsprismarine_raknet.RakNetListener(this.getConfig().getMaxPlayers(), this.getConfig().getOnlineMode(), new _jsprismarine_raknet.ServerName(this), this.getLogger());
this.raknet.start(serverIp, port);
this.raknet.on("openConnection", async (session) => {
const event = new require_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 require_utils_Timer.default();
this.logger.debug(`${token} is attempting to connect`);
this.sessionManager.add(token, new require_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 require_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 require_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 require_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 packet = new (this.packetRegistry.getPackets().get(pid))(buf);
try {
packet.decode();
} catch (error) {
this.logger.error(error);
this.logger.error(`Error while decoding packet: ${packet.constructor.name}: ${error}`);
continue;
}
try {
const handler = this.packetRegistry.getHandler(pid);
this.logger.silly(`Received §b${packet.constructor.name}§r packet`);
await handler.handle(packet, this, connection.getPlayerSession() ?? connection);
} catch (error) {
this.logger.error(`Handler error ${packet.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 require_events_other_TickEvent.default(this.getTick());
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, Math.max(0, sleepTime));
this.tickerTimer.unref();
};
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 require_package.version;
}
/**
* Returns the identifiers.
* @returns {Identifiers} The identifiers.
*/
getIdentifiers() {
return require_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));
}
};
//#endregion
exports.default = Server;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VydmVyLmNqcy5janMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL1NlcnZlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBSYWtOZXRMaXN0ZW5lciwgU2VydmVyTmFtZSB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvcmFrbmV0JztcbmltcG9ydCBDb25zb2xlIGZyb20gJy4vQ29uc29sZSc7XG5pbXBvcnQgU2Vzc2lvbk1hbmFnZXIgZnJvbSAnLi9TZXNzaW9uTWFuYWdlcic7XG5pbXBvcnQgQmFuTWFuYWdlciBmcm9tICcuL2Jhbi9CYW5NYW5hZ2VyJztcbmltcG9ydCBCbG9ja01hbmFnZXIgZnJvbSAnLi9ibG9jay9CbG9ja01hbmFnZXInO1xuaW1wb3J0IHsgQmxvY2tNYXBwaW5ncyB9IGZyb20gJy4vYmxvY2svQmxvY2tNYXBwaW5ncyc7XG5pbXBvcnQgeyBDaGF0TWFuYWdlciB9IGZyb20gJy4vY2hhdC9DaGF0TWFuYWdlcic7XG5pbXBvcnQgeyBDb21tYW5kTWFuYWdlciB9IGZyb20gJy4vY29tbWFuZC9Db21tYW5kTWFuYWdlcic7XG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICcuL2V2ZW50cy9FdmVudEVtaXR0ZXInO1xuaW1wb3J0IHsgVGlja0V2ZW50IH0gZnJvbSAnLi9ldmVudHMvRXZlbnRzJztcbmltcG9ydCBSYWtuZXRDb25uZWN0RXZlbnQgZnJvbSAnLi9ldmVudHMvcmFrbmV0L1Jha25ldENvbm5lY3RFdmVudCc7XG5pbXBvcnQgUmFrbmV0RGlzY29ubmVjdEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXREaXNjb25uZWN0RXZlbnQnO1xuaW1wb3J0IFJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50IGZyb20gJy4vZXZlbnRzL3Jha25ldC9SYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXRFdmVudCc7XG5pbXBvcnQgSXRlbU1hbmFnZXIgZnJvbSAnLi9pdGVtL0l0ZW1NYW5hZ2VyJztcbmltcG9ydCBDbGllbnRDb25uZWN0aW9uIGZyb20gJy4vbmV0d29yay9DbGllbnRDb25uZWN0aW9uJztcbmltcG9ydCBJZGVudGlmaWVycyBmcm9tICcuL25ldHdvcmsvSWRlbnRpZmllcnMnO1xuaW1wb3J0IFBhY2tldFJlZ2lzdHJ5IGZyb20gJy4vbmV0d29yay9QYWNrZXRSZWdpc3RyeSc7XG5pbXBvcnQgdHlwZSB7IERhdGFQYWNrZXQgfSBmcm9tICcuL25ldHdvcmsvUGFja2V0cyc7XG5pbXBvcnQgQmF0Y2hQYWNrZXQgZnJvbSAnLi9uZXR3b3JrL3BhY2tldC9CYXRjaFBhY2tldCc7XG5pbXBvcnQgeyBQZXJtaXNzaW9uTWFuYWdlciB9IGZyb20gJy4vcGVybWlzc2lvbi9QZXJtaXNzaW9uTWFuYWdlcic7XG5pbXBvcnQgeyBRdWVyeU1hbmFnZXIgfSBmcm9tICcuL3F1ZXJ5L1F1ZXJ5TWFuYWdlcic7XG5pbXBvcnQgVGltZXIgZnJvbSAnLi91dGlscy9UaW1lcic7XG5pbXBvcnQgV29ybGRNYW5hZ2VyIGZyb20gJy4vd29ybGQvV29ybGRNYW5hZ2VyJztcblxuaW1wb3J0IHR5cGUgeyBJbmV0QWRkcmVzcywgUmFrTmV0U2Vzc2lvbiB9IGZyb20gJ0Bqc3ByaXNtYXJpbmUvcmFrbmV0JztcbmltcG9ydCB0eXBlIHsgQ29uZmlnIH0gZnJvbSAnLi9jb25maWcvQ29uZmlnJztcblxuaW1wb3J0IHR5cGUgeyBMb2dnZXIgfSBmcm9tICdAanNwcmlzbWFyaW5lL2xvZ2dlcic7XG5pbXBvcnQgeyB2ZXJzaW9uIH0gZnJvbSAnLi4vcGFja2FnZS5qc29uJyB3aXRoIHsgdHlwZTogJ2pzb24nIH07XG5cbi8qKlxuICogSlNQcmlzbWFyaW5lJ3MgbWFpbiBzZXJ2ZXIgY2xhc3MuXG4gKiBAcHVibGljXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFNlcnZlciBleHRlbmRzIEV2ZW50RW1pdHRlciB7XG4gICAgcHJpdmF0ZSByYWtuZXQ6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9nZ2VyOiBMb2dnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBjb25maWc6IENvbmZpZztcbiAgICBwcml2YXRlIHJlYWRvbmx5IGNvbnNvbGU6IENvbnNvbGUgfCB1bmRlZmluZWQ7XG4gICAgcHJpdmF0ZSByZWFkb25seSBwYWNrZXRSZWdpc3RyeTogUGFja2V0UmVnaXN0cnk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXNzaW9uTWFuYWdlciA9IG5ldyBTZXNzaW9uTWFuYWdlcigpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29tbWFuZE1hbmFnZXI6IENvbW1hbmRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgd29ybGRNYW5hZ2VyOiBXb3JsZE1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBpdGVtTWFuYWdlcjogSXRlbU1hbmFnZXI7XG4gICAgcHJpdmF0ZSByZWFkb25seSBibG9ja01hbmFnZXI6IEJsb2NrTWFuYWdlcjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHF1ZXJ5TWFuYWdlcjogUXVlcnlNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY2hhdE1hbmFnZXI6IENoYXRNYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgcGVybWlzc2lvbk1hbmFnZXI6IFBlcm1pc3Npb25NYW5hZ2VyO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgYmFuTWFuYWdlcjogQmFuTWFuYWdlcjtcblxuICAgIC8qKlxuICAgICAqIElmIHRoZSBzZXJ2ZXIgaXMgc3RvcHBpbmcuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBzdG9wcGluZyA9IGZhbHNlO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgdGlja2VyIHRpbWVyLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgdGlja2VyVGltZXI6IE5vZGVKUy5UaW1lb3V0IHwgdW5kZWZpbmVkO1xuXG4gICAgLyoqXG4gICAgICogVGhlIGN1cnJlbnQgVFBTLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgdHBzID0gMjA7XG5cbiAgICAvKipcbiAgICAgKiBUaGUgY3VycmVudCB0aWNrLlxuICAgICAqIEBpbnRlcm5hbFxuICAgICAqL1xuICAgIHByaXZhdGUgY3VycmVudFRpY2sgPSAwbjtcblxuICAgIC8qKlxuICAgICAqIElmIHRoZSBzZXJ2ZXIgaXMgaGVhZGxlc3MuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBoZWFkbGVzczogYm9vbGVhbjtcblxuICAgIC8vIFRPRE86IE1vdmUgdGhpcyBzb21ld2hlcmUgZWxzZS5cbiAgICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBNSU5FQ1JBRlRfVElDS19USU1FX01TID0gMTAwMCAvIDIwO1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhIG5ldyBzZXJ2ZXIgaW5zdGFuY2UuXG4gICAgICogQHBhcmFtIHtvYmplY3R9IG9wdGlvbnMgLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge0xvZ2dlckJ1aWxkZXJ9IG9wdGlvbnMubG9nZ2VyIC0gVGhlIGxvZ2dlci5cbiAgICAgKiBAcGFyYW0ge0NvbmZpZ30gb3B0aW9ucy5jb25maWcgLSBUaGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBzZXJ2ZXIgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKHsgbG9nZ2VyLCBjb25maWcsIGhlYWRsZXNzID0gZmFsc2UgfTogeyBsb2dnZXI6IExvZ2dlcjsgY29uZmlnOiBDb25maWc7IGhlYWRsZXNzPzogYm9vbGVhbiB9KSB7XG4gICAgICAgIHN1cGVyKCk7XG5cbiAgICAgICAgdGhpcy5oZWFkbGVzcyA9IGhlYWRsZXNzO1xuXG4gICAgICAgIGxvZ2dlci5pbmZvKFxuICAgICAgICAgICAgYFN0YXJ0aW5nIEpTUHJpc21hcmluZSBzZXJ2ZXIgdmVyc2lvbiDCp2V2JHt2ZXJzaW9ufcKnciBmb3IgTWluZWNyYWZ0OiBCZWRyb2NrIEVkaXRpb24gJHtJZGVudGlmaWVycy5NaW5lY3JhZnRWZXJzaW9ucy5hdCgtMSl9IChwcm90b2NvbCB2ZXJzaW9uIMKnZSR7SWRlbnRpZmllcnMuUHJvdG9jb2x9wqdyKWBcbiAgICAgICAgKTtcblxuICAgICAgICB0aGlzLmxvZ2dlciA9IGxvZ2dlcjtcbiAgICAgICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgICAgIHRoaXMucGFja2V0UmVnaXN0cnkgPSBuZXcgUGFja2V0UmVnaXN0cnkodGhpcyk7XG4gICAgICAgIHRoaXMuaXRlbU1hbmFnZXIgPSBuZXcgSXRlbU1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuYmxvY2tNYW5hZ2VyID0gbmV3IEJsb2NrTWFuYWdlcih0aGlzKTtcbiAgICAgICAgdGhpcy53b3JsZE1hbmFnZXIgPSBuZXcgV29ybGRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICBpZiAoIXRoaXMuaGVhZGxlc3MpIHRoaXMuY29uc29sZSA9IG5ldyBDb25zb2xlKHRoaXMpO1xuICAgICAgICB0aGlzLmNvbW1hbmRNYW5hZ2VyID0gbmV3IENvbW1hbmRNYW5hZ2VyKHRoaXMpO1xuICAgICAgICB0aGlzLnF1ZXJ5TWFuYWdlciA9IG5ldyBRdWVyeU1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuY2hhdE1hbmFnZXIgPSBuZXcgQ2hhdE1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMucGVybWlzc2lvbk1hbmFnZXIgPSBuZXcgUGVybWlzc2lvbk1hbmFnZXIodGhpcyk7XG4gICAgICAgIHRoaXMuYmFuTWFuYWdlciA9IG5ldyBCYW5NYW5hZ2VyKHRoaXMpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVuYWJsZXMgdGhlIHNlcnZlci5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGVuYWJsZWQuXG4gICAgICogQGludGVybmFsXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBlbmFibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IEJsb2NrTWFwcGluZ3MuaW5pdE1hcHBpbmdzKHRoaXMpO1xuXG4gICAgICAgIGF3YWl0IHRoaXMuY29uZmlnLmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbnNvbGU/LmVuYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmxvZ2dlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wZXJtaXNzaW9uTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5wYWNrZXRSZWdpc3RyeS5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pdGVtTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5ibG9ja01hbmFnZXIuZW5hYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmFuTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb21tYW5kTWFuYWdlci5lbmFibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy53b3JsZE1hbmFnZXIuZW5hYmxlKCk7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuc2V0Q29uc29sZSh0aGlzLmNvbnNvbGUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERpc2FibGVzIHRoZSBzZXJ2ZXIuXG4gICAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNlcnZlciBpcyBkaXNhYmxlZC5cbiAgICAgKiBAaW50ZXJuYWxcbiAgICAgKi9cbiAgICBwcml2YXRlIGFzeW5jIGRpc2FibGUoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMud29ybGRNYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb21tYW5kTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmFuTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuYmxvY2tNYW5hZ2VyLmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5pdGVtTWFuYWdlci5kaXNhYmxlKCk7XG4gICAgICAgIGF3YWl0IHRoaXMucGVybWlzc2lvbk1hbmFnZXIuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLnBhY2tldFJlZ2lzdHJ5LmRpc2FibGUoKTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25maWcuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmxvZ2dlci5kaXNhYmxlKCk7XG5cbiAgICAgICAgQmxvY2tNYXBwaW5ncy5yZXNldCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXRNZXRhZGF0YSgpIHtcbiAgICAgICAgaWYgKCF0aGlzLnJha25ldCkgdGhyb3cgbmV3IEVycm9yKCdTZXJ2ZXIgaXMgbm90IHN0YXJ0ZWQnKTtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0LnNlcnZlck5hbWU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVsb2FkcyB0aGUgc2VydmVyLlxuICAgICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBzZXJ2ZXIgaXMgcmVsb2FkZWQuXG4gICAgICogQHJlbWFya3MgVGhpcyBtZXRob2QgaXMgZXF1aXZhbGVudCB0byBjYWxsaW5nIHtAbGluayBTZXJ2ZXIjZGlzYWJsZX0gYW5kIHtAbGluayBTZXJ2ZXIjZW5hYmxlfS5cbiAgICAgKiBAcmVtYXJrcyBUaGlzIG1ldGhvZCBhbmQgZnVuY3Rpb25hbGl0eSBpcyB1bnN1cHBvcnRlZCBhbmQgc2hvdWxkIGlkZWFsbHkgYmUgY29tcGxldGVseSBhdm9pZGVkLlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyByZWxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuZGlzYWJsZSgpO1xuICAgICAgICBhd2FpdCB0aGlzLmVuYWJsZSgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0YXJ0cyB0aGUgc2VydmVyLlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBbc2VydmVySXA9JzAuMC4wLjAnXSAtIFRoZSBzZXJ2ZXIgSVAuXG4gICAgICogQHBhcmFtIHtudW1iZXJ9IFtwb3J0PTE5MTMyXSAtIFRoZSBzZXJ2ZXIgcG9ydC5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIHN0YXJ0ZWQuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGJvb3RzdHJhcChzZXJ2ZXJJcCA9ICcwLjAuMC4wJywgcG9ydCA9IDE5MTMyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGF3YWl0IHRoaXMuZW5hYmxlKCk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQgPSBuZXcgUmFrTmV0TGlzdGVuZXIoXG4gICAgICAgICAgICB0aGlzLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSxcbiAgICAgICAgICAgIHRoaXMuZ2V0Q29uZmlnKCkuZ2V0T25saW5lTW9kZSgpLFxuICAgICAgICAgICAgbmV3IFNlcnZlck5hbWUodGhpcyksXG4gICAgICAgICAgICB0aGlzLmdldExvZ2dlcigpXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMucmFrbmV0LnN0YXJ0KHNlcnZlcklwLCBwb3J0KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbignb3BlbkNvbm5lY3Rpb24nLCBhc3luYyAoc2Vzc2lvbjogUmFrTmV0U2Vzc2lvbikgPT4ge1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgUmFrbmV0Q29ubmVjdEV2ZW50KHNlc3Npb24pO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXRDb25uZWN0JywgZXZlbnQpO1xuXG4gICAgICAgICAgICBpZiAoZXZlbnQuaXNDYW5jZWxsZWQoKSkge1xuICAgICAgICAgICAgICAgIHNlc3Npb24uZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSBzZXNzaW9uLmdldEFkZHJlc3MoKS50b1Rva2VuKCk7XG4gICAgICAgICAgICBpZiAodGhpcy5zZXNzaW9uTWFuYWdlci5oYXModG9rZW4pKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoYEFub3RoZXIgY2xpZW50IHdpdGggdG9rZW4gKCR7dG9rZW59KSBpcyBhbHJlYWR5IGNvbm5lY3RlZCFgKTtcbiAgICAgICAgICAgICAgICBzZXNzaW9uLmRpc2Nvbm5lY3QoJ0FscmVhZHkgY29ubmVjdGVkIGZyb20gYW5vdGhlciBsb2NhdGlvbicpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdGltZXIgPSBuZXcgVGltZXIoKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3Rva2VufSBpcyBhdHRlbXB0aW5nIHRvIGNvbm5lY3RgKTtcbiAgICAgICAgICAgIHRoaXMuc2Vzc2lvbk1hbmFnZXIuYWRkKHRva2VuLCBuZXcgQ2xpZW50Q29ubmVjdGlvbihzZXNzaW9uLCB0aGlzLmxvZ2dlcikpO1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgTmV3IGNvbm5lY3Rpb24gaGFuZGxpbmcgdG9vayDCp2Uke3RpbWVyLnN0b3AoKX0gbXPCp3JgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ2Nsb3NlQ29ubmVjdGlvbicsIGFzeW5jIChpbmV0QWRkcjogSW5ldEFkZHJlc3MsIHJlYXNvbjogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBSYWtuZXREaXNjb25uZWN0RXZlbnQoaW5ldEFkZHIsIHJlYXNvbik7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmVtaXQoJ3Jha25ldERpc2Nvbm5lY3QnLCBldmVudCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgY29uc3QgdG9rZW4gPSBpbmV0QWRkci50b1Rva2VuKCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHNlc3Npb24gPSB0aGlzLnNlc3Npb25NYW5hZ2VyLmdldCh0b2tlbik7XG4gICAgICAgICAgICBpZiAoIXNlc3Npb24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgQ2Fubm90IHJlbW92ZSBjb25uZWN0aW9uIGZyb20gbm9uLWV4aXN0aW5nIHBsYXllciAoJHt0b2tlbn0pYCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBhd2FpdCBzZXNzaW9uLmNsb3NlUGxheWVyU2Vzc2lvbigpO1xuXG4gICAgICAgICAgICB0aGlzLnNlc3Npb25NYW5hZ2VyLnJlbW92ZSh0b2tlbik7XG4gICAgICAgICAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0b2tlbn0gZGlzY29ubmVjdGVkIGR1ZSB0byAke3JlYXNvbn1gKTtcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKGBQbGF5ZXIgZGVzdHJ1Y3Rpb24gdG9vayBhYm91dCAke0RhdGUubm93KCkgLSB0aW1lfSBtc2ApO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnJha25ldC5vbignZW5jYXBzdWxhdGVkJywgYXN5bmMgKHBhY2tldDogYW55LCBpbmV0QWRkcjogSW5ldEFkZHJlc3MpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gbmV3IFJha25ldEVuY2Fwc3VsYXRlZFBhY2tldEV2ZW50KGluZXRBZGRyLCBwYWNrZXQpO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5lbWl0KCdyYWtuZXRFbmNhcHN1bGF0ZWRQYWNrZXQnLCBldmVudCk7XG5cbiAgICAgICAgICAgIGxldCBjb25uZWN0aW9uOiBDbGllbnRDb25uZWN0aW9uIHwgbnVsbDtcbiAgICAgICAgICAgIGlmICgoY29ubmVjdGlvbiA9IHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0KGluZXRBZGRyLnRvVG9rZW4oKSkgPz8gbnVsbCkgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgR290IGEgcGFja2V0IGZyb20gYSBjbG9zZWQgY29ubmVjdGlvbiAoJHtpbmV0QWRkci50b1Rva2VuKCl9KWApO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAvLyBSZWFkIGJhdGNoIGNvbnRlbnQgYW5kIGhhbmRsZSB0aGVtXG4gICAgICAgICAgICAgICAgY29uc3QgYmF0Y2hlZCA9IG5ldyBCYXRjaFBhY2tldChwYWNrZXQuY29udGVudCk7XG4gICAgICAgICAgICAgICAgYmF0Y2hlZC5jb21wcmVzc2VkID0gY29ubmVjdGlvbi5oYXNDb21wcmVzc2lvbjtcblxuICAgICAgICAgICAgICAgIC8vIFJlYWQgYWxsIHBhY2tldHMgaW5zaWRlIGJhdGNoIGFuZCBoYW5kbGUgdGhlbVxuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgYnVmIG9mIGF3YWl0IGJhdGNoZWQuYXN5bmNEZWNvZGUoKSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBwaWQgPSBidWZbMF0hO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICghdGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRQYWNrZXRzKCkuaGFzKHBpZCkpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLndhcm4oYFBhY2tldCAweCR7cGlkLnRvU3RyaW5nKDE2KX0gaXNuJ3QgaW1wbGVtZW50ZWRgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgLy8gR2V0IHBhY2tldCBmcm9tIHJlZ2lzdHJ5XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyAodGhpcy5wYWNrZXRSZWdpc3RyeS5nZXRQYWNrZXRzKCkuZ2V0KHBpZCkhKShidWYpO1xuXG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBwYWNrZXQuZGVjb2RlKCk7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgRXJyb3Igd2hpbGUgZGVjb2RpbmcgcGFja2V0OiAke3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfTogJHtlcnJvcn1gKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLnBhY2tldFJlZ2lzdHJ5LmdldEhhbmRsZXIocGlkKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLnNpbGx5KGBSZWNlaXZlZCDCp2Ike3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfcKnciBwYWNrZXRgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IChoYW5kbGVyIGFzIGFueSkuaGFuZGxlKHBhY2tldCwgdGhpcywgY29ubmVjdGlvbi5nZXRQbGF5ZXJTZXNzaW9uKCkgPz8gY29ubmVjdGlvbik7XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihgSGFuZGxlciBlcnJvciAke3BhY2tldC5jb25zdHJ1Y3Rvci5uYW1lfS1oYW5kbGVyOiAoJHtlcnJvcn0pYCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmxvZ2dlci5lcnJvcihlcnJvcik7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgICAgICAgICAgIHRoaXMubG9nZ2VyLmVycm9yKGVycm9yKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5yYWtuZXQub24oJ3JhdycsIGFzeW5jIChidWZmZXI6IEJ1ZmZlciwgaW5ldEFkZHI6IEluZXRBZGRyZXNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuY29uZmlnLmdldEVuYWJsZVF1ZXJ5KCkpIHJldHVybjtcblxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnF1ZXJ5TWFuYWdlci5vblJhdyhidWZmZXIsIGluZXRBZGRyKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIudmVyYm9zZShgUXVlcnlNYW5hZ2VyIGVuY291bnRlcmVkIGFuIGVycm9yYCk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXIuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBpZiAodGhpcy5jb25maWcuZ2V0RW5hYmxlVGlja2luZygpKSB7XG4gICAgICAgICAgICBsZXQgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgIGxldCB0cHNTdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgICAgICAgICAgbGV0IGxhc3RUaWNrVGltZSA9IERhdGUubm93KCk7XG4gICAgICAgICAgICBsZXQgdHBzU3RhcnRUaWNrID0gdGhpcy5nZXRUaWNrKCk7XG4gICAgICAgICAgICBjb25zdCB0aWNrID0gYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICh0aGlzLnN0b3BwaW5nKSByZXR1cm47XG5cbiAgICAgICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBUaWNrRXZlbnQodGhpcy5nZXRUaWNrKCkpO1xuICAgICAgICAgICAgICAgIHZvaWQgdGhpcy5lbWl0KCd0aWNrJywgZXZlbnQpO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgdGlja3NQZXJTZWNvbmQgPSAxMDAwIC8gU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVM7XG5cbiAgICAgICAgICAgICAgICAvLyBVcGRhdGUgYWxsIHdvcmxkcy5cbiAgICAgICAgICAgICAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLndvcmxkTWFuYWdlci5nZXRXb3JsZHMoKS5tYXAoKHdvcmxkKSA9PiB3b3JsZC51cGRhdGUoZXZlbnQuZ2V0VGljaygpKSkpO1xuXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuY29uZmlnLmdldEVuYWJsZVByb2Nlc3NUaXRsZSgpICYmIHRoaXMuZ2V0VGljaygpICUgdGlja3NQZXJTZWNvbmQgPT09IDAgJiYgIXRoaXMuaGVhZGxlc3MpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gVXBkYXRlIHRoZSBwcm9jZXNzIHRpdGxlIHdpdGggVFBTIGFuZCB0aWNrLlxuICAgICAgICAgICAgICAgICAgICBwcm9jZXNzLnRpdGxlID0gYFRQUzogJHt0aGlzLmdldFRQUygpLnRvRml4ZWQoMil9IHwgVGljazogJHt0aGlzLmdldFRpY2soKX0gfCAke3Byb2Nlc3MudGl0bGUuc3BsaXQoJ3wgJykuYXQoLTEpIX1gO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudFRpY2srKztcbiAgICAgICAgICAgICAgICBjb25zdCBlbmRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICAgICAgICAgICAgICBjb25zdCBlbGFwc2VkVGltZSA9IGVuZFRpbWUgLSBzdGFydFRpbWU7XG4gICAgICAgICAgICAgICAgY29uc3QgZXhwZWN0ZWRFbGFwc2VkVGltZSA9IHRoaXMuZ2V0VGljaygpICogU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVM7XG4gICAgICAgICAgICAgICAgY29uc3QgZXhlY3V0aW9uVGltZSA9IGVuZFRpbWUgLSBsYXN0VGlja1RpbWU7XG5cbiAgICAgICAgICAgICAgICAvLyBBZGp1c3Qgc2xlZXBUaW1lIGJhc2VkIG9uIGV4ZWN1dGlvbiBzcGVlZC5cbiAgICAgICAgICAgICAgICBsZXQgc2xlZXBUaW1lID0gU2VydmVyLk1JTkVDUkFGVF9USUNLX1RJTUVfTVMgLSBleGVjdXRpb25UaW1lO1xuICAgICAgICAgICAgICAgIGlmIChlbGFwc2VkVGltZSA8IGV4cGVjdGVkRWxhcHNlZFRpbWUpIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgd2UncmUgcnVubmluZyBmYXN0ZXIgdGhhbiBleHBlY3RlZCwgaW5jcmVhc2Ugc2xlZXBUaW1lLlxuICAgICAgICAgICAgICAgICAgICBzbGVlcFRpbWUgKz0gZXhwZWN0ZWRFbGFwc2VkVGltZSAtIGVsYXBzZWRUaW1lO1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoZWxhcHNlZFRpbWUgPiBleHBlY3RlZEVsYXBzZWRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIElmIHdlJ3JlIHJ1bm5pbmcgc2xvd2VyIHRoYW4gZXhwZWN0ZWQsIGRlY3JlYXNlIHNsZWVwVGltZSBidXQgZG9uJ3QgbGV0IGl0IGdvIGJlbG93IDAuXG4gICAgICAgICAgICAgICAgICAgIHNsZWVwVGltZSA9IE1hdGgubWF4KDAsIHNsZWVwVGltZSAtIChlbGFwc2VkVGltZSAtIGV4cGVjdGVkRWxhcHNlZFRpbWUpKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAvLyBDYWxjdWxhdGUgdHBzIGJhc2VkIG9uIHRoZSBhY3R1YWwgZWxhcHNlZCB0aW1lIHNpbmNlIHRoZSBzdGFydCBvZiB0aGUgdGljay5cbiAgICAgICAgICAgICAgICBpZiAodHBzU3RhcnRUaW1lICE9PSBlbmRUaW1lKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudHBzID0gKCh0aGlzLmdldFRpY2soKSAtIHRwc1N0YXJ0VGljaykgKiAxMDAwKSAvIChlbmRUaW1lIC0gdHBzU3RhcnRUaW1lKTtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAoZW5kVGltZSAtIHRwc1N0YXJ0VGltZSA+PSAxMDAwKSB7XG4gICAgICAgICAgICAgICAgICAgIHRwc1N0YXJ0VGljayA9IHRoaXMuZ2V0VGljaygpO1xuICAgICAgICAgICAgICAgICAgICB0cHNTdGFydFRpbWUgPSBlbmRUaW1lO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHRoaXMudHBzID0gTWF0aC5taW4odGhpcy50cHMsIDIwKTsgLy8gRW5zdXJlIHRwcyBkb2VzIG5vdCBleGNlZWQgMjBcblxuICAgICAgICAgICAgICAgIGxhc3RUaWNrVGltZSA9IGVuZFRpbWU7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lciA9IHNldFRpbWVvdXQodGljaywgTWF0aC5tYXgoMCwgc2xlZXBUaW1lKSk7XG4gICAgICAgICAgICAgICAgdGhpcy50aWNrZXJUaW1lci51bnJlZigpO1xuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgLy8gU3RhcnQgdGlja2luZ1xuICAgICAgICAgICAgdm9pZCB0aWNrKCk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmxvZ2dlci5pbmZvKGBKU1ByaXNtYXJpbmUgaXMgbm93IGxpc3RlbmluZyBvbiBwb3J0IMKnYiR7cG9ydH1gKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBLaWxscyB0aGUgc2VydmVyIGFzeW5jaHJvbm91c2x5LlxuICAgICAqIEBwYXJhbSB7b2JqZWN0fSBbb3B0aW9uc10gLSBUaGUgb3B0aW9ucy5cbiAgICAgKiBAcGFyYW0ge2Jvb2xlYW59IFtvcHRpb25zLmNyYXNoXSAtIElmIHRoZSBzZXJ2ZXIgc2hvdWxkIGNyYXNoLlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gW29wdGlvbnMuc3RheUFsaXZlXSAtIElmIHdlIHNob3VsZCBsZXQgdGhlIHByb2Nlc3Mgc3RheSBhbGl2ZS5cbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgc2VydmVyIGlzIGtpbGxlZC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2h1dGRvd24ob3B0aW9ucz86IHsgY3Jhc2g/OiBib29sZWFuOyBzdGF5QWxpdmU/OiBib29sZWFuIH0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuc3RvcHBpbmcpIHJldHVybjtcbiAgICAgICAgdGhpcy5zdG9wcGluZyA9IHRydWU7XG5cbiAgICAgICAgdGhpcy5sb2dnZXIuaW5mbygnU3RvcHBpbmcgc2VydmVyJywgJ1NlcnZlci9raWxsJyk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29uc29sZT8uZGlzYWJsZSgpO1xuXG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy50aWNrZXJUaW1lcik7XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEtpY2sgYWxsIG9ubGluZSBwbGF5ZXJzLlxuICAgICAgICAgICAgYXdhaXQgdGhpcy5zZXNzaW9uTWFuYWdlci5raWNrQWxsUGxheWVycygnU2VydmVyIGNsb3NlZC4nKTtcblxuICAgICAgICAgICAgLy8gRGlzYWJsZSBhbGwgbWFuYWdlcnMuXG4gICAgICAgICAgICBhd2FpdCB0aGlzLmRpc2FibGUoKTtcblxuICAgICAgICAgICAgLy8gYHRoaXMucmFrbmV0YCBtaWdodCBiZSB1bmRlZmluZWQgaWYgd2Uga2lsbCB0aGUgc2VydmVyIHJlYWxseSBlYXJseS5cbiAgICAgICAgICAgIHRoaXMucmFrbmV0Py5raWxsKCk7XG5cbiAgICAgICAgICAgIC8vIEZpbmFsbHksIHJlbW92ZSBhbGwgbGlzdGVuZXJzLlxuICAgICAgICAgICAgdGhpcy5yZW1vdmVBbGxMaXN0ZW5lcnMoKTtcblxuICAgICAgICAgICAgLy8gTG9nZ2VyIGlzIG5vIGxvbmdlciBhdmFpbGFibGUuXG4gICAgICAgICAgICBjb25zb2xlLmRlYnVnKCdTZXJ2ZXIgc3RvcHBlZCwgR29vZGJ5ZSFcXG4nKTtcblxuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdChvcHRpb25zPy5jcmFzaCA/IDEgOiAwKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgICAgICAgIGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuICAgICAgICAgICAgaWYgKCFvcHRpb25zPy5zdGF5QWxpdmUpIHByb2Nlc3MuZXhpdCgxKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBicm9hZGNhc3RQYWNrZXQ8VCBleHRlbmRzIERhdGFQYWNrZXQ+KGRhdGFQYWNrZXQ6IFQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgLy8gTWF5YmUgaSBjYW4gaW1wcm92ZSB0aGlzIGJ5IHVzaW5nIHRoZSBVRFAgYnJvYWRjYXN0LCBhbGwgdW5jb25uZWN0ZWQgY2xpZW50c1xuICAgICAgICAvLyB3aWxsIGlnbm9yZSB0aGUgY29ubmVjdGVkIHBhY2tldCBwcm9iYWJseSwgYnV0IG1heSBjYXVzZSBpc3N1ZXMuXG4gICAgICAgIGZvciAoY29uc3Qgb25saW5lUGxheWVyIG9mIHRoaXMuc2Vzc2lvbk1hbmFnZXIuZ2V0QWxsUGxheWVycygpKSB7XG4gICAgICAgICAgICBhd2FpdCBvbmxpbmVQbGF5ZXIuZ2V0TmV0d29ya1Nlc3Npb24oKS5nZXRDb25uZWN0aW9uKCkuc2VuZERhdGFQYWNrZXQoZGF0YVBhY2tldCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBzZXJ2ZXIgdmVyc2lvbi5cbiAgICAgKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc2VydmVyIHZlcnNpb24uXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldFZlcnNpb24oKSk7XG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldFZlcnNpb24oKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHZlcnNpb247XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgaWRlbnRpZmllcnMuXG4gICAgICogQHJldHVybnMge0lkZW50aWZpZXJzfSBUaGUgaWRlbnRpZmllcnMuXG4gICAgICovXG4gICAgcHVibGljIGdldElkZW50aWZpZXJzKCk6IHR5cGVvZiBJZGVudGlmaWVycyB7XG4gICAgICAgIHJldHVybiBJZGVudGlmaWVycztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtRdWVyeU1hbmFnZXJ9IFRoZSBxdWVyeSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRRdWVyeU1hbmFnZXIoKTogUXVlcnlNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucXVlcnlNYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNvbW1hbmQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q29tbWFuZE1hbmFnZXJ9IFRoZSBjb21tYW5kIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbW1hbmRNYW5hZ2VyKCk6IENvbW1hbmRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29tbWFuZE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge1Nlc3Npb25NYW5hZ2VyfSBUaGUgcGxheWVyIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldFNlc3Npb25NYW5hZ2VyKCk6IFNlc3Npb25NYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc2Vzc2lvbk1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgd29ybGQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7V29ybGRNYW5hZ2VyfSBUaGUgd29ybGQgbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0V29ybGRNYW5hZ2VyKCk6IFdvcmxkTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLndvcmxkTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBpdGVtIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0l0ZW1NYW5hZ2VyfSBUaGUgaXRlbSBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRJdGVtTWFuYWdlcigpOiBJdGVtTWFuYWdlciB7XG4gICAgICAgIHJldHVybiB0aGlzLml0ZW1NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICogQHJldHVybnMge0Jsb2NrTWFuYWdlcn0gVGhlIGJsb2NrIG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldEJsb2NrTWFuYWdlcigpOiBCbG9ja01hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5ibG9ja01hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgbG9nZ2VyLlxuICAgICAqIEByZXR1cm5zIHtMb2dnZXJCdWlsZGVyfSBUaGUgbG9nZ2VyLlxuICAgICAqIEBleGFtcGxlXG4gICAgICogYGBgdHlwZXNjcmlwdFxuICAgICAqIC8vIE5vcm1hbCBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmluZm8oJ0hlbGxvLCB3b3JsZCEnKTtcbiAgICAgKiAvLyBEZWJ1ZyBsb2c6XG4gICAgICogc2VydmVyLmdldExvZ2dlcigpLmRlYnVnKCdIZWxsbywgd29ybGQhJyk7XG4gICAgICogLy8gRXJyb3IgbG9nOlxuICAgICAqIHNlcnZlci5nZXRMb2dnZXIoKS5lcnJvcihuZXcgRXJyb3IoJ0hlbGxvIFdvcmxkJykpO1xuICAgICAqIGBgYFxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRMb2dnZXIoKTogTG9nZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubG9nZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIHBhY2tldCByZWdpc3RyeS5cbiAgICAgKiBAcmV0dXJucyB7UGFja2V0UmVnaXN0cnl9IFRoZSBwYWNrZXQgcmVnaXN0cnkuXG4gICAgICovXG4gICAgcHVibGljIGdldFBhY2tldFJlZ2lzdHJ5KCk6IFBhY2tldFJlZ2lzdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGFja2V0UmVnaXN0cnk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcmFrbmV0IGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtSYWtOZXRMaXN0ZW5lciB8IHVuZGVmaW5lZH0gVGhlIHJha25ldCBpbnN0YW5jZS5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UmFrbmV0KCk6IFJha05ldExpc3RlbmVyIHwgdW5kZWZpbmVkIHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmFrbmV0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhlIGNoYXQgbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7Q2hhdE1hbmFnZXJ9IFRoZSBjaGF0IG1hbmFnZXIuXG4gICAgICovXG4gICAgcHVibGljIGdldENoYXRNYW5hZ2VyKCk6IENoYXRNYW5hZ2VyIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY2hhdE1hbmFnZXI7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uZmlnLlxuICAgICAqIEByZXR1cm5zIHtDb25maWd9IFRoZSBjb25maWcuXG4gICAgICogQGV4YW1wbGVcbiAgICAgKiBgYGB0eXBlc2NyaXB0XG4gICAgICogY29uc29sZS5sb2coc2VydmVyLmdldENvbmZpZygpLmdldE1heFBsYXllcnMoKSk7IC8vIDIwXG4gICAgICogYGBgXG4gICAgICovXG4gICAgcHVibGljIGdldENvbmZpZygpOiBDb25maWcge1xuICAgICAgICByZXR1cm4gdGhpcy5jb25maWc7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY29uc29sZSBpbnN0YW5jZS5cbiAgICAgKiBAcmV0dXJucyB7Q29uc29sZSB8IHVuZGVmaW5lZH0gVGhlIGNvbnNvbGUgaW5zdGFuY2UuXG4gICAgICovXG4gICAgcHVibGljIGdldENvbnNvbGUoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnNvbGU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgcGVybWlzc2lvbiBtYW5hZ2VyLlxuICAgICAqIEByZXR1cm5zIHtQZXJtaXNzaW9uTWFuYWdlcn0gVGhlIHBlcm1pc3Npb24gbWFuYWdlci5cbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0UGVybWlzc2lvbk1hbmFnZXIoKTogUGVybWlzc2lvbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5wZXJtaXNzaW9uTWFuYWdlcjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBiYW4gbWFuYWdlci5cbiAgICAgKiBAcmV0dXJucyB7QmFuTWFuYWdlcn0gVGhlIGJhbiBtYW5hZ2VyLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRCYW5NYW5hZ2VyKCk6IEJhbk1hbmFnZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5iYW5NYW5hZ2VyO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJldHVybnMgdGhpcyBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqIEByZXR1cm5zIHtTZXJ2ZXJ9IFRoZSBQcmlzbWFyaW5lIGluc3RhbmNlLlxuICAgICAqL1xuICAgIHB1YmxpYyBnZXRTZXJ2ZXIoKTogU2VydmVyIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmV0dXJucyB0aGUgY3VycmVudCBUaWNrLlxuICAgICAqIEByZXR1cm5zIHtudW1iZXJ9IFRoZSBjdXJyZW50IFRpY2suXG4gICAgICovXG4gICAgcHVibGljIGdldFRpY2soKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIE51bWJlcih0aGlzLmN1cnJlbnRUaWNrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXR1cm5zIHRoZSBjdXJyZW50IFRQUy5cbiAgICAgKiBAcmV0dXJucyB7bnVtYmVyfSBUaGUgY3VycmVudCBUUFMuXG4gICAgICovXG4gICAgcHVibGljIGdldFRQUygpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gTnVtYmVyLnBhcnNlRmxvYXQodGhpcy50cHMudG9GaXhlZCgyKSk7XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBa0NBLElBQXFCLFNBQXJCLE1BQXFCLGVBQWUsNEJBQUEsYUFBYTtDQUM3QztDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBQ0EsaUJBQWtDLElBQUksdUJBQUEsUUFBZTtDQUNyRDtDQUNBO0NBQ0E7Q0FDQTtDQUNBO0NBQ0E7Q0FDQTtDQUNBOzs7OztDQU1BLFdBQW1COzs7OztDQU1uQjs7Ozs7Q0FNQSxNQUFjOzs7OztDQU1kLGNBQXNCOzs7OztDQU10QjtDQUdBLE9BQXdCLHlCQUF5QixNQUFPOzs7Ozs7OztDQVN4RCxZQUFtQixFQUFFLFFBQVEsUUFBUSxXQUFXLFNBQWlFO0VBQzdHLE1BQU07RUFFTixLQUFLLFdBQVc7RUFFaEIsT0FBTyxLQUNILDJDQUEyQyxnQkFBQSxRQUFRLG9DQUFvQyw0QkFBQSxRQUFZLGtCQUFrQixHQUFHLEVBQUUsRUFBRSx1QkFBdUIsNEJBQUEsUUFBWSxTQUFTLElBQzVLO0VBRUEsS0FBSyxTQUFTO0VBQ2QsS0FBSyxTQUFTO0VBQ2QsS0FBSyxpQkFBaUIsSUFBSSwrQkFBQSxRQUFlLElBQUk7RUFDN0MsS0FBSyxjQUFjLElBQUkseUJBQUEs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