UNPKG

@ultipa-graph/ultipa-driver

Version:

NodeJS SDK for Ultipa GQL

247 lines 25.9 kB
"use strict"; /** * Connection management for GQLDB Node.js driver. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ConnectionPool = void 0; const grpc = __importStar(require("@grpc/grpc-js")); const errors_1 = require("./errors"); /** * Number of consecutive unhealthy ticks required before forcing a * reconnect (which replaces the channel; in-flight RPCs are NOT * cancelled because the old channel is left alive until GC). */ const UNHEALTHY_RECONNECT_THRESHOLD = 3; /** Manages a pool of connections to GQLDB servers */ class ConnectionPool { config; connections = new Map(); closed = false; healthCheckTimer; constructor(config) { this.config = config; this.initConnections(); this.startHealthCheck(); } initConnections() { for (const host of this.config.hosts) { try { const conn = this.createConnection(host); this.connections.set(host, conn); } catch (e) { // Continue with other hosts console.error(`Failed to connect to ${host}:`, e); } } if (this.connections.size === 0) { throw new errors_1.AllHostsFailedError(); } } createConnection(host) { let credentials; if (this.config.tlsOptions) { credentials = grpc.credentials.createSsl(); } else { credentials = grpc.credentials.createInsecure(); } const options = { 'grpc.max_receive_message_length': this.config.maxRecvSize ?? 64 * 1024 * 1024, 'grpc.max_send_message_length': this.config.maxRecvSize ?? 64 * 1024 * 1024, 'grpc.keepalive_time_ms': 30000, 'grpc.keepalive_timeout_ms': 10000, 'grpc.keepalive_permit_without_calls': 1, }; // Create a generic client for the channel const client = new grpc.Client(host, credentials, options); return { host, client, healthy: true, lastPing: Date.now(), consecutiveUnhealthy: 0, }; } /** Get a healthy connection from the pool */ getConnection() { if (this.closed) { throw new errors_1.ConnectionClosedError(); } for (const conn of this.connections.values()) { if (conn.healthy) { return conn.client; } } throw new errors_1.NoConnectionError(); } /** Get a connection for a specific host */ getConnectionForHost(host) { const conn = this.connections.get(host); if (!conn || !conn.healthy) { throw new errors_1.NoConnectionError(); } return conn.client; } startHealthCheck() { const interval = this.config.healthCheckInterval ?? 30000; if (interval > 0) { this.healthCheckTimer = setInterval(() => this.checkHealth(), interval); } } /** * Health-check tick. Connectivity-state semantics: * - READY / IDLE → healthy; reset the unhealthy counter. * - CONNECTING → transient; let the channel heal itself, * don't count toward unhealthy. * - SHUTDOWN → terminal; reconnect immediately. * - TRANSIENT_FAILURE → count toward unhealthy; reconnect only * after UNHEALTHY_RECONNECT_THRESHOLD * consecutive ticks. * * Reconnect replaces the pool's channel reference; it does NOT close * the old channel, so in-flight RPCs continue to completion on it. */ checkHealth() { for (const [host, conn] of this.connections) { const state = conn.client.getChannel().getConnectivityState(false); if (state === grpc.connectivityState.READY || state === grpc.connectivityState.IDLE) { conn.healthy = true; conn.consecutiveUnhealthy = 0; conn.lastPing = Date.now(); continue; } if (state === grpc.connectivityState.CONNECTING) { // Transient — let the channel heal itself. continue; } if (state === grpc.connectivityState.SHUTDOWN) { conn.healthy = false; conn.consecutiveUnhealthy = UNHEALTHY_RECONNECT_THRESHOLD; this.reconnect(host); continue; } // TRANSIENT_FAILURE or any other non-READY state. conn.consecutiveUnhealthy++; if (conn.consecutiveUnhealthy >= UNHEALTHY_RECONNECT_THRESHOLD) { conn.healthy = false; this.reconnect(host); } } } /** * Replace the pool's channel for `host` with a fresh one. * * Critically, does NOT close the old channel. In-flight RPCs hold * their own reference to it via the client they were issued on and * continue to completion on that channel; the Node.js runtime's * garbage collector releases the old channel only after every * in-flight RPC has resolved. * * Earlier versions closed the old channel synchronously here, which * cancelled every pending RPC with a "Channel closed!"-style error. */ /** * Rebuild every host's gRPC client in the pool. Called by the client * on transport-level errors (UNAVAILABLE / connection reset) so the * next RPC gets a fresh channel. Like reconnect, this does NOT close * old clients synchronously — in-flight RPCs hold them alive and gRPC * closes them naturally once those calls complete. */ async forceReconnectAll() { if (this.closed) return; const hosts = Array.from(this.connections.keys()); for (const host of hosts) { await this.reconnect(host); } } async reconnect(host) { if (this.closed) return; try { // Build the new connection up-front; only swap if it succeeds. const newConn = this.createConnection(host); // Overwrite the pool entry. The old Connection is now // unreferenced by the pool; in-flight RPCs still hold its // grpc.Client alive via per-call references. Once they // complete the client becomes garbage and gRPC closes it // naturally. this.connections.set(host, newConn); } catch (e) { console.error(`Failed to reconnect to ${host}:`, e); } } /** Close all connections in the pool */ close() { if (this.closed) return; this.closed = true; if (this.healthCheckTimer) { clearInterval(this.healthCheckTimer); } for (const conn of this.connections.values()) { try { conn.client.close(); } catch (e) { // Ignore } } this.connections.clear(); } /** Get the number of healthy hosts */ healthyHostCount() { let count = 0; for (const conn of this.connections.values()) { if (conn.healthy) count++; } return count; } /** Get the list of configured hosts */ hosts() { return Array.from(this.connections.keys()); } /** Check if the pool is closed */ isClosed() { return this.closed; } } exports.ConnectionPool = ConnectionPool; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9jb25uZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7R0FFRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUgsb0RBQXNDO0FBRXRDLHFDQUF5RjtBQWtCekY7Ozs7R0FJRztBQUNILE1BQU0sNkJBQTZCLEdBQUcsQ0FBQyxDQUFDO0FBRXhDLHFEQUFxRDtBQUNyRCxNQUFhLGNBQWM7SUFDakIsTUFBTSxDQUFjO0lBQ3BCLFdBQVcsR0FBNEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNqRCxNQUFNLEdBQUcsS0FBSyxDQUFDO0lBQ2YsZ0JBQWdCLENBQWtCO0lBRTFDLFlBQVksTUFBbUI7UUFDN0IsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO0lBQzFCLENBQUM7SUFFTyxlQUFlO1FBQ3JCLEtBQUssTUFBTSxJQUFJLElBQUksSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNyQyxJQUFJLENBQUM7Z0JBQ0gsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDO2dCQUN6QyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDbkMsQ0FBQztZQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ1gsNEJBQTRCO2dCQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLHdCQUF3QixJQUFJLEdBQUcsRUFBRSxDQUFDLENBQUMsQ0FBQztZQUNwRCxDQUFDO1FBQ0gsQ0FBQztRQUVELElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDaEMsTUFBTSxJQUFJLDRCQUFt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QUksSUFBSSxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDN0MsSUFBSSxJQUFJLENBQUMsT0FBTztnQkFBRSxLQUFLLEVBQUUsQ0FBQztRQUM1QixDQUFDO1FBQ0QsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQsdUNBQXVDO0lBQ3ZDLEtBQUs7UUFDSCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFRCxrQ0FBa0M7SUFDbEMsUUFBUTtRQUNOLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO0NBQ0Y7QUF6TkQsd0NBeU5DIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBDb25uZWN0aW9uIG1hbmFnZW1lbnQgZm9yIEdRTERCIE5vZGUuanMgZHJpdmVyLlxuICovXG5cbmltcG9ydCAqIGFzIGdycGMgZnJvbSAnQGdycGMvZ3JwYy1qcyc7XG5pbXBvcnQgeyBHcWxkYkNvbmZpZyB9IGZyb20gJy4vY29uZmlnJztcbmltcG9ydCB7IEFsbEhvc3RzRmFpbGVkRXJyb3IsIENvbm5lY3Rpb25DbG9zZWRFcnJvciwgTm9Db25uZWN0aW9uRXJyb3IgfSBmcm9tICcuL2Vycm9ycyc7XG5cbi8qKiBSZXByZXNlbnRzIGEgc2luZ2xlIGdSUEMgY29ubmVjdGlvbiAqL1xuaW50ZXJmYWNlIENvbm5lY3Rpb24ge1xuICBob3N0OiBzdHJpbmc7XG4gIGNsaWVudDogZ3JwYy5DbGllbnQ7XG4gIGhlYWx0aHk6IGJvb2xlYW47XG4gIGxhc3RQaW5nOiBudW1iZXI7XG4gIC8qKlxuICAgKiBOdW1iZXIgb2YgY29uc2VjdXRpdmUgaGVhbHRoLWNoZWNrIHRpY2tzIHRoYXQgb2JzZXJ2ZWQgYSBub24tUkVBRFlcbiAgICogc3RhdGUuIFJlY29ubmVjdCBpcyBvbmx5IHRyaWdnZXJlZCBhZnRlciB0aGlzIHJlYWNoZXNcbiAgICogVU5IRUFMVEhZX1JFQ09OTkVDVF9USFJFU0hPTEQsIHNvIGEgc2luZ2xlIHRyYW5zaWVudCBmbGlja2VyXG4gICAqIGR1cmluZyBhIGJ1c3kgaW4tZmxpZ2h0IFJQQyBkb2VzIG5vdCB0ZWFyIHRoZSBjaGFubmVsIGRvd24gYW5kXG4gICAqIGNhbmNlbCBldmVyeSBwZW5kaW5nIHJlcXVlc3QuXG4gICAqL1xuICBjb25zZWN1dGl2ZVVuaGVhbHRoeTogbnVtYmVyO1xufVxuXG4vKipcbiAqIE51bWJlciBvZiBjb25zZWN1dGl2ZSB1bmhlYWx0aHkgdGlja3MgcmVxdWlyZWQgYmVmb3JlIGZvcmNpbmcgYVxuICogcmVjb25uZWN0ICh3aGljaCByZXBsYWNlcyB0aGUgY2hhbm5lbDsgaW4tZmxpZ2h0IFJQQ3MgYXJlIE5PVFxuICogY2FuY2VsbGVkIGJlY2F1c2UgdGhlIG9sZCBjaGFubmVsIGlzIGxlZnQgYWxpdmUgdW50aWwgR0MpLlxuICovXG5jb25zdCBVTkhFQUxUSFlfUkVDT05ORUNUX1RIUkVTSE9MRCA9IDM7XG5cbi8qKiBNYW5hZ2VzIGEgcG9vbCBvZiBjb25uZWN0aW9ucyB0byBHUUxEQiBzZXJ2ZXJzICovXG5leHBvcnQgY2xhc3MgQ29ubmVjdGlvblBvb2wge1xuICBwcml2YXRlIGNvbmZpZzogR3FsZGJDb25maWc7XG4gIHByaXZhdGUgY29ubmVjdGlvbnM6IE1hcDxzdHJpbmcsIENvbm5lY3Rpb24+ID0gbmV3IE1hcCgpO1xuICBwcml2YXRlIGNsb3NlZCA9IGZhbHNlO1xuICBwcml2YXRlIGhlYWx0aENoZWNrVGltZXI/OiBOb2RlSlMuVGltZW91dDtcblxuICBjb25zdHJ1Y3Rvcihjb25maWc6IEdxbGRiQ29uZmlnKSB7XG4gICAgdGhpcy5jb25maWcgPSBjb25maWc7XG4gICAgdGhpcy5pbml0Q29ubmVjdGlvbnMoKTtcbiAgICB0aGlzLnN0YXJ0SGVhbHRoQ2hlY2soKTtcbiAgfVxuXG4gIHByaXZhdGUgaW5pdENvbm5lY3Rpb25zKCk6IHZvaWQge1xuICAgIGZvciAoY29uc3QgaG9zdCBvZiB0aGlzLmNvbmZpZy5ob3N0cykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgY29ubiA9IHRoaXMuY3JlYXRlQ29ubmVjdGlvbihob3N0KTtcbiAgICAgICAgdGhpcy5jb25uZWN0aW9ucy5zZXQoaG9zdCwgY29ubik7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIENvbnRpbnVlIHdpdGggb3RoZXIgaG9zdHNcbiAgICAgICAgY29uc29sZS5lcnJvcihgRmFpbGVkIHRvIGNvbm5lY3QgdG8gJHtob3N0fTpgLCBlKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodGhpcy5jb25uZWN0aW9ucy5zaXplID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgQWxsSG9zdHNGYWlsZWRFcnJvcigpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY3JlYXRlQ29ubmVjdGlvbihob3N0OiBzdHJpbmcpOiBDb25uZWN0aW9uIHtcbiAgICBsZXQgY3JlZGVudGlhbHM6IGdycGMuQ2hhbm5lbENyZWRlbnRpYWxzO1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLnRsc09wdGlvbnMpIHtcbiAgICAgIGNyZWRlbnRpYWxzID0gZ3JwYy5jcmVkZW50aWFscy5jcmVhdGVTc2woKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY3JlZGVudGlhbHMgPSBncnBjLmNyZWRlbnRpYWxzLmNyZWF0ZUluc2VjdXJlKCk7XG4gICAgfVxuXG4gICAgY29uc3Qgb3B0aW9uczogZ3JwYy5DaGFubmVsT3B0aW9ucyA9IHtcbiAgICAgICdncnBjLm1heF9yZWNlaXZlX21lc3NhZ2VfbGVuZ3RoJzogdGhpcy5jb25maWcubWF4UmVjdlNpemUgPz8gNjQgKiAxMDI0ICogMTAyNCxcbiAgICAgICdncnBjLm1heF9zZW5kX21lc3NhZ2VfbGVuZ3RoJzogdGhpcy5jb25maWcubWF4UmVjdlNpemUgPz8gNjQgKiAxMDI0ICogMTAyNCxcbiAgICAgICdncnBjLmtlZXBhbGl2ZV90aW1lX21zJzogMzAwMDAsXG4gICAgICAnZ3JwYy5rZWVwYWxpdmVfdGltZW91dF9tcyc6IDEwMDAwLFxuICAgICAgJ2dycGMua2VlcGFsaXZlX3Blcm1pdF93aXRob3V0X2NhbGxzJzogMSxcbiAgICB9O1xuXG4gICAgLy8gQ3JlYXRlIGEgZ2VuZXJpYyBjbGllbnQgZm9yIHRoZSBjaGFubmVsXG4gICAgY29uc3QgY2xpZW50ID0gbmV3IGdycGMuQ2xpZW50KGhvc3QsIGNyZWRlbnRpYWxzLCBvcHRpb25zKTtcblxuICAgIHJldHVybiB7XG4gICAgICBob3N0LFxuICAgICAgY2xpZW50LFxuICAgICAgaGVhbHRoeTogdHJ1ZSxcbiAgICAgIGxhc3RQaW5nOiBEYXRlLm5vdygpLFxuICAgICAgY29uc2VjdXRpdmVVbmhlYWx0aHk6IDAsXG4gICAgfTtcbiAgfVxuXG4gIC8qKiBHZXQgYSBoZWFsdGh5IGNvbm5lY3Rpb24gZnJvbSB0aGUgcG9vbCAqL1xuICBnZXRDb25uZWN0aW9uKCk6IGdycGMuQ2xpZW50IHtcbiAgICBpZiAodGhpcy5jbG9zZWQpIHtcbiAgICAgIHRocm93IG5ldyBDb25uZWN0aW9uQ2xvc2VkRXJyb3IoKTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGNvbm4gb2YgdGhpcy5jb25uZWN0aW9ucy52YWx1ZXMoKSkge1xuICAgICAgaWYgKGNvbm4uaGVhbHRoeSkge1xuICAgICAgICByZXR1cm4gY29ubi5jbGllbnQ7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IE5vQ29ubmVjdGlvbkVycm9yKCk7XG4gIH1cblxuICAvKiogR2V0IGEgY29ubmVjdGlvbiBmb3IgYSBzcGVjaWZpYyBob3N0ICovXG4gIGdldENvbm5lY3Rpb25Gb3JIb3N0KGhvc3Q6IHN0cmluZyk6IGdycGMuQ2xpZW50IHtcbiAgICBjb25zdCBjb25uID0gdGhpcy5jb25uZWN0aW9ucy5nZXQoaG9zdCk7XG4gICAgaWYgKCFjb25uIHx8ICFjb25uLmhlYWx0aHkpIHtcbiAgICAgIHRocm93IG5ldyBOb0Nvbm5lY3Rpb25FcnJvcigpO1xuICAgIH1cbiAgICByZXR1cm4gY29ubi5jbGllbnQ7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0SGVhbHRoQ2hlY2soKTogdm9pZCB7XG4gICAgY29uc3QgaW50ZXJ2YWwgPSB0aGlzLmNvbmZpZy5oZWFsdGhDaGVja0ludGVydmFsID8/IDMwMDAwO1xuICAgIGlmIChpbnRlcnZhbCA+IDApIHtcbiAgICAgIHRoaXMuaGVhbHRoQ2hlY2tUaW1lciA9IHNldEludGVydmFsKCgpID0+IHRoaXMuY2hlY2tIZWFsdGgoKSwgaW50ZXJ2YWwpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIZWFsdGgtY2hlY2sgdGljay4gIENvbm5lY3Rpdml0eS1zdGF0ZSBzZW1hbnRpY3M6XG4gICAqICAtIFJFQURZIC8gSURMRSAgICAgICAgIOKGkiBoZWFsdGh5OyByZXNldCB0aGUgdW5oZWFsdGh5IGNvdW50ZXIuXG4gICAqICAtIENPTk5FQ1RJTkcgICAgICAgICAgIOKGkiB0cmFuc2llbnQ7IGxldCB0aGUgY2hhbm5lbCBoZWFsIGl0c2VsZixcbiAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9uJ3QgY291bnQgdG93YXJkIHVuaGVhbHRoeS5cbiAgICogIC0gU0hVVERPV04gICAgICAgICAgICAg4oaSIHRlcm1pbmFsOyByZWNvbm5lY3QgaW1tZWRpYXRlbHkuXG4gICAqICAtIFRSQU5TSUVOVF9GQUlMVVJFICAgIOKGkiBjb3VudCB0b3dhcmQgdW5oZWFsdGh5OyByZWNvbm5lY3Qgb25seVxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZnRlciBVTkhFQUxUSFlfUkVDT05ORUNUX1RIUkVTSE9MRFxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zZWN1dGl2ZSB0aWNrcy5cbiAgICpcbiAgICogUmVjb25uZWN0IHJlcGxhY2VzIHRoZSBwb29sJ3MgY2hhbm5lbCByZWZlcmVuY2U7IGl0IGRvZXMgTk9UIGNsb3NlXG4gICAqIHRoZSBvbGQgY2hhbm5lbCwgc28gaW4tZmxpZ2h0IFJQQ3MgY29udGludWUgdG8gY29tcGxldGlvbiBvbiBpdC5cbiAgICovXG4gIHByaXZhdGUgY2hlY2tIZWFsdGgoKTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBbaG9zdCwgY29ubl0gb2YgdGhpcy5jb25uZWN0aW9ucykge1xuICAgICAgY29uc3Qgc3RhdGUgPSBjb25uLmNsaWVudC5nZXRDaGFubmVsKCkuZ2V0Q29ubmVjdGl2aXR5U3RhdGUoZmFsc2UpO1xuXG4gICAgICBpZiAoc3RhdGUgPT09IGdycGMuY29ubmVjdGl2aXR5U3RhdGUuUkVBRFkgfHxcbiAgICAgICAgICBzdGF0ZSA9PT0gZ3JwYy5jb25uZWN0aXZpdHlTdGF0ZS5JRExFKSB7XG4gICAgICAgIGNvbm4uaGVhbHRoeSA9IHRydWU7XG4gICAgICAgIGNvbm4uY29uc2VjdXRpdmVVbmhlYWx0aHkgPSAwO1xuICAgICAgICBjb25uLmxhc3RQaW5nID0gRGF0ZS5ub3coKTtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIGlmIChzdGF0ZSA9PT0gZ3JwYy5jb25uZWN0aXZpdHlTdGF0ZS5DT05ORUNUSU5HKSB7XG4gICAgICAgIC8vIFRyYW5zaWVudCDigJQgbGV0IHRoZSBjaGFubmVsIGhlYWwgaXRzZWxmLlxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKHN0YXRlID09PSBncnBjLmNvbm5lY3Rpdml0eVN0YXRlLlNIVVRET1dOKSB7XG4gICAgICAgIGNvbm4uaGVhbHRoeSA9IGZhbHNlO1xuICAgICAgICBjb25uLmNvbnNlY3V0aXZlVW5oZWFsdGh5ID0gVU5IRUFMVEhZX1JFQ09OTkVDVF9USFJFU0hPTEQ7XG4gICAgICAgIHRoaXMucmVjb25uZWN0KGhvc3QpO1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cblxuICAgICAgLy8gVFJBTlNJRU5UX0ZBSUxVUkUgb3IgYW55IG90aGVyIG5vbi1SRUFEWSBzdGF0ZS5cbiAgICAgIGNvbm4uY29uc2VjdXRpdmVVbmhlYWx0aHkrKztcbiAgICAgIGlmIChjb25uLmNvbnNlY3V0aXZlVW5oZWFsdGh5ID49IFVOSEVBTFRIWV9SRUNPTk5FQ1RfVEhSRVNIT0xEKSB7XG4gICAgICAgIGNvbm4uaGVhbHRoeSA9IGZhbHNlO1xuICAgICAgICB0aGlzLnJlY29ubmVjdChob3N0KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVwbGFjZSB0aGUgcG9vbCdzIGNoYW5uZWwgZm9yIGBob3N0YCB3aXRoIGEgZnJlc2ggb25lLlxuICAgKlxuICAgKiBDcml0aWNhbGx5LCBkb2VzIE5PVCBjbG9zZSB0aGUgb2xkIGNoYW5uZWwuICBJbi1mbGlnaHQgUlBDcyBob2xkXG4gICAqIHRoZWlyIG93biByZWZlcmVuY2UgdG8gaXQgdmlhIHRoZSBjbGllbnQgdGhleSB3ZXJlIGlzc3VlZCBvbiBhbmRcbiAgICogY29udGludWUgdG8gY29tcGxldGlvbiBvbiB0aGF0IGNoYW5uZWw7IHRoZSBOb2RlLmpzIHJ1bnRpbWUnc1xuICAgKiBnYXJiYWdlIGNvbGxlY3RvciByZWxlYXNlcyB0aGUgb2xkIGNoYW5uZWwgb25seSBhZnRlciBldmVyeVxuICAgKiBpbi1mbGlnaHQgUlBDIGhhcyByZXNvbHZlZC5cbiAgICpcbiAgICogRWFybGllciB2ZXJzaW9ucyBjbG9zZWQgdGhlIG9sZCBjaGFubmVsIHN5bmNocm9ub3VzbHkgaGVyZSwgd2hpY2hcbiAgICogY2FuY2VsbGVkIGV2ZXJ5IHBlbmRpbmcgUlBDIHdpdGggYSBcIkNoYW5uZWwgY2xvc2VkIVwiLXN0eWxlIGVycm9yLlxuICAgKi9cbiAgLyoqXG4gICAqIFJlYnVpbGQgZXZlcnkgaG9zdCdzIGdSUEMgY2xpZW50IGluIHRoZSBwb29sLiAgQ2FsbGVkIGJ5IHRoZSBjbGllbnRcbiAgICogb24gdHJhbnNwb3J0LWxldmVsIGVycm9ycyAoVU5BVkFJTEFCTEUgLyBjb25uZWN0aW9uIHJlc2V0KSBzbyB0aGVcbiAgICogbmV4dCBSUEMgZ2V0cyBhIGZyZXNoIGNoYW5uZWwuICBMaWtlIHJlY29ubmVjdCwgdGhpcyBkb2VzIE5PVCBjbG9zZVxuICAgKiBvbGQgY2xpZW50cyBzeW5jaHJvbm91c2x5IOKAlCBpbi1mbGlnaHQgUlBDcyBob2xkIHRoZW0gYWxpdmUgYW5kIGdSUENcbiAgICogY2xvc2VzIHRoZW0gbmF0dXJhbGx5IG9uY2UgdGhvc2UgY2FsbHMgY29tcGxldGUuXG4gICAqL1xuICBhc3luYyBmb3JjZVJlY29ubmVjdEFsbCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAodGhpcy5jbG9zZWQpIHJldHVybjtcbiAgICBjb25zdCBob3N0cyA9IEFycmF5LmZyb20odGhpcy5jb25uZWN0aW9ucy5rZXlzKCkpO1xuICAgIGZvciAoY29uc3QgaG9zdCBvZiBob3N0cykge1xuICAgICAgYXdhaXQgdGhpcy5yZWNvbm5lY3QoaG9zdCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyByZWNvbm5lY3QoaG9zdDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuY2xvc2VkKSByZXR1cm47XG5cbiAgICB0cnkge1xuICAgICAgLy8gQnVpbGQgdGhlIG5ldyBjb25uZWN0aW9uIHVwLWZyb250OyBvbmx5IHN3YXAgaWYgaXQgc3VjY2VlZHMuXG4gICAgICBjb25zdCBuZXdDb25uID0gdGhpcy5jcmVhdGVDb25uZWN0aW9uKGhvc3QpO1xuICAgICAgLy8gT3ZlcndyaXRlIHRoZSBwb29sIGVudHJ5LiAgVGhlIG9sZCBDb25uZWN0aW9uIGlzIG5vd1xuICAgICAgLy8gdW5yZWZlcmVuY2VkIGJ5IHRoZSBwb29sOyBpbi1mbGlnaHQgUlBDcyBzdGlsbCBob2xkIGl0c1xuICAgICAgLy8gZ3JwYy5DbGllbnQgYWxpdmUgdmlhIHBlci1jYWxsIHJlZmVyZW5jZXMuICBPbmNlIHRoZXlcbiAgICAgIC8vIGNvbXBsZXRlIHRoZSBjbGllbnQgYmVjb21lcyBnYXJiYWdlIGFuZCBnUlBDIGNsb3NlcyBpdFxuICAgICAgLy8gbmF0dXJhbGx5LlxuICAgICAgdGhpcy5jb25uZWN0aW9ucy5zZXQoaG9zdCwgbmV3Q29ubik7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5lcnJvcihgRmFpbGVkIHRvIHJlY29ubmVjdCB0byAke2hvc3R9OmAsIGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBDbG9zZSBhbGwgY29ubmVjdGlvbnMgaW4gdGhlIHBvb2wgKi9cbiAgY2xvc2UoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuY2xvc2VkKSByZXR1cm47XG5cbiAgICB0aGlzLmNsb3NlZCA9IHRydWU7XG5cbiAgICBpZiAodGhpcy5oZWFsdGhDaGVja1RpbWVyKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuaGVhbHRoQ2hlY2tUaW1lcik7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBjb25uIG9mIHRoaXMuY29ubmVjdGlvbnMudmFsdWVzKCkpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbm4uY2xpZW50LmNsb3NlKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIC8vIElnbm9yZVxuICAgICAgfVxuICAgIH1cblxuICAgIHRoaXMuY29ubmVjdGlvbnMuY2xlYXIoKTtcbiAgfVxuXG4gIC8qKiBHZXQgdGhlIG51bWJlciBvZiBoZWFsdGh5IGhvc3RzICovXG4gIGhlYWx0aHlIb3N0Q291bnQoKTogbnVtYmVyIHtcbiAgICBsZXQgY291bnQgPSAwO1xuICAgIGZvciAoY29uc3QgY29ubiBvZiB0aGlzLmNvbm5lY3Rpb25zLnZhbHVlcygpKSB7XG4gICAgICBpZiAoY29ubi5oZWFsdGh5KSBjb3VudCsrO1xuICAgIH1cbiAgICByZXR1cm4gY291bnQ7XG4gIH1cblxuICAvKiogR2V0IHRoZSBsaXN0IG9mIGNvbmZpZ3VyZWQgaG9zdHMgKi9cbiAgaG9zdHMoKTogc3RyaW5nW10ge1xuICAgIHJldHVybiBBcnJheS5mcm9tKHRoaXMuY29ubmVjdGlvbnMua2V5cygpKTtcbiAgfVxuXG4gIC8qKiBDaGVjayBpZiB0aGUgcG9vbCBpcyBjbG9zZWQgKi9cbiAgaXNDbG9zZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuY2xvc2VkO1xuICB9XG59XG4iXX0=