UNPKG

hivessh

Version:

HiveSsh is an innovative library designed to streamline SSH2 connections and simplify task execution on Linux servers.

218 lines 28.3 kB
import { ExecSession } from "./ExecSession.js"; import { handleHops } from "./HostHop.js"; import { execSshChannel } from "./SshExec.js"; import { loadSettings } from "./SshHostOptions.js"; import { tunnelOut } from "./SshTunnel.js"; import { getApm } from "./apm/apm.js"; import { fetchOsRelease } from "./essentials/OsRelease.js"; import { createSFTPPromiseWrapper } from "./essentials/SftpPromiseWrapper.js"; import { trimAll } from "./utils/base.js"; export class SshHost { settings; closeErr; connected = false; ssh = undefined; sftp = undefined; release = undefined; /** * * @description The only public function to create a SshHost object. * @param options Connection options for the ssh host * @returns A promise that resolves when the connection is established and the promise provides you an SshHost object */ static async connect(options) { const host = new SshHost(await loadSettings(options)); await host.connect(); return host; } constructor(settings) { this.settings = settings; } async connect() { this.ssh = await handleHops(this.settings); this.connected = true; this.ssh.on("close", () => { this.emergencyClose(new Error("Ssh2 client closed!")); }); this.ssh.on("end", () => { this.emergencyClose(new Error("Ssh2 client end!")); }); this.ssh.on("timeout", () => { this.emergencyClose(new Error("Ssh2 client connection timeout!")); }); this.ssh.on("error", (err) => { this.emergencyClose(err); }); const sftpWrapper = await new Promise((res, rej) => this.ssh.sftp((err, sftpClient) => err ? rej(err) : res(sftpClient))); this.sftp = createSFTPPromiseWrapper(sftpWrapper); } emergencyClose(err) { if (typeof err == "string") { err = new Error(err); } this.connected = false; if (!this.closeErr) { this.closeErr = err; } this.ssh.destroy(); } /** * @description If the SshHost socket is closed because of an error this method throws that error. * @throws Ssh socket close reason */ throwCloseError() { if (!this.connected) { if (this.closeErr) { throw this.closeErr; } throw new Error("SshHost is not connected!"); } } /** * @deprecated Use disconnect() * @description Closes the ssh socket if still connected */ close() { if (this.connected) { this.ssh.end(); } this.connected = false; } /** * @description Closes the ssh socket if still connected */ disconnect() { if (this.connected) { this.ssh.end(); } this.connected = false; } /** * * @param cmd Command string to execute * @param options Command channel execution options * @description Opens a ssh channel to handle a command execution by yourself * @returns A promise that resolves when the command begins execution and the promise provides you with the SshChannel to process the command output */ execChannel(cmd, options) { this.throwCloseError(); return execSshChannel(this.ssh, cmd, options); } /** * * @param cmd Command string to execute * @param options Command execution options * @description Opens an SSH channel to execute the command and resolves the promise as soon as the exit code is available for the executed command * @returns A promise that resolves when the command begins execution and the promise provides you the process some runtime, stdout, stderr and some exit data */ async exec(cmd, options) { this.throwCloseError(); try { const channel = await execSshChannel(this.ssh, cmd, options); return await channel.toPromise(options); } catch (err) { if (err instanceof Error && err.message == "Ssh channel closed, check why the socket was closed or lost connection") { this.throwCloseError(); } throw err; } } /** * * @description Opens a ssh shell channel to handle the shell by yourself * @returns A promise that resolves when the shell is started and the promise provides you with a session channel */ shellChannel() { return new Promise((res, rej) => this.ssh.shell((err, channel) => err ? rej(err) : res(channel))); } /** * * @param pwd Inital path of the session * @param sudo Defines if commands should be prefied as sudo * @param timeoutMillis Default command timeout milliseconds * @returns The new created ExecSession object */ session(pwd, sudo, timeoutMillis) { const session = new ExecSession(this, { PWD: pwd, }); session.sudo = sudo; session.timeoutMillis = timeoutMillis; return session; } async homeDir() { const result = await this.exec("pwd"); return trimAll(result.out); } /** * * @param cmd Command to check if it cmdExists * @description Function that checks if a spisific command exists on the remote ssh host * @returns True if command exists and false if not */ async cmdExists(cmd) { this.throwCloseError(); if (cmd.includes(" ")) { throw new Error("Command cant contain a space: '" + cmd + "'"); } const exit = await this.exec("command -v " + cmd, { expectedExitCode: [0, 1] }); return exit.code == 0; } cachedOsRelease; /** * * @description Resolves all releases files from '/etc/*-release' on the remove ssh host and maps them. Also tries to detect the distro name and version. * @param useCache The return value of this function get cached for the next function call. Set this to false to disable the caching. * @returns Returns an object that maps all values of all '/etc/*-release' files. */ fetchOsRelease(useCache = true) { this.throwCloseError(); if (useCache && this.cachedOsRelease) { return this.cachedOsRelease; } return fetchOsRelease(this).then((release) => { return this.cachedOsRelease = release; }); } cachedApm; /** * @deprecated Check your self * @description Checks one of the predefined package mangers is installed (apt, dnf, yum or a custom one) and returns a abstract interface for that package manager. * @param useCache The return value of this function get cached for the next function call. Set this to false to disable the caching. * @throws Throws an error if the package manager cant be detected. * @returns The abstract interface of the hosts package manager (if found). */ getApm(useCache = true) { this.throwCloseError(); if (useCache && this.cachedApm) { return this.cachedApm; } return getApm(this).then((apm) => { return this.cachedApm = apm; }); } /** * @experimental This function is experimental and may not be stable. * Creates a local server that tunnels incoming connections to a remote linux socket or host and port bind. * * You need to close the server to stop tunneling! * * This function creates a server that listens for incoming connections and forwards them to the remote SSH host. * * @param tunnelOptions - Options specifying remote linux socket or host and port details. * * @returns A promise that resolves to the created server that need to be closed. */ tunnelOut(tunnelOptions) { return tunnelOut(this.ssh, tunnelOptions); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3NoSG9zdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Tc2hIb3N0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQTtBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQ3pDLE9BQU8sRUFBaUUsY0FBYyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBQzVHLE9BQU8sRUFBbUMsWUFBWSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDbkYsT0FBTyxFQUF1QixTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQTtBQUMvRCxPQUFPLEVBQTBCLE1BQU0sRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUM3RCxPQUFPLEVBQWEsY0FBYyxFQUFFLE1BQU0sMkJBQTJCLENBQUE7QUFDckUsT0FBTyxFQUFzQix3QkFBd0IsRUFBRSxNQUFNLG9DQUFvQyxDQUFBO0FBQ2pHLE9BQU8sRUFBYSxPQUFPLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUVwRCxNQUFNLE9BQU8sT0FBTztJQXdCTDtJQXZCWCxRQUFRLENBQWlCO0lBQ3pCLFNBQVMsR0FBWSxLQUFLLENBQUE7SUFDMUIsR0FBRyxHQUFjLFNBQWdCLENBQUE7SUFDakMsSUFBSSxHQUF1QixTQUFnQixDQUFBO0lBQzNDLE9BQU8sR0FBYyxTQUFnQixDQUFBO0lBRXJDOzs7OztPQUtHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQ2hCLE9BQXVCO1FBRXZCLE1BQU0sSUFBSSxHQUFHLElBQUksT0FBTyxDQUNwQixNQUFNLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FDOUIsQ0FBQTtRQUNELE1BQU0sSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFBO1FBQ3BCLE9BQU8sSUFBSSxDQUFBO0lBQ2YsQ0FBQztJQUVELFlBQ1csUUFBeUI7UUFBekIsYUFBUSxHQUFSLFFBQVEsQ0FBaUI7SUFDaEMsQ0FBQztJQUVHLEtB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QyxlQUFlLEdBQUcsT0FBTyxDQUFBO1FBQ3pDLENBQUMsQ0FBQyxDQUFBO0lBQ04sQ0FBQztJQUlELFNBQVMsQ0FBb0M7SUFFN0M7Ozs7OztPQU1HO0lBQ0gsTUFBTSxDQUNGLFdBQW9CLElBQUk7UUFFeEIsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFBO1FBRXRCLElBQ0ksUUFBUTtZQUNSLElBQUksQ0FBQyxTQUFTLEVBQ2hCLENBQUM7WUFDQyxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUE7UUFDekIsQ0FBQztRQUVELE9BQU8sTUFBTSxDQUNULElBQUksQ0FDUCxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFO1lBQ1gsT0FBTyxJQUFJLENBQUMsU0FBUyxHQUFHLEdBQUcsQ0FBQTtRQUMvQixDQUFDLENBQUMsQ0FBQTtJQUNOLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7T0FXRztJQUNILFNBQVMsQ0FDTCxhQUFrQztRQUVsQyxPQUFPLFNBQVMsQ0FDWixJQUFJLENBQUMsR0FBRyxFQUNSLGFBQWEsQ0FDaEIsQ0FBQTtJQUNMLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBuZXQgZnJvbSBcIm5ldFwiXG5pbXBvcnQgeyBDbGllbnRDaGFubmVsLCBDbGllbnRFcnJvckV4dGVuc2lvbnMsIFNGVFBXcmFwcGVyLCBDbGllbnQgYXMgU3NoQ2xpZW50IH0gZnJvbSBcInNzaDJcIlxuaW1wb3J0IHsgRXhlY1Nlc3Npb24gfSBmcm9tIFwiLi9FeGVjU2Vzc2lvbi5qc1wiXG5pbXBvcnQgeyBoYW5kbGVIb3BzIH0gZnJvbSBcIi4vSG9zdEhvcC5qc1wiXG5pbXBvcnQgeyBDbWRDaGFubmVsT3B0aW9ucywgQ21kRXhlY09wdGlvbnMsIFNzaENoYW5uZWwsIFNzaENoYW5uZWxFeGl0LCBleGVjU3NoQ2hhbm5lbCB9IGZyb20gXCIuL1NzaEV4ZWMuanNcIlxuaW1wb3J0IHsgU3NoSG9zdE9wdGlvbnMsIFNzaEhvc3RTZXR0aW5ncywgbG9hZFNldHRpbmdzIH0gZnJvbSBcIi4vU3NoSG9zdE9wdGlvbnMuanNcIlxuaW1wb3J0IHsgU3NoVHVubmVsT3V0T3B0aW9ucywgdHVubmVsT3V0IH0gZnJvbSBcIi4vU3NoVHVubmVsLmpzXCJcbmltcG9ydCB7IEFic3RyYWN0UGFja2FnZU1hbmFnZXIsIGdldEFwbSB9IGZyb20gXCIuL2FwbS9hcG0uanNcIlxuaW1wb3J0IHsgT3NSZWxlYXNlLCBmZXRjaE9zUmVsZWFzZSB9IGZyb20gXCIuL2Vzc2VudGlhbHMvT3NSZWxlYXNlLmpzXCJcbmltcG9ydCB7IFNGVFBQcm9taXNlV3JhcHBlciwgY3JlYXRlU0ZUUFByb21pc2VXcmFwcGVyIH0gZnJvbSBcIi4vZXNzZW50aWFscy9TZnRwUHJvbWlzZVdyYXBwZXIuanNcIlxuaW1wb3J0IHsgQXdhaXRhYmxlLCB0cmltQWxsIH0gZnJvbSBcIi4vdXRpbHMvYmFzZS5qc1wiXG5cbmV4cG9ydCBjbGFzcyBTc2hIb3N0IHtcbiAgICBjbG9zZUVycj86IEVycm9yIHwgc3RyaW5nXG4gICAgY29ubmVjdGVkOiBib29sZWFuID0gZmFsc2VcbiAgICBzc2g6IFNzaENsaWVudCA9IHVuZGVmaW5lZCBhcyBhbnlcbiAgICBzZnRwOiBTRlRQUHJvbWlzZVdyYXBwZXIgPSB1bmRlZmluZWQgYXMgYW55XG4gICAgcmVsZWFzZTogT3NSZWxlYXNlID0gdW5kZWZpbmVkIGFzIGFueVxuXG4gICAgLyoqXG4gICAgICogXG4gICAgICogQGRlc2NyaXB0aW9uIFRoZSBvbmx5IHB1YmxpYyBmdW5jdGlvbiB0byBjcmVhdGUgYSBTc2hIb3N0IG9iamVjdC5cbiAgICAgKiBAcGFyYW0gb3B0aW9ucyBDb25uZWN0aW9uIG9wdGlvbnMgZm9yIHRoZSBzc2ggaG9zdFxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGNvbm5lY3Rpb24gaXMgZXN0YWJsaXNoZWQgYW5kIHRoZSBwcm9taXNlIHByb3ZpZGVzIHlvdSBhbiBTc2hIb3N0IG9iamVjdFxuICAgICAqL1xuICAgIHN0YXRpYyBhc3luYyBjb25uZWN0KFxuICAgICAgICBvcHRpb25zOiBTc2hIb3N0T3B0aW9ucyxcbiAgICApOiBQcm9taXNlPFNzaEhvc3Q+IHtcbiAgICAgICAgY29uc3QgaG9zdCA9IG5ldyBTc2hIb3N0KFxuICAgICAgICAgICAgYXdhaXQgbG9hZFNldHRpbmdzKG9wdGlvbnMpXG4gICAgICAgIClcbiAgICAgICAgYXdhaXQgaG9zdC5jb25uZWN0KClcbiAgICAgICAgcmV0dXJuIGhvc3RcbiAgICB9XG5cbiAgICBwcml2YXRlIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgc2V0dGluZ3M6IFNzaEhvc3RTZXR0aW5nc1xuICAgICkgeyB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGNvbm5lY3QoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMuc3NoID0gYXdhaXQgaGFuZGxlSG9wcyh0aGlzLnNldHRpbmdzKVxuICAgICAgICB0aGlzLmNvbm5lY3RlZCA9IHRydWVcblxuICAgICAgICB0aGlzLnNzaC5vbihcImNsb3NlXCIsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZW1lcmdlbmN5Q2xvc2UoXG4gICAgICAgICAgICAgICAgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBcIlNzaDIgY2xpZW50IGNsb3NlZCFcIlxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIClcbiAgICAgICAgfSlcblxuICAgICAgICB0aGlzLnNzaC5vbihcImVuZFwiLCAoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmVtZXJnZW5jeUNsb3NlKFxuICAgICAgICAgICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICAgICAgXCJTc2gyIGNsaWVudCBlbmQhXCJcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICApXG4gICAgICAgIH0pXG5cbiAgICAgICAgdGhpcy5zc2gub24oXCJ0aW1lb3V0XCIsICgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuZW1lcmdlbmN5Q2xvc2UoXG4gICAgICAgICAgICAgICAgbmV3IEVycm9yKFxuICAgICAgICAgICAgICAgICAgICBcIlNzaDIgY2xpZW50IGNvbm5lY3Rpb24gdGltZW91dCFcIlxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgIClcbiAgICAgICAgfSlcblxuICAgICAgICB0aGlzLnNzaC5vbihcImVycm9yXCIsIChlcnI6IEVycm9yICYgQ2xpZW50RXJyb3JFeHRlbnNpb25zKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmVtZXJnZW5jeUNsb3NlKFxuICAgICAgICAgICAgICAgIGVyclxuICAgICAgICAgICAgKVxuICAgICAgICB9KVxuXG4gICAgICAgIGNvbnN0IHNmdHBXcmFwcGVyID0gYXdhaXQgbmV3IFByb21pc2U8U0ZUUFdyYXBwZXI+KFxuICAgICAgICAgICAgKHJlcywgcmVqKSA9PiB0aGlzLnNzaC5zZnRwKFxuICAgICAgICAgICAgICAgIChlcnIsIHNmdHBDbGllbnQpID0+IGVyciA/IHJlaihlcnIpIDogcmVzKHNmdHBDbGllbnQpXG4gICAgICAgICAgICApXG4gICAgICAgIClcblxuICAgICAgICB0aGlzLnNmdHAgPSBjcmVhdGVTRlRQUHJvbWlzZVdyYXBwZXIoc2Z0cFdyYXBwZXIpXG5cblxuICAgIH1cblxuICAgIHByaXZhdGUgZW1lcmdlbmN5Q2xvc2UoZXJyOiBFcnJvciB8IHN0cmluZykge1xuICAgICAgICBpZiAodHlwZW9mIGVyciA9PSBcInN0cmluZ1wiKSB7XG4gICAgICAgICAgICBlcnIgPSBuZXcgRXJyb3IoZXJyKVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5jb25uZWN0ZWQgPSBmYWxzZVxuICAgICAgICBpZiAoIXRoaXMuY2xvc2VFcnIpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvc2VFcnIgPSBlcnJcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNzaC5kZXN0cm95KClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBAZGVzY3JpcHRpb24gSWYgdGhlIFNzaEhvc3Qgc29ja2V0IGlzIGNsb3NlZCBiZWNhdXNlIG9mIGFuIGVycm9yIHRoaXMgbWV0aG9kIHRocm93cyB0aGF0IGVycm9yLiBcbiAgICAgKiBAdGhyb3dzIFNzaCBzb2NrZXQgY2xvc2UgcmVhc29uXG4gICAgICovXG4gICAgdGhyb3dDbG9zZUVycm9yKCk6IHZvaWQgfCBuZXZlciB7XG4gICAgICAgIGlmICghdGhpcy5jb25uZWN0ZWQpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLmNsb3NlRXJyKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgdGhpcy5jbG9zZUVyclxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgICAgXCJTc2hIb3N0IGlzIG5vdCBjb25uZWN0ZWQhXCJcbiAgICAgICAgICAgIClcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBkZXByZWNhdGVkIFVzZSBkaXNjb25uZWN0KClcbiAgICAgKiBAZGVzY3JpcHRpb24gQ2xvc2VzIHRoZSBzc2ggc29ja2V0IGlmIHN0aWxsIGNvbm5lY3RlZFxuICAgICAqL1xuICAgIGNsb3NlKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5jb25uZWN0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMuc3NoLmVuZCgpXG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5jb25uZWN0ZWQgPSBmYWxzZVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBkZXNjcmlwdGlvbiBDbG9zZXMgdGhlIHNzaCBzb2NrZXQgaWYgc3RpbGwgY29ubmVjdGVkXG4gICAgICovXG4gICAgZGlzY29ubmVjdCgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuY29ubmVjdGVkKSB7XG4gICAgICAgICAgICB0aGlzLnNzaC5lbmQoKVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuY29ubmVjdGVkID0gZmFsc2VcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcGFyYW0gY21kIENvbW1hbmQgc3RyaW5nIHRvIGV4ZWN1dGVcbiAgICAgKiBAcGFyYW0gb3B0aW9ucyBDb21tYW5kIGNoYW5uZWwgZXhlY3V0aW9uIG9wdGlvbnNcbiAgICAgKiBAZGVzY3JpcHRpb24gT3BlbnMgYSBzc2ggY2hhbm5lbCB0byBoYW5kbGUgYSBjb21tYW5kIGV4ZWN1dGlvbiBieSB5b3Vyc2VsZlxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIGNvbW1hbmQgYmVnaW5zIGV4ZWN1dGlvbiBhbmQgdGhlIHByb21pc2UgcHJvdmlkZXMgeW91IHdpdGggdGhlIFNzaENoYW5uZWwgdG8gcHJvY2VzcyB0aGUgY29tbWFuZCBvdXRwdXRcbiAgICAgKi9cbiAgICBleGVjQ2hhbm5lbChcbiAgICAgICAgY21kOiBzdHJpbmcsXG4gICAgICAgIG9wdGlvbnM/OiBDbWRDaGFubmVsT3B0aW9uc1xuICAgICk6IFByb21pc2U8U3NoQ2hhbm5lbD4ge1xuICAgICAgICB0aGlzLnRocm93Q2xvc2VFcnJvcigpXG5cbiAgICAgICAgcmV0dXJuIGV4ZWNTc2hDaGFubmVsKFxuICAgICAgICAgICAgdGhpcy5zc2gsXG4gICAgICAgICAgICBjbWQsXG4gICAgICAgICAgICBvcHRpb25zXG4gICAgICAgIClcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBcbiAgICAgKiBAcGFyYW0gY21kIENvbW1hbmQgc3RyaW5nIHRvIGV4ZWN1dGVcbiAgICAgKiBAcGFyYW0gb3B0aW9ucyBDb21tYW5kIGV4ZWN1dGlvbiBvcHRpb25zXG4gICAgICogQGRlc2NyaXB0aW9uIE9wZW5zIGFuIFNTSCBjaGFubmVsIHRvIGV4ZWN1dGUgdGhlIGNvbW1hbmQgYW5kIHJlc29sdmVzIHRoZSBwcm9taXNlIGFzIHNvb24gYXMgdGhlIGV4aXQgY29kZSBpcyBhdmFpbGFibGUgZm9yIHRoZSBleGVjdXRlZCBjb21tYW5kXG4gICAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2hlbiB0aGUgY29tbWFuZCBiZWdpbnMgZXhlY3V0aW9uIGFuZCB0aGUgcHJvbWlzZSBwcm92aWRlcyB5b3UgdGhlIHByb2Nlc3Mgc29tZSBydW50aW1lLCBzdGRvdXQsIHN0ZGVyciBhbmQgc29tZSBleGl0IGRhdGFcbiAgICAgKi9cbiAgICBhc3luYyBleGVjKFxuICAgICAgICBjbWQ6IHN0cmluZyxcbiAgICAgICAgb3B0aW9ucz86IENtZEV4ZWNPcHRpb25zXG4gICAgKTogUHJvbWlzZTxTc2hDaGFubmVsRXhpdD4ge1xuICAgICAgICB0aGlzLnRocm93Q2xvc2VFcnJvcigpXG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGNoYW5uZWwgPSBhd2FpdCBleGVjU3NoQ2hhbm5lbChcbiAgICAgICAgICAgICAgICB0aGlzLnNzaCxcbiAgICAgICAgICAgICAgICBjbWQsXG4gICAgICAgICAgICAgICAgb3B0aW9uc1xuICAgICAgICAgICAgKVxuXG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgY2hhbm5lbC50b1Byb21pc2UoXG4gICAgICAgICAgICAgICAgb3B0aW9uc1xuICAgICAgICAgICAgKVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBlcnIgaW5zdGFuY2VvZiBFcnJvciAmJlxuICAgICAgICAgICAgICAgIGVyci5tZXNzYWdlID09IFwiU3NoIGNoYW5uZWwgY2xvc2VkLCBjaGVjayB3aHkgdGhlIHNvY2tldCB3YXMgY2xvc2VkIG9yIGxvc3QgY29ubmVjdGlvblwiXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICB0aGlzLnRocm93Q2xvc2VFcnJvcigpXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRocm93IGVyclxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogXG4gICAgICogQGRlc2NyaXB0aW9uIE9wZW5zIGEgc3NoIHNoZWxsIGNoYW5uZWwgdG8gaGFuZGxlIHRoZSBzaGVsbCBieSB5b3Vyc2VsZlxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHdoZW4gdGhlIHNoZWxsIGlzIHN0YXJ0ZWQgYW5kIHRoZSBwcm9taXNlIHByb3ZpZGVzIHlvdSB3aXRoIGEgc2Vzc2lvbiBjaGFubmVsXG4gICAgICovXG4gICAgc2hlbGxDaGFubmVsKCk6IFByb21pc2U8Q2xpZW50Q2hhbm5lbD4ge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2U8Q2xpZW50Q2hhbm5lbD4oXG4gICAgICAgICAgICAocmVzLCByZWopID0+IHRoaXMuc3NoLnNoZWxsKFxuICAgICAgICAgICAgICAgIChlcnIsIGNoYW5uZWwpID0+XG4gICAgICAgICAgICAgICAgICAgIGVyciA/XG4gICAgICAgICAgICAgICAgICAgICAgICByZWooZXJyKSA6XG4gICAgICAgICAgICAgICAgICAgICAgICByZXMoY2hhbm5lbClcbiAgICAgICAgICAgIClcbiAgICAgICAgKVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFxuICAgICAqIEBwYXJhbSBwd2QgSW5pdGFsIHBhdGggb2YgdGhlIHNlc3Npb25cbiAgICAgKiBAcGFyYW0gc3VkbyBEZWZpbmVzIGlmIGNvbW1hbmRzIHNob3VsZCBiZSBwcmVmaWVkIGFzIHN1ZG9cbiAgICAgKiBAcGFyYW0gdGltZW91dE1pbGxpcyBEZWZhdWx0IGNvbW1hbmQgdGltZW91dCBtaWxsaXNlY29uZHNcbiAgICAgKiBAcmV0dXJucyBUaGUgbmV3IGNyZWF0ZWQgRXhlY1Nlc3Npb24gb2JqZWN0XG4gICAgICovXG4gICAgc2Vzc2lvbihcbiAgICAgICAgcHdkOiBzdHJpbmcsXG4gICAgICAgIHN1ZG8/OiBib29sZWFuLFxuICAgICAgICB0aW1lb3V0TWlsbGlzPzogbnVtYmVyLFxuICAgICk6IEV4ZWNTZXNzaW9uIHtcbiAgICAgICAgY29uc3Qgc2Vzc2lvbiA9IG5ldyBFeGVjU2Vzc2lvbihcbiAgICAgICAgICAgIHRoaXMsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgUFdEOiBwd2QsXG4gICAgICAgICAgICB9XG4gICAgICAgIClcblxuICAgICAgICBzZXNzaW9uLnN1ZG8gPSBzdWRvXG4gICAgICAgIHNlc3Npb24udGltZW91dE1pbGxpcyA9IHRpbWVvdXRNaWxsaXNcblxuICAgICAgICByZXR1cm4gc2Vzc2lvblxuICAgIH1cblxuICAgIGFzeW5jIGhvbWVEaXIoKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5leGVjKFwicHdkXCIpXG4gICAgICAgIHJldHVybiB0cmltQWxsKHJlc3VsdC5vdXQpXG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogXG4gICAgICogQHBhcmFtIGNtZCBDb21tYW5kIHRvIGNoZWNrIGlmIGl0IGNtZEV4aXN0c1xuICAgICAqIEBkZXNjcmlwdGlvbiBGdW5jdGlvbiB0aGF0IGNoZWNrcyBpZiBhIHNwaXNpZmljIGNvbW1hbmQgZXhpc3RzIG9uIHRoZSByZW1vdGUgc3NoIGhvc3RcbiAgICAgKiBAcmV0dXJucyBUcnVlIGlmIGNvbW1hbmQgZXhpc3RzIGFuZCBmYWxzZSBpZiBub3RcbiAgICAgKi9cbiAgICBhc3luYyBjbWRFeGlzdHMoXG4gICAgICAgIGNtZDogc3RyaW5nXG4gICAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgICAgIHRoaXMudGhyb3dDbG9zZUVycm9yKClcblxuICAgICAgICBpZiAoY21kLmluY2x1ZGVzKFwiIFwiKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ29tbWFuZCBjYW50IGNvbnRhaW4gYSBzcGFjZTogJ1wiICsgY21kICsgXCInXCIpXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBleGl0ID0gYXdhaXQgdGhpcy5leGVjKFwiY29tbWFuZCAtdiBcIiArIGNtZCwge1xuICAgICAgICAgICAgZXhwZWN0ZWRFeGl0Q29kZTogWzAsIDFdXG4gICAgICAgIH0pXG5cbiAgICAgICAgcmV0dXJuIGV4aXQuY29kZSA9PSAwXG4gICAgfVxuXG4gICAgY2FjaGVkT3NSZWxlYXNlOiBPc1JlbGVhc2UgfCB1bmRlZmluZWRcblxuICAgIC8qKlxuICAgICAqIFxuICAgICAqIEBkZXNjcmlwdGlvbiBSZXNvbHZlcyBhbGwgcmVsZWFzZXMgZmlsZXMgZnJvbSAnL2V0Yy8qLXJlbGVhc2UnIG9uIHRoZSByZW1vdmUgc3NoIGhvc3QgYW5kIG1hcHMgdGhlbS4gQWxzbyB0cmllcyB0byBkZXRlY3QgdGhlIGRpc3RybyBuYW1lIGFuZCB2ZXJzaW9uLlxuICAgICAqIEBwYXJhbSB1c2VDYWNoZSBUaGUgcmV0dXJuIHZhbHVlIG9mIHRoaXMgZnVuY3Rpb24gZ2V0IGNhY2hlZCBmb3IgdGhlIG5leHQgZnVuY3Rpb24gY2FsbC4gU2V0IHRoaXMgdG8gZmFsc2UgdG8gZGlzYWJsZSB0aGUgY2FjaGluZy5cbiAgICAgKiBAcmV0dXJucyBSZXR1cm5zIGFuIG9iamVjdCB0aGF0IG1hcHMgYWxsIHZhbHVlcyBvZiBhbGwgJy9ldGMvKi1yZWxlYXNlJyBmaWxlcy5cbiAgICAgKi9cbiAgICBmZXRjaE9zUmVsZWFzZShcbiAgICAgICAgdXNlQ2FjaGU6IGJvb2xlYW4gPSB0cnVlLFxuICAgICk6IEF3YWl0YWJsZTxPc1JlbGVhc2U+IHtcbiAgICAgICAgdGhpcy50aHJvd0Nsb3NlRXJyb3IoKVxuXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHVzZUNhY2hlICYmXG4gICAgICAgICAgICB0aGlzLmNhY2hlZE9zUmVsZWFzZVxuICAgICAgICApIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlZE9zUmVsZWFzZVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGZldGNoT3NSZWxlYXNlKFxuICAgICAgICAgICAgdGhpc1xuICAgICAgICApLnRoZW4oKHJlbGVhc2UpID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlZE9zUmVsZWFzZSA9IHJlbGVhc2VcbiAgICAgICAgfSlcbiAgICB9XG5cblxuXG4gICAgY2FjaGVkQXBtOiBBYnN0cmFjdFBhY2thZ2VNYW5hZ2VyIHwgdW5kZWZpbmVkXG5cbiAgICAvKipcbiAgICAgKiBAZGVwcmVjYXRlZCBDaGVjayB5b3VyIHNlbGZcbiAgICAgKiBAZGVzY3JpcHRpb24gQ2hlY2tzIG9uZSBvZiB0aGUgcHJlZGVmaW5lZCBwYWNrYWdlIG1hbmdlcnMgaXMgaW5zdGFsbGVkIChhcHQsIGRuZiwgeXVtIG9yIGEgY3VzdG9tIG9uZSkgYW5kIHJldHVybnMgYSBhYnN0cmFjdCBpbnRlcmZhY2UgZm9yIHRoYXQgcGFja2FnZSBtYW5hZ2VyLlxuICAgICAqIEBwYXJhbSB1c2VDYWNoZSBUaGUgcmV0dXJuIHZhbHVlIG9mIHRoaXMgZnVuY3Rpb24gZ2V0IGNhY2hlZCBmb3IgdGhlIG5leHQgZnVuY3Rpb24gY2FsbC4gU2V0IHRoaXMgdG8gZmFsc2UgdG8gZGlzYWJsZSB0aGUgY2FjaGluZy5cbiAgICAgKiBAdGhyb3dzIFRocm93cyBhbiBlcnJvciBpZiB0aGUgcGFja2FnZSBtYW5hZ2VyIGNhbnQgYmUgZGV0ZWN0ZWQuXG4gICAgICogQHJldHVybnMgVGhlIGFic3RyYWN0IGludGVyZmFjZSBvZiB0aGUgaG9zdHMgcGFja2FnZSBtYW5hZ2VyIChpZiBmb3VuZCkuXG4gICAgICovXG4gICAgZ2V0QXBtKFxuICAgICAgICB1c2VDYWNoZTogYm9vbGVhbiA9IHRydWUsXG4gICAgKTogQXdhaXRhYmxlPEFic3RyYWN0UGFja2FnZU1hbmFnZXI+IHtcbiAgICAgICAgdGhpcy50aHJvd0Nsb3NlRXJyb3IoKVxuXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHVzZUNhY2hlICYmXG4gICAgICAgICAgICB0aGlzLmNhY2hlZEFwbVxuICAgICAgICApIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlZEFwbVxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGdldEFwbShcbiAgICAgICAgICAgIHRoaXNcbiAgICAgICAgKS50aGVuKChhcG0pID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLmNhY2hlZEFwbSA9IGFwbVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBleHBlcmltZW50YWwgVGhpcyBmdW5jdGlvbiBpcyBleHBlcmltZW50YWwgYW5kIG1heSBub3QgYmUgc3RhYmxlLlxuICAgICAqIENyZWF0ZXMgYSBsb2NhbCBzZXJ2ZXIgdGhhdCB0dW5uZWxzIGluY29taW5nIGNvbm5lY3Rpb25zIHRvIGEgcmVtb3RlIGxpbnV4IHNvY2tldCBvciBob3N0IGFuZCBwb3J0IGJpbmQuXG4gICAgICpcbiAgICAgKiBZb3UgbmVlZCB0byBjbG9zZSB0aGUgc2VydmVyIHRvIHN0b3AgdHVubmVsaW5nIVxuICAgICAqIFxuICAgICAqIFRoaXMgZnVuY3Rpb24gY3JlYXRlcyBhIHNlcnZlciB0aGF0IGxpc3RlbnMgZm9yIGluY29taW5nIGNvbm5lY3Rpb25zIGFuZCBmb3J3YXJkcyB0aGVtIHRvIHRoZSByZW1vdGUgU1NIIGhvc3QuIFxuICAgICAqIFxuICAgICAqIEBwYXJhbSB0dW5uZWxPcHRpb25zIC0gT3B0aW9ucyBzcGVjaWZ5aW5nIHJlbW90ZSBsaW51eCBzb2NrZXQgb3IgaG9zdCBhbmQgcG9ydCBkZXRhaWxzLlxuICAgICAqIFxuICAgICAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBjcmVhdGVkIHNlcnZlciB0aGF0IG5lZWQgdG8gYmUgY2xvc2VkLlxuICAgICAqL1xuICAgIHR1bm5lbE91dChcbiAgICAgICAgdHVubmVsT3B0aW9uczogU3NoVHVubmVsT3V0T3B0aW9ucyxcbiAgICApOiBQcm9taXNlPG5ldC5TZXJ2ZXI+IHtcbiAgICAgICAgcmV0dXJuIHR1bm5lbE91dChcbiAgICAgICAgICAgIHRoaXMuc3NoLFxuICAgICAgICAgICAgdHVubmVsT3B0aW9uc1xuICAgICAgICApXG4gICAgfVxufSJdfQ==