@ultipa-graph/ultipa-driver
Version:
NodeJS SDK for Ultipa GQL
234 lines • 30.4 kB
JavaScript
;
/**
* Query service handles GQL query execution.
*/
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.QueryService = void 0;
const grpc = __importStar(require("@grpc/grpc-js"));
const response_1 = require("../response");
const errors_1 = require("../errors");
const services_1 = require("../services");
const converters_1 = require("./converters");
/**
* Match a SINGLE `USE GRAPH <ident>` statement (case-insensitive) with
* optional trailing whitespace and semicolons. Compound queries do not
* match — see comment in QueryService.gql below.
*/
const USE_GRAPH_RE = /^\s*USE\s+GRAPH\s+(\S+?)\s*;*\s*$/i;
/**
* Query service for executing GQL queries.
*/
class QueryService {
ctx;
constructor(ctx) {
this.ctx = ctx;
}
/**
* Calculate timeout with the following priority:
* 1. QueryConfig.timeout (highest priority) - explicitly specified timeout
* 2. Context deadline (medium priority) - remaining time from context deadline
* 3. client.config.timeout (default) - default timeout from client config
*
* @param config Optional query configuration
* @returns Timeout in milliseconds
*/
calculateTimeout(config) {
// Priority 1: Use explicitly specified timeout from QueryConfig
if (config?.timeout && config.timeout > 0) {
return config.timeout;
}
// Priority 2: Context deadline support
// Note: Node.js SDK doesn't currently expose context deadline parameter
// This is left for future enhancement
// Priority 3: Use default timeout from client config
return this.ctx.config.timeout || 30000;
}
/**
* Execute a GQL query and return the result.
*
* Falls back to GqlStream + client-side aggregation when the server
* rejects the result set as too large for non-streaming RPC
* (RESOURCE_EXHAUSTED with "use streaming API" detail).
*/
async gql(query, config) {
if (!query) {
throw new errors_1.EmptyQueryError();
}
const session = this.ctx.sessions.getSession();
const request = (0, converters_1.buildGqlRequest)(query, session, this.ctx.config, config);
const timeoutMs = this.calculateTimeout(config);
try {
const metadata = this.ctx.getSessionMetadata();
const response = await (0, services_1.promisifyCallWithDeadline)(this.ctx.clients.queryService, 'Gql', request, timeoutMs, metadata);
this.ctx.updateActivity();
// Phase 2 dual-source cache update: prefer the server's authoritative
// current_graph (covers compound queries, multiple embedded
// USE GRAPH, and last-write-wins) and fall back to the strict
// client-side regex / DROP GRAPH detection for older servers that
// don't populate the field.
const serverCurrent = response.current_graph || '';
if (serverCurrent) {
this.ctx.sessions.setDefaultGraph(serverCurrent);
}
else {
const m = query.match(USE_GRAPH_RE);
if (m) {
// Compound queries that merely *start* with `USE GRAPH` must
// NOT poison the default-graph state — only single-statement
// form matches.
this.ctx.sessions.setDefaultGraph(m[1]);
}
else {
// Round-21 #5: a successful DROP GRAPH X must clear our cached
// default_graph if it pointed at X. New servers handle this
// authoritatively via current_graph (returns "" after self-drop)
// — this branch only fires on older servers without the field.
const trimmed = query.trim();
if (trimmed.toUpperCase().startsWith('DROP GRAPH')) {
let rest = trimmed.substring('DROP GRAPH'.length).trimStart();
if (rest.toUpperCase().startsWith('IF EXISTS')) {
rest = rest.substring('IF EXISTS'.length).trimStart();
}
const dropped = rest.replace(/;+$/, '').trim().replace(/^[`"']+|[`"']+$/g, '');
const current = this.ctx.sessions.getDefaultGraph();
if (dropped && current && dropped === current) {
this.ctx.sessions.setDefaultGraph('');
}
}
}
}
return (0, converters_1.convertGqlResponse)(response);
}
catch (error) {
if (error?.code === grpc.status.RESOURCE_EXHAUSTED &&
typeof error?.details === 'string' &&
error.details.includes('use streaming API')) {
return this.gqlStreamCollect(query, config);
}
throw new errors_1.QueryFailedError(error.message || 'Query failed');
}
}
/**
* Run GqlStream and aggregate chunks into a single Response.
*
* Used as fallback from gql() when the server rejects the result set
* as too large for non-streaming RPC.
*/
async gqlStreamCollect(query, config) {
const chunks = [];
await this.gqlStream(query, config, (chunk) => {
chunks.push(chunk);
});
if (chunks.length === 0) {
return new response_1.Response([], [], 0, false, [], 0);
}
const columns = chunks[0].columns;
const rows = chunks.flatMap(c => c.rows);
const warnings = chunks.flatMap(c => c.warnings);
let rowsAffected = 0;
// Server populates timing / current_graph only on the final batch
// (has_more=false). Take latest non-zero values.
let currentGraph = '';
let timeCostNs = 0, diskCostNs = 0, computeCostNs = 0;
for (const c of chunks) {
if (c.rowsAffected)
rowsAffected = c.rowsAffected;
if (c.currentGraph)
currentGraph = c.currentGraph;
if (c.timeCostNs)
timeCostNs = c.timeCostNs;
if (c.diskCostNs)
diskCostNs = c.diskCostNs;
if (c.computeCostNs)
computeCostNs = c.computeCostNs;
}
return new response_1.Response(columns, rows, rows.length, false, warnings, rowsAffected, currentGraph, timeCostNs, diskCostNs, computeCostNs);
}
/**
* Execute a GQL query and stream the results.
*/
async gqlStream(query, config, callback) {
if (!query) {
throw new errors_1.EmptyQueryError();
}
const session = this.ctx.sessions.getSession();
const request = (0, converters_1.buildGqlRequest)(query, session, this.ctx.config, config);
const metadata = this.ctx.getSessionMetadata();
const timeoutMs = this.calculateTimeout(config);
const deadline = new Date(Date.now() + timeoutMs);
return new Promise((resolve, reject) => {
const stream = this.ctx.clients.queryService.GqlStream(request, metadata, { deadline });
stream.on('data', (response) => {
this.ctx.updateActivity();
if (callback) {
callback((0, converters_1.convertGqlResponse)(response));
}
});
stream.on('end', () => resolve());
stream.on('error', (error) => reject(new errors_1.QueryFailedError(error.message)));
});
}
/**
* Return the execution plan for a query.
*/
async explain(query, config) {
if (!query) {
throw new errors_1.EmptyQueryError();
}
const session = this.ctx.sessions.getSession();
const request = (0, converters_1.buildGqlRequest)(query, session, this.ctx.config, config);
const metadata = this.ctx.getSessionMetadata();
const timeoutMs = this.calculateTimeout(config);
const response = await (0, services_1.promisifyCallWithDeadline)(this.ctx.clients.queryService, 'Explain', request, timeoutMs, metadata);
return response.plan || '';
}
/**
* Execute a query with profiling and return statistics.
*/
async profile(query, config) {
if (!query) {
throw new errors_1.EmptyQueryError();
}
const session = this.ctx.sessions.getSession();
const request = (0, converters_1.buildGqlRequest)(query, session, this.ctx.config, config);
const metadata = this.ctx.getSessionMetadata();
const timeoutMs = this.calculateTimeout(config);
const response = await (0, services_1.promisifyCallWithDeadline)(this.ctx.clients.queryService, 'Profile', request, timeoutMs, metadata);
return response.profile || '';
}
}
exports.QueryService = QueryService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicXVlcnktc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlcy9xdWVyeS1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7R0FFRzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUgsb0RBQXNDO0FBRXRDLDBDQUF1QztBQUN2QyxzQ0FBOEQ7QUFDOUQsMENBQXdEO0FBQ3hELDZDQUdzQjtBQUd0Qjs7OztHQUlHO0FBQ0gsTUFBTSxZQUFZLEdBQUcsb0NBQW9DLENBQUM7QUFFMUQ7O0dBRUc7QUFDSCxNQUFhLFlBQVk7SUFDSDtJQUFwQixZQUFvQixHQUFtQjtRQUFuQixRQUFHLEdBQUgsR0FBRyxDQUFnQjtJQUFHLENBQUM7SUFFM0M7Ozs7Ozs7O09BUUc7SUFDSyxnQkFBZ0IsQ0FBQyxNQUFvQjtRQUMzQyxnRUFBZ0U7UUFDaEUsSUFBSSxNQUFNLEVBQUUsT0FBTyxJQUFJLE1BQU0sQ0FBQyxPQUFPLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDMUMsT0FBTyxNQUFNLENBQUMsT0FBTyxDQUFDO1FBQ3hCLENBQUM7UUFFRCx1Q0FBdUM7UUFDdkMsd0VBQXdFO1FBQ3hFLHNDQUFzQztRQUV0QyxxREFBcUQ7UUFDckQsT0FBTyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxPQUFPLElBQUksS0FBSyxDQUFDO0lBQzFDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSCxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQWEsRUFBRSxNQUFvQjtRQUMzQyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDWCxNQUFNLElBQUksd0JBQWUsRUFBRSxDQUFDO1FBQzlCLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUMvQyxNQUFNLE9BQU8sR0FBRyxJQUFBLDRCQUFlLEVBQUMsS0FBSyxFQUFFLE9B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U0FBUyxFQUNULE9BQU8sRUFDUCxTQUFTLEVBQ1QsUUFBUSxDQUNULENBQUM7UUFFRixPQUFPLFFBQVEsQ0FBQyxPQUFPLElBQUksRUFBRSxDQUFDO0lBQ2hDLENBQUM7Q0FDRjtBQTNORCxvQ0EyTkMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFF1ZXJ5IHNlcnZpY2UgaGFuZGxlcyBHUUwgcXVlcnkgZXhlY3V0aW9uLlxuICovXG5cbmltcG9ydCAqIGFzIGdycGMgZnJvbSAnQGdycGMvZ3JwYy1qcyc7XG5pbXBvcnQgeyBTZXJ2aWNlQ29udGV4dCB9IGZyb20gJy4vc2VydmljZS1jb250ZXh0JztcbmltcG9ydCB7IFJlc3BvbnNlIH0gZnJvbSAnLi4vcmVzcG9uc2UnO1xuaW1wb3J0IHsgRW1wdHlRdWVyeUVycm9yLCBRdWVyeUZhaWxlZEVycm9yIH0gZnJvbSAnLi4vZXJyb3JzJztcbmltcG9ydCB7IHByb21pc2lmeUNhbGxXaXRoRGVhZGxpbmUgfSBmcm9tICcuLi9zZXJ2aWNlcyc7XG5pbXBvcnQge1xuICBidWlsZEdxbFJlcXVlc3QsXG4gIGNvbnZlcnRHcWxSZXNwb25zZSxcbn0gZnJvbSAnLi9jb252ZXJ0ZXJzJztcbmltcG9ydCB7IFF1ZXJ5Q29uZmlnIH0gZnJvbSAnLi4vY2xpZW50JztcblxuLyoqXG4gKiBNYXRjaCBhIFNJTkdMRSBgVVNFIEdSQVBIIDxpZGVudD5gIHN0YXRlbWVudCAoY2FzZS1pbnNlbnNpdGl2ZSkgd2l0aFxuICogb3B0aW9uYWwgdHJhaWxpbmcgd2hpdGVzcGFjZSBhbmQgc2VtaWNvbG9ucy4gQ29tcG91bmQgcXVlcmllcyBkbyBub3RcbiAqIG1hdGNoIOKAlCBzZWUgY29tbWVudCBpbiBRdWVyeVNlcnZpY2UuZ3FsIGJlbG93LlxuICovXG5jb25zdCBVU0VfR1JBUEhfUkUgPSAvXlxccypVU0VcXHMrR1JBUEhcXHMrKFxcUys/KVxccyo7KlxccyokL2k7XG5cbi8qKlxuICogUXVlcnkgc2VydmljZSBmb3IgZXhlY3V0aW5nIEdRTCBxdWVyaWVzLlxuICovXG5leHBvcnQgY2xhc3MgUXVlcnlTZXJ2aWNlIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBjdHg6IFNlcnZpY2VDb250ZXh0KSB7fVxuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGUgdGltZW91dCB3aXRoIHRoZSBmb2xsb3dpbmcgcHJpb3JpdHk6XG4gICAqIDEuIFF1ZXJ5Q29uZmlnLnRpbWVvdXQgKGhpZ2hlc3QgcHJpb3JpdHkpIC0gZXhwbGljaXRseSBzcGVjaWZpZWQgdGltZW91dFxuICAgKiAyLiBDb250ZXh0IGRlYWRsaW5lIChtZWRpdW0gcHJpb3JpdHkpIC0gcmVtYWluaW5nIHRpbWUgZnJvbSBjb250ZXh0IGRlYWRsaW5lXG4gICAqIDMuIGNsaWVudC5jb25maWcudGltZW91dCAoZGVmYXVsdCkgLSBkZWZhdWx0IHRpbWVvdXQgZnJvbSBjbGllbnQgY29uZmlnXG4gICAqXG4gICAqIEBwYXJhbSBjb25maWcgT3B0aW9uYWwgcXVlcnkgY29uZmlndXJhdGlvblxuICAgKiBAcmV0dXJucyBUaW1lb3V0IGluIG1pbGxpc2Vjb25kc1xuICAgKi9cbiAgcHJpdmF0ZSBjYWxjdWxhdGVUaW1lb3V0KGNvbmZpZz86IFF1ZXJ5Q29uZmlnKTogbnVtYmVyIHtcbiAgICAvLyBQcmlvcml0eSAxOiBVc2UgZXhwbGljaXRseSBzcGVjaWZpZWQgdGltZW91dCBmcm9tIFF1ZXJ5Q29uZmlnXG4gICAgaWYgKGNvbmZpZz8udGltZW91dCAmJiBjb25maWcudGltZW91dCA+IDApIHtcbiAgICAgIHJldHVybiBjb25maWcudGltZW91dDtcbiAgICB9XG5cbiAgICAvLyBQcmlvcml0eSAyOiBDb250ZXh0IGRlYWRsaW5lIHN1cHBvcnRcbiAgICAvLyBOb3RlOiBOb2RlLmpzIFNESyBkb2Vzbid0IGN1cnJlbnRseSBleHBvc2UgY29udGV4dCBkZWFkbGluZSBwYXJhbWV0ZXJcbiAgICAvLyBUaGlzIGlzIGxlZnQgZm9yIGZ1dHVyZSBlbmhhbmNlbWVudFxuXG4gICAgLy8gUHJpb3JpdHkgMzogVXNlIGRlZmF1bHQgdGltZW91dCBmcm9tIGNsaWVudCBjb25maWdcbiAgICByZXR1cm4gdGhpcy5jdHguY29uZmlnLnRpbWVvdXQgfHwgMzAwMDA7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSBhIEdRTCBxdWVyeSBhbmQgcmV0dXJuIHRoZSByZXN1bHQuXG4gICAqXG4gICAqIEZhbGxzIGJhY2sgdG8gR3FsU3RyZWFtICsgY2xpZW50LXNpZGUgYWdncmVnYXRpb24gd2hlbiB0aGUgc2VydmVyXG4gICAqIHJlamVjdHMgdGhlIHJlc3VsdCBzZXQgYXMgdG9vIGxhcmdlIGZvciBub24tc3RyZWFtaW5nIFJQQ1xuICAgKiAoUkVTT1VSQ0VfRVhIQVVTVEVEIHdpdGggXCJ1c2Ugc3RyZWFtaW5nIEFQSVwiIGRldGFpbCkuXG4gICAqL1xuICBhc3luYyBncWwocXVlcnk6IHN0cmluZywgY29uZmlnPzogUXVlcnlDb25maWcpOiBQcm9taXNlPFJlc3BvbnNlPiB7XG4gICAgaWYgKCFxdWVyeSkge1xuICAgICAgdGhyb3cgbmV3IEVtcHR5UXVlcnlFcnJvcigpO1xuICAgIH1cblxuICAgIGNvbnN0IHNlc3Npb24gPSB0aGlzLmN0eC5zZXNzaW9ucy5nZXRTZXNzaW9uKCk7XG4gICAgY29uc3QgcmVxdWVzdCA9IGJ1aWxkR3FsUmVxdWVzdChxdWVyeSwgc2Vzc2lvbiwgdGhpcy5jdHguY29uZmlnLCBjb25maWcpO1xuICAgIGNvbnN0IHRpbWVvdXRNcyA9IHRoaXMuY2FsY3VsYXRlVGltZW91dChjb25maWcpO1xuXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IG1ldGFkYXRhID0gdGhpcy5jdHguZ2V0U2Vzc2lvbk1ldGFkYXRhKCk7XG4gICAgICBjb25zdCByZXNwb25zZTogYW55ID0gYXdhaXQgcHJvbWlzaWZ5Q2FsbFdpdGhEZWFkbGluZShcbiAgICAgICAgdGhpcy5jdHguY2xpZW50cy5xdWVyeVNlcnZpY2UsXG4gICAgICAgICdHcWwnLFxuICAgICAgICByZXF1ZXN0LFxuICAgICAgICB0aW1lb3V0TXMsXG4gICAgICAgIG1ldGFkYXRhXG4gICAgICApO1xuXG4gICAgICB0aGlzLmN0eC51cGRhdGVBY3Rpdml0eSgpO1xuXG4gICAgICAvLyBQaGFzZSAyIGR1YWwtc291cmNlIGNhY2hlIHVwZGF0ZTogcHJlZmVyIHRoZSBzZXJ2ZXIncyBhdXRob3JpdGF0aXZlXG4gICAgICAvLyBjdXJyZW50X2dyYXBoIChjb3ZlcnMgY29tcG91bmQgcXVlcmllcywgbXVsdGlwbGUgZW1iZWRkZWRcbiAgICAgIC8vIFVTRSBHUkFQSCwgYW5kIGxhc3Qtd3JpdGUtd2lucykgYW5kIGZhbGwgYmFjayB0byB0aGUgc3RyaWN0XG4gICAgICAvLyBjbGllbnQtc2lkZSByZWdleCAvIERST1AgR1JBUEggZGV0ZWN0aW9uIGZvciBvbGRlciBzZXJ2ZXJzIHRoYXRcbiAgICAgIC8vIGRvbid0IHBvcHVsYXRlIHRoZSBmaWVsZC5cbiAgICAgIGNvbnN0IHNlcnZlckN1cnJlbnQ6IHN0cmluZyA9IChyZXNwb25zZSBhcyBhbnkpLmN1cnJlbnRfZ3JhcGggfHwgJyc7XG4gICAgICBpZiAoc2VydmVyQ3VycmVudCkge1xuICAgICAgICB0aGlzLmN0eC5zZXNzaW9ucy5zZXREZWZhdWx0R3JhcGgoc2VydmVyQ3VycmVudCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCBtID0gcXVlcnkubWF0Y2goVVNFX0dSQVBIX1JFKTtcbiAgICAgICAgaWYgKG0pIHtcbiAgICAgICAgICAvLyBDb21wb3VuZCBxdWVyaWVzIHRoYXQgbWVyZWx5ICpzdGFydCogd2l0aCBgVVNFIEdSQVBIYCBtdXN0XG4gICAgICAgICAgLy8gTk9UIHBvaXNvbiB0aGUgZGVmYXVsdC1ncmFwaCBzdGF0ZSDigJQgb25seSBzaW5nbGUtc3RhdGVtZW50XG4gICAgICAgICAgLy8gZm9ybSBtYXRjaGVzLlxuICAgICAgICAgIHRoaXMuY3R4LnNlc3Npb25zLnNldERlZmF1bHRHcmFwaChtWzFdKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSb3VuZC0yMSAjNTogYSBzdWNjZXNzZnVsIERST1AgR1JBUEggWCBtdXN0IGNsZWFyIG91ciBjYWNoZWRcbiAgICAgICAgICAvLyBkZWZhdWx0X2dyYXBoIGlmIGl0IHBvaW50ZWQgYXQgWC4gTmV3IHNlcnZlcnMgaGFuZGxlIHRoaXNcbiAgICAgICAgICAvLyBhdXRob3JpdGF0aXZlbHkgdmlhIGN1cnJlbnRfZ3JhcGggKHJldHVybnMgXCJcIiBhZnRlciBzZWxmLWRyb3ApXG4gICAgICAgICAgLy8g4oCUIHRoaXMgYnJhbmNoIG9ubHkgZmlyZXMgb24gb2xkZXIgc2VydmVycyB3aXRob3V0IHRoZSBmaWVsZC5cbiAgICAgICAgICBjb25zdCB0cmltbWVkID0gcXVlcnkudHJpbSgpO1xuICAgICAgICAgIGlmICh0cmltbWVkLnRvVXBwZXJDYXNlKCkuc3RhcnRzV2l0aCgnRFJPUCBHUkFQSCcpKSB7XG4gICAgICAgICAgICBsZXQgcmVzdCA9IHRyaW1tZWQuc3Vic3RyaW5nKCdEUk9QIEdSQVBIJy5sZW5ndGgpLnRyaW1TdGFydCgpO1xuICAgICAgICAgICAgaWYgKHJlc3QudG9VcHBlckNhc2UoKS5zdGFydHNXaXRoKCdJRiBFWElTVFMnKSkge1xuICAgICAgICAgICAgICByZXN0ID0gcmVzdC5zdWJzdHJpbmcoJ0lGIEVYSVNUUycubGVuZ3RoKS50cmltU3RhcnQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IGRyb3BwZWQgPSByZXN0LnJlcGxhY2UoLzsrJC8sICcnKS50cmltKCkucmVwbGFjZSgvXltgXCInXSt8W2BcIiddKyQvZywgJycpO1xuICAgICAgICAgICAgY29uc3QgY3VycmVudCA9IHRoaXMuY3R4LnNlc3Npb25zLmdldERlZmF1bHRHcmFwaCgpO1xuICAgICAgICAgICAgaWYgKGRyb3BwZWQgJiYgY3VycmVudCAmJiBkcm9wcGVkID09PSBjdXJyZW50KSB7XG4gICAgICAgICAgICAgIHRoaXMuY3R4LnNlc3Npb25zLnNldERlZmF1bHRHcmFwaCgnJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBjb252ZXJ0R3FsUmVzcG9uc2UocmVzcG9uc2UpO1xuICAgIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICAgIGlmIChcbiAgICAgICAgZXJyb3I/LmNvZGUgPT09IGdycGMuc3RhdHVzLlJFU09VUkNFX0VYSEFVU1RFRCAmJlxuICAgICAgICB0eXBlb2YgZXJyb3I/LmRldGFpbHMgPT09ICdzdHJpbmcnICYmXG4gICAgICAgIGVycm9yLmRldGFpbHMuaW5jbHVkZXMoJ3VzZSBzdHJlYW1pbmcgQVBJJylcbiAgICAgICkge1xuICAgICAgICByZXR1cm4gdGhpcy5ncWxTdHJlYW1Db2xsZWN0KHF1ZXJ5LCBjb25maWcpO1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IFF1ZXJ5RmFpbGVkRXJyb3IoZXJyb3IubWVzc2FnZSB8fCAnUXVlcnkgZmFpbGVkJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJ1biBHcWxTdHJlYW0gYW5kIGFnZ3JlZ2F0ZSBjaHVua3MgaW50byBhIHNpbmdsZSBSZXNwb25zZS5cbiAgICpcbiAgICogVXNlZCBhcyBmYWxsYmFjayBmcm9tIGdxbCgpIHdoZW4gdGhlIHNlcnZlciByZWplY3RzIHRoZSByZXN1bHQgc2V0XG4gICAqIGFzIHRvbyBsYXJnZSBmb3Igbm9uLXN0cmVhbWluZyBSUEMuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGdxbFN0cmVhbUNvbGxlY3QocXVlcnk6IHN0cmluZywgY29uZmlnPzogUXVlcnlDb25maWcpOiBQcm9taXNlPFJlc3BvbnNlPiB7XG4gICAgY29uc3QgY2h1bmtzOiBSZXNwb25zZVtdID0gW107XG4gICAgYXdhaXQgdGhpcy5ncWxTdHJlYW0ocXVlcnksIGNvbmZpZywgKGNodW5rOiBSZXNwb25zZSkgPT4ge1xuICAgICAgY2h1bmtzLnB1c2goY2h1bmspO1xuICAgIH0pO1xuXG4gICAgaWYgKGNodW5rcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoW10sIFtdLCAwLCBmYWxzZSwgW10sIDApO1xuICAgIH1cblxuICAgIGNvbnN0IGNvbHVtbnMgPSBjaHVua3NbMF0uY29sdW1ucztcbiAgICBjb25zdCByb3dzID0gY2h1bmtzLmZsYXRNYXAoYyA9PiBjLnJvd3MpO1xuICAgIGNvbnN0IHdhcm5pbmdzID0gY2h1bmtzLmZsYXRNYXAoYyA9PiBjLndhcm5pbmdzKTtcbiAgICBsZXQgcm93c0FmZmVjdGVkID0gMDtcbiAgICAvLyBTZXJ2ZXIgcG9wdWxhdGVzIHRpbWluZyAvIGN1cnJlbnRfZ3JhcGggb25seSBvbiB0aGUgZmluYWwgYmF0Y2hcbiAgICAvLyAoaGFzX21vcmU9ZmFsc2UpLiBUYWtlIGxhdGVzdCBub24temVybyB2YWx1ZXMuXG4gICAgbGV0IGN1cnJlbnRHcmFwaCA9ICcnO1xuICAgIGxldCB0aW1lQ29zdE5zID0gMCwgZGlza0Nvc3ROcyA9IDAsIGNvbXB1dGVDb3N0TnMgPSAwO1xuICAgIGZvciAoY29uc3QgYyBvZiBjaHVua3MpIHtcbiAgICAgIGlmIChjLnJvd3NBZmZlY3RlZCkgcm93c0FmZmVjdGVkID0gYy5yb3dzQWZmZWN0ZWQ7XG4gICAgICBpZiAoYy5jdXJyZW50R3JhcGgpIGN1cnJlbnRHcmFwaCA9IGMuY3VycmVudEdyYXBoO1xuICAgICAgaWYgKGMudGltZUNvc3ROcykgdGltZUNvc3ROcyA9IGMudGltZUNvc3ROcztcbiAgICAgIGlmIChjLmRpc2tDb3N0TnMpIGRpc2tDb3N0TnMgPSBjLmRpc2tDb3N0TnM7XG4gICAgICBpZiAoYy5jb21wdXRlQ29zdE5zKSBjb21wdXRlQ29zdE5zID0gYy5jb21wdXRlQ29zdE5zO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IFJlc3BvbnNlKGNvbHVtbnMsIHJvd3MsIHJvd3MubGVuZ3RoLCBmYWxzZSwgd2FybmluZ3MsXG4gICAgICByb3dzQWZmZWN0ZWQsIGN1cnJlbnRHcmFwaCwgdGltZUNvc3ROcywgZGlza0Nvc3ROcywgY29tcHV0ZUNvc3ROcyk7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSBhIEdRTCBxdWVyeSBhbmQgc3RyZWFtIHRoZSByZXN1bHRzLlxuICAgKi9cbiAgYXN5bmMgZ3FsU3RyZWFtKFxuICAgIHF1ZXJ5OiBzdHJpbmcsXG4gICAgY29uZmlnPzogUXVlcnlDb25maWcsXG4gICAgY2FsbGJhY2s/OiAocmVzcG9uc2U6IFJlc3BvbnNlKSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghcXVlcnkpIHtcbiAgICAgIHRocm93IG5ldyBFbXB0eVF1ZXJ5RXJyb3IoKTtcbiAgICB9XG5cbiAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5jdHguc2Vzc2lvbnMuZ2V0U2Vzc2lvbigpO1xuICAgIGNvbnN0IHJlcXVlc3QgPSBidWlsZEdxbFJlcXVlc3QocXVlcnksIHNlc3Npb24sIHRoaXMuY3R4LmNvbmZpZywgY29uZmlnKTtcbiAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMuY3R4LmdldFNlc3Npb25NZXRhZGF0YSgpO1xuICAgIGNvbnN0IHRpbWVvdXRNcyA9IHRoaXMuY2FsY3VsYXRlVGltZW91dChjb25maWcpO1xuICAgIGNvbnN0IGRlYWRsaW5lID0gbmV3IERhdGUoRGF0ZS5ub3coKSArIHRpbWVvdXRNcyk7XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3Qgc3RyZWFtID0gKHRoaXMuY3R4LmNsaWVudHMucXVlcnlTZXJ2aWNlIGFzIGFueSkuR3FsU3RyZWFtKFxuICAgICAgICByZXF1ZXN0LFxuICAgICAgICBtZXRhZGF0YSxcbiAgICAgICAgeyBkZWFkbGluZSB9XG4gICAgICApO1xuXG4gICAgICBzdHJlYW0ub24oJ2RhdGEnLCAocmVzcG9uc2U6IGFueSkgPT4ge1xuICAgICAgICB0aGlzLmN0eC51cGRhdGVBY3Rpdml0eSgpO1xuICAgICAgICBpZiAoY2FsbGJhY2spIHtcbiAgICAgICAgICBjYWxsYmFjayhjb252ZXJ0R3FsUmVzcG9uc2UocmVzcG9uc2UpKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIHN0cmVhbS5vbignZW5kJywgKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICAgIHN0cmVhbS5vbignZXJyb3InLCAoZXJyb3I6IGFueSkgPT4gcmVqZWN0KG5ldyBRdWVyeUZhaWxlZEVycm9yKGVycm9yLm1lc3NhZ2UpKSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBleGVjdXRpb24gcGxhbiBmb3IgYSBxdWVyeS5cbiAgICovXG4gIGFzeW5jIGV4cGxhaW4ocXVlcnk6IHN0cmluZywgY29uZmlnPzogUXVlcnlDb25maWcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGlmICghcXVlcnkpIHtcbiAgICAgIHRocm93IG5ldyBFbXB0eVF1ZXJ5RXJyb3IoKTtcbiAgICB9XG5cbiAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5jdHguc2Vzc2lvbnMuZ2V0U2Vzc2lvbigpO1xuICAgIGNvbnN0IHJlcXVlc3QgPSBidWlsZEdxbFJlcXVlc3QocXVlcnksIHNlc3Npb24sIHRoaXMuY3R4LmNvbmZpZywgY29uZmlnKTtcbiAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMuY3R4LmdldFNlc3Npb25NZXRhZGF0YSgpO1xuICAgIGNvbnN0IHRpbWVvdXRNcyA9IHRoaXMuY2FsY3VsYXRlVGltZW91dChjb25maWcpO1xuICAgIGNvbnN0IHJlc3BvbnNlOiBhbnkgPSBhd2FpdCBwcm9taXNpZnlDYWxsV2l0aERlYWRsaW5lKFxuICAgICAgdGhpcy5jdHguY2xpZW50cy5xdWVyeVNlcnZpY2UsXG4gICAgICAnRXhwbGFpbicsXG4gICAgICByZXF1ZXN0LFxuICAgICAgdGltZW91dE1zLFxuICAgICAgbWV0YWRhdGFcbiAgICApO1xuXG4gICAgcmV0dXJuIHJlc3BvbnNlLnBsYW4gfHwgJyc7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSBhIHF1ZXJ5IHdpdGggcHJvZmlsaW5nIGFuZCByZXR1cm4gc3RhdGlzdGljcy5cbiAgICovXG4gIGFzeW5jIHByb2ZpbGUocXVlcnk6IHN0cmluZywgY29uZmlnPzogUXVlcnlDb25maWcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIGlmICghcXVlcnkpIHtcbiAgICAgIHRocm93IG5ldyBFbXB0eVF1ZXJ5RXJyb3IoKTtcbiAgICB9XG5cbiAgICBjb25zdCBzZXNzaW9uID0gdGhpcy5jdHguc2Vzc2lvbnMuZ2V0U2Vzc2lvbigpO1xuICAgIGNvbnN0IHJlcXVlc3QgPSBidWlsZEdxbFJlcXVlc3QocXVlcnksIHNlc3Npb24sIHRoaXMuY3R4LmNvbmZpZywgY29uZmlnKTtcbiAgICBjb25zdCBtZXRhZGF0YSA9IHRoaXMuY3R4LmdldFNlc3Npb25NZXRhZGF0YSgpO1xuICAgIGNvbnN0IHRpbWVvdXRNcyA9IHRoaXMuY2FsY3VsYXRlVGltZW91dChjb25maWcpO1xuICAgIGNvbnN0IHJlc3BvbnNlOiBhbnkgPSBhd2FpdCBwcm9taXNpZnlDYWxsV2l0aERlYWRsaW5lKFxuICAgICAgdGhpcy5jdHguY2xpZW50cy5xdWVyeVNlcnZpY2UsXG4gICAgICAnUHJvZmlsZScsXG4gICAgICByZXF1ZXN0LFxuICAgICAgdGltZW91dE1zLFxuICAgICAgbWV0YWRhdGFcbiAgICApO1xuXG4gICAgcmV0dXJuIHJlc3BvbnNlLnByb2ZpbGUgfHwgJyc7XG4gIH1cbn1cbiJdfQ==