UNPKG

gremlin-secure

Version:

Private version of the gremlin websocket client module: https://github.com/jbmusso/gremlin-javascript. This private version supports SSL and SASL authentication, and is undergoing a PR with the main module. This package is created as a private, temporary

482 lines (379 loc) 39.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _events = require('events'); var _nodeUuid = require('node-uuid'); var _nodeUuid2 = _interopRequireDefault(_nodeUuid); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _highland = require('highland'); var _highland2 = _interopRequireDefault(_highland); var _WebSocketGremlinConnection = require('./WebSocketGremlinConnection'); var _WebSocketGremlinConnection2 = _interopRequireDefault(_WebSocketGremlinConnection); var _MessageStream = require('./MessageStream'); var _MessageStream2 = _interopRequireDefault(_MessageStream); var _executeHandler = require('./executeHandler'); var _executeHandler2 = _interopRequireDefault(_executeHandler); var _utils = require('./utils'); var Utils = _interopRequireWildcard(_utils); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /*jslint -W079 */ /*jslint node: true */ var GremlinClient = function (_EventEmitter) { _inherits(GremlinClient, _EventEmitter); function GremlinClient() { var port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 8182; var host = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'localhost'; var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; _classCallCheck(this, GremlinClient); var _this = _possibleConstructorReturn(this, (GremlinClient.__proto__ || Object.getPrototypeOf(GremlinClient)).call(this)); _this.port = port; _this.host = host; var _options$path = options.path, path = _options$path === undefined ? '' : _options$path; _this.options = _extends({ language: 'gremlin-groovy', session: false, ssl: false, user: '', password: '', op: 'eval', processor: '', accept: 'application/json', executeHandler: _executeHandler2.default }, options, { path: path && path.length && !path.startsWith('/') ? '/' + path : path }); _this.useSession = _this.options.session; _this.user = _this.options.user; _this.password = _this.options.password; _this.ssl = _this.options.ssl; if (_this.useSession) { _this.sessionId = _nodeUuid2.default.v1(); } _this.connected = false; _this.queue = []; _this.commands = {}; _this.connection = _this.createConnection({ port: port, host: host, path: _this.options.path, ssl: _this.ssl }); return _this; } _createClass(GremlinClient, [{ key: 'createConnection', value: function createConnection(_ref) { var _this2 = this; var port = _ref.port, host = _ref.host, path = _ref.path, ssl = _ref.ssl; var connection = new _WebSocketGremlinConnection2.default({ port: port, host: host, path: path, ssl: ssl }); connection.on('open', function () { return _this2.onConnectionOpen(); }); connection.on('error', function (error) { return _this2.handleError(error); }); connection.on('message', function (message) { return _this2.handleProtocolMessage(message); }); connection.on('close', function (event) { return _this2.handleDisconnection(event); }); return connection; } }, { key: 'handleError', value: function handleError(err) { this.connected = false; this.emit('error', err); } /** * Process all incoming raw message events sent by Gremlin Server, and dispatch * to the appropriate command. * * @param {MessageEvent} event */ }, { key: 'handleProtocolMessage', value: function handleProtocolMessage(message) { var data = message.data; var buffer = new Buffer(data, 'binary'); var rawMessage = JSON.parse(buffer.toString('utf-8')); var requestId = rawMessage.requestId, _rawMessage$status = rawMessage.status, statusCode = _rawMessage$status.code, statusMessage = _rawMessage$status.message; var messageStream = this.commands[requestId].messageStream; switch (statusCode) { case 200: // SUCCESS delete this.commands[requestId]; // TODO: optimize performance messageStream.push(rawMessage); messageStream.push(null); break; case 204: // NO_CONTENT delete this.commands[requestId]; messageStream.push(null); break; case 206: // PARTIAL_CONTENT messageStream.push(rawMessage); break; case 407: // AUTHENTICATE CHALLANGE var challengeResponse = this.buildChallengeResponse(requestId); this.sendMessage(challengeResponse); break; default: delete this.commands[requestId]; messageStream.emit('error', new Error(statusMessage + ' (Error ' + statusCode + ')')); break; } } /** * Handle the WebSocket onOpen event, flag the client as connected and * process command queue. */ }, { key: 'onConnectionOpen', value: function onConnectionOpen() { this.connected = true; this.emit('connect'); this.executeQueue(); } }, { key: 'handleDisconnection', /** * @param {CloseEvent} event */ value: function handleDisconnection(event) { this.cancelPendingCommands({ message: 'WebSocket closed', details: event }); } }, { key: 'executeQueue', /** * Process the current command queue, sending commands to Gremlin Server * (First In, First Out). */ value: function executeQueue() { while (this.queue.length > 0) { var _queue$shift = this.queue.shift(), message = _queue$shift.message; this.sendMessage(message); } } }, { key: 'cancelPendingCommands', /** * @param {Object} reason */ value: function cancelPendingCommands(_ref2) { var message = _ref2.message, details = _ref2.details; var commands = this.commands; var command = void 0; var error = new Error(message); error.details = details; // Empty queue this.queue.length = 0; this.commands = {}; Object.keys(commands).forEach(function (key) { command = commands[key]; command.messageStream.emit('error', error); }); } }, { key: 'buildMessage', /** * For a given script string and optional bound parameters, build a protocol * message object to be sent to Gremlin Server. * * @param {String|Function} script * @param {Object} bindings * @param {Object} message */ value: function buildMessage(rawScript) { var rawBindings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var baseMessage = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _Utils$buildQueryFrom = Utils.buildQueryFromSignature(rawScript, rawBindings), gremlin = _Utils$buildQueryFrom.gremlin, bindings = _Utils$buildQueryFrom.bindings; var _options = this.options, processor = _options.processor, op = _options.op, accept = _options.accept, language = _options.language, aliases = _options.aliases; var baseArgs = { gremlin: gremlin, bindings: bindings, accept: accept, language: language, aliases: aliases }; var args = _lodash2.default.defaults(baseMessage.args || {}, baseArgs); var message = _extends({ requestId: _nodeUuid2.default.v1(), processor: processor, op: op, args: args }, baseMessage); if (this.useSession) { // Assume that people want to use the 'session' processor unless specified message.processor = message.processor || processor || 'session'; message.args.session = this.sessionId; } return message; } }, { key: 'buildChallengeResponse', value: function buildChallengeResponse(requestId) { var _options2 = this.options, processor = _options2.processor, op = _options2.op, accept = _options2.accept, language = _options2.language, aliases = _options2.aliases; var args = { SASL: '\0' + this.user + '\0' + this.password }; var message = { requestId: requestId, processor: processor, op: 'authentication', args: args }; return message; } }, { key: 'sendMessage', value: function sendMessage(message) { var serializedMessage = this.options.accept + JSON.stringify(message); serializedMessage = unescape(encodeURIComponent(serializedMessage)); // Let's start packing the message into binary // mimeLength(1) + mimeType Length + serializedMessage Length var binaryMessage = new Uint8Array(1 + serializedMessage.length); binaryMessage[0] = this.options.accept.length; for (var i = 0; i < serializedMessage.length; i++) { binaryMessage[i + 1] = serializedMessage.charCodeAt(i); } this.connection.sendMessage(binaryMessage); } }, { key: 'execute', /** * Asynchronously send a script to Gremlin Server for execution and fire * the provided callback when all results have been fetched. * * This method internally uses a stream to handle the potential concatenation * of results. * * Callback signature: (Error, Array<result>) * * @public * @param {String|Function} script * @param {Object} bindings * @param {Object} message * @param {Function} callback */ value: function execute(script) { var bindings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var _ref3; var message = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var callback = (_ref3 = (arguments.length <= 3 ? 0 : arguments.length - 3) - 1 + 3, arguments.length <= _ref3 ? undefined : arguments[_ref3]); if (typeof message === 'function') { callback = message; message = {}; } var messageStream = this.messageStream(script, bindings, message); // TO CHECK: errors handling could be improved // See https://groups.google.com/d/msg/nodejs/lJYT9hZxFu0/L59CFbqWGyYJ // for an example using domains var executeHandler = this.options.executeHandler; executeHandler(messageStream, callback); } /** * Execute the script and return a stream of distinct/single results. * This method reemits a distinct data event for each returned result, which * makes the stream behave as if `resultIterationBatchSize` was set to 1. * * If you do not wish this behavior, please use client.messageStream() instead. * * Even though this method uses Highland.js internally, it does not return * a high level Highland readable stream so we do not risk having to deal * with unexpected API breaking changes as Highland.js evolves. * * @return {ReadableStream} A Node.js Stream2 */ }, { key: 'stream', value: function stream(script, bindings, message) { var messageStream = this.messageStream(script, bindings, message); var _ = _highland2.default; // override lo-dash locally // Create a local highland 'through' pipeline so we don't expose // a Highland stream to the end user, but a standard Node.js Stream2 var through = _.pipeline(_.map(function (_ref4) { var data = _ref4.result.data; return data; }), _.sequence()); var rawStream = messageStream.pipe(through); messageStream.on('error', function (e) { rawStream.emit('error', new Error(e)); }); return rawStream; } }, { key: 'messageStream', /** * Execute the script and return a stream of raw messages returned by Gremlin * Server. * This method does not reemit one distinct data event per result. It directly * emits the raw messages returned by Gremlin Server as they are received. * * Although public, this is a low level method intended to be used for * advanced usages. * * @public * @param {String|Function} script * @param {Object} bindings * @param {Object} message * @return {MessageStream} */ value: function messageStream(script, bindings, rawMessage) { var stream = new _MessageStream2.default({ objectMode: true }); var command = { message: this.buildMessage(script, bindings, rawMessage), messageStream: stream }; this.sendCommand(command); //todo improve for streams return stream; } }, { key: 'sendCommand', /** * Send a command to Gremlin Server, or add it to queue if the connection * is not established. * * @param {Object} command */ value: function sendCommand(command) { var message = command.message, requestId = command.message.requestId; this.commands[requestId] = command; if (this.connected) { this.sendMessage(message); } else { this.queue.push(command); } } }]); return GremlinClient; }(_events.EventEmitter); exports.default = GremlinClient; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9HcmVtbGluQ2xpZW50LmpzIl0sIm5hbWVzIjpbIlV0aWxzIiwiR3JlbWxpbkNsaWVudCIsInBvcnQiLCJob3N0Iiwib3B0aW9ucyIsInBhdGgiLCJsYW5ndWFnZSIsInNlc3Npb24iLCJzc2wiLCJ1c2VyIiwicGFzc3dvcmQiLCJvcCIsInByb2Nlc3NvciIsImFjY2VwdCIsImV4ZWN1dGVIYW5kbGVyIiwibGVuZ3RoIiwic3RhcnRzV2l0aCIsInVzZVNlc3Npb24iLCJzZXNzaW9uSWQiLCJ2MSIsImNvbm5lY3RlZCIsInF1ZXVlIiwiY29tbWFuZHMiLCJjb25uZWN0aW9uIiwiY3JlYXRlQ29ubmVjdGlvbiIsIm9uIiwib25Db25uZWN0aW9uT3BlbiIsImVycm9yIiwiaGFuZGxlRXJyb3IiLCJtZXNzYWdlIiwiaGFuZGxlUHJvdG9jb2xNZXNzYWdlIiwiZXZlbnQiLCJoYW5kbGVEaXNjb25uZWN0aW9uIiwiZXJyIiwiZW1pdCIsImRhdGEiLCJidWZmZXIiLCJCdWZmZXIiLCJyYXdNZXNzYWdlIiwiSlNPTiIsInBhcnNlIiwidG9TdHJpbmciLCJyZXF1ZXN0SWQiLCJzdGF0dXMiLCJzdGF0dXNDb2RlIiwiY29kZSIsInN0YXR1c01lc3NhZ2UiLCJtZXNzYWdlU3RyZWFtIiwicHVzaCIsImNoYWxsZW5nZVJlc3BvbnNlIiwiYnVpbGRDaGFsbGVuZ2VSZXNwb25zZSIsInNlbmRNZXNzYWdlIiwiRXJyb3IiLCJleGVjdXRlUXVldWUiLCJjYW5jZWxQZW5kaW5nQ29tbWFuZHMiLCJkZXRhaWxzIiwic2hpZnQiLCJjb21tYW5kIiwiT2JqZWN0Iiwia2V5cyIsImZvckVhY2giLCJrZXkiLCJyYXdTY3JpcHQiLCJyYXdCaW5kaW5ncyIsImJhc2VNZXNzYWdlIiwiYnVpbGRRdWVyeUZyb21TaWduYXR1cmUiLCJncmVtbGluIiwiYmluZGluZ3MiLCJhbGlhc2VzIiwiYmFzZUFyZ3MiLCJhcmdzIiwiZGVmYXVsdHMiLCJTQVNMIiwic2VyaWFsaXplZE1lc3NhZ2UiLCJzdHJpbmdpZnkiLCJ1bmVzY2FwZSIsImVuY29kZVVSSUNvbXBvbmVudCIsImJpbmFyeU1lc3NhZ2UiLCJVaW50OEFycmF5IiwiaSIsImNoYXJDb2RlQXQiLCJzY3JpcHQiLCJjYWxsYmFjayIsIl8iLCJ0aHJvdWdoIiwicGlwZWxpbmUiLCJtYXAiLCJyZXN1bHQiLCJzZXF1ZW5jZSIsInJhd1N0cmVhbSIsInBpcGUiLCJlIiwic3RyZWFtIiwib2JqZWN0TW9kZSIsImJ1aWxkTWVzc2FnZSIsInNlbmRDb21tYW5kIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBRUE7O0FBRUE7Ozs7QUFDQTs7OztBQUNBOzs7O0FBRUE7Ozs7QUFDQTs7OztBQUNBOzs7O0FBQ0E7O0lBQVlBLEs7Ozs7Ozs7Ozs7K2VBWFo7QUFDQTs7O0lBYU1DLGE7OztBQUNKLDJCQUEyRDtBQUFBLFFBQS9DQyxJQUErQyx1RUFBeEMsSUFBd0M7QUFBQSxRQUFsQ0MsSUFBa0MsdUVBQTNCLFdBQTJCO0FBQUEsUUFBZEMsT0FBYyx1RUFBSixFQUFJOztBQUFBOztBQUFBOztBQUd6RCxVQUFLRixJQUFMLEdBQVlBLElBQVo7QUFDQSxVQUFLQyxJQUFMLEdBQVlBLElBQVo7O0FBSnlELHdCQU1uQ0MsT0FObUMsQ0FNakRDLElBTmlEO0FBQUEsUUFNakRBLElBTmlELGlDQU0xQyxFQU4wQzs7O0FBUXpELFVBQUtELE9BQUw7QUFDRUUsZ0JBQVUsZ0JBRFo7QUFFRUMsZUFBUyxLQUZYO0FBR0VDLFdBQUssS0FIUDtBQUlFQyxZQUFNLEVBSlI7QUFLRUMsZ0JBQVUsRUFMWjtBQU1FQyxVQUFJLE1BTk47QUFPRUMsaUJBQVcsRUFQYjtBQVFFQyxjQUFRLGtCQVJWO0FBU0VDO0FBVEYsT0FVS1YsT0FWTDtBQVdFQyxZQUFNQSxRQUFRQSxLQUFLVSxNQUFiLElBQXVCLENBQUNWLEtBQUtXLFVBQUwsQ0FBZ0IsR0FBaEIsQ0FBeEIsU0FBbURYLElBQW5ELEdBQTREQTtBQVhwRTs7QUFjQSxVQUFLWSxVQUFMLEdBQWtCLE1BQUtiLE9BQUws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QUFtQk4sT0FBbkIsQ0FBaEI7O0FBRUFyQyxvQkFBY3RCLEVBQWQsQ0FBaUIsT0FBakIsRUFBMEIsVUFBQ2tFLENBQUQsRUFBTztBQUMvQkYsa0JBQVV2RCxJQUFWLENBQWUsT0FBZixFQUF3QixJQUFJa0IsS0FBSixDQUFVdUMsQ0FBVixDQUF4QjtBQUNELE9BRkQ7O0FBSUEsYUFBT0YsU0FBUDtBQUNEOzs7OztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7a0NBZWNSLE0sRUFBUWQsUSxFQUFVN0IsVSxFQUFZO0FBQzFDLFVBQUlzRCxTQUFTLDRCQUFrQixFQUFFQyxZQUFZLElBQWQsRUFBbEIsQ0FBYjs7QUFFQSxVQUFNcEMsVUFBVTtBQUNkNUIsaUJBQVMsS0FBS2lFLFlBQUwsQ0FBa0JiLE1BQWxCLEVBQTBCZCxRQUExQixFQUFvQzdCLFVBQXBDLENBREs7QUFFZFMsdUJBQWU2QztBQUZELE9BQWhCOztBQUtBLFdBQUtHLFdBQUwsQ0FBaUJ0QyxPQUFqQixFQVIwQyxDQVFmOztBQUUzQixhQUFPbUMsTUFBUDtBQUNEOzs7OztBQUVEOzs7Ozs7Z0NBTVluQyxPLEVBQVM7QUFBQSxVQUVqQjVCLE9BRmlCLEdBTWY0QixPQU5lLENBRWpCNUIsT0FGaUI7QUFBQSxVQUlmYSxTQUplLEdBTWZlLE9BTmUsQ0FHakI1QixPQUhpQixDQUlmYSxTQUplOzs7QUFRbkIsV0FBS3BCLFFBQUwsQ0FBY29CLFNBQWQsSUFBMkJlLE9BQTNCOztBQUVBLFVBQUksS0FBS3JDLFNBQVQsRUFBb0I7QUFDbEIsYUFBSytCLFdBQUwsQ0FBaUJ0QixPQUFqQjtBQUNELE9BRkQsTUFFTztBQUNMLGFBQUtSLEtBQUwsQ0FBVzJCLElBQVgsQ0FBZ0JTLE9BQWhCO0FBQ0Q7QUFDRjs7Ozs7O2tCQUdZeEQsYSIsImZpbGUiOiJHcmVtbGluQ2xpZW50LmpzIiwic291cmNlc0NvbnRlbnQiOlsiLypqc2xpbnQgLVcwNzkgKi9cbi8qanNsaW50IG5vZGU6IHRydWUgKi9cbmltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gJ2V2ZW50cyc7XG5cbmltcG9ydCB1dWlkIGZyb20gJ25vZGUtdXVpZCc7XG5pbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IGhpZ2hsYW5kIGZyb20gJ2hpZ2hsYW5kJztcblxuaW1wb3J0IFdlYlNvY2tldEdyZW1saW5Db25uZWN0aW9uIGZyb20gJy4vV2ViU29ja2V0R3JlbWxpbkNvbm5lY3Rpb24nO1xuaW1wb3J0IE1lc3NhZ2VTdHJlYW0gZnJvbSAnLi9NZXNzYWdlU3RyZWFtJztcbmltcG9ydCBleGVjdXRlSGFuZGxlciBmcm9tICcuL2V4ZWN1dGVIYW5kbGVyJztcbmltcG9ydCAqIGFzIFV0aWxzIGZyb20gJy4vdXRpbHMnO1xuXG5cbmNsYXNzIEdyZW1saW5DbGllbnQgZXh0ZW5kcyBFdmVudEVtaXR0ZXIge1xuICBjb25zdHJ1Y3Rvcihwb3J0ID0gODE4MiwgaG9zdCA9ICdsb2NhbGhvc3QnLCBvcHRpb25zID0ge30pIHtcbiAgICBzdXBlcigpO1xuXG4gICAgdGhpcy5wb3J0ID0gcG9ydDtcbiAgICB0aGlzLmhvc3QgPSBob3N0O1xuXG4gICAgY29uc3QgeyBwYXRoID0gJycgfSA9IG9wdGlvbnM7XG5cbiAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICBsYW5ndWFnZTogJ2dyZW1saW4tZ3Jvb3Z5JyxcbiAgICAgIHNlc3Npb246IGZhbHNlLFxuICAgICAgc3NsOiBmYWxzZSxcbiAgICAgIHVzZXI6ICcnLFxuICAgICAgcGFzc3dvcmQ6ICcnLFxuICAgICAgb3A6ICdldmFsJyxcbiAgICAgIHByb2Nlc3NvcjogJycsXG4gICAgICBhY2NlcHQ6ICdhcHBsaWNhdGlvbi9qc29uJyxcbiAgICAgIGV4ZWN1dGVIYW5kbGVyLFxuICAgICAgLi4ub3B0aW9ucyxcbiAgICAgIHBhdGg6IHBhdGggJiYgcGF0aC5sZW5ndGggJiYgIXBhdGguc3RhcnRzV2l0aCgnLycpID8gYC8ke3BhdGh9YCA6IHBhdGhcbiAgICB9XG5cbiAgICB0aGlzLnVzZVNlc3Npb24gPSB0aGlzLm9wdGlvbnMuc2Vzc2lvbjtcbiAgICB0aGlzLnVzZXIgPSB0aGlzLm9wdGlvbnMudXNlcjtcbiAgICB0aGlzLnBhc3N3b3JkID0gdGhpcy5vcHRpb25zLnBhc3N3b3JkO1xuICAgIHRoaXMuc3NsID0gdGhpcy5vcHRpb25zLnNzbDtcblxuICAgIGlmICh0aGlzLnVzZVNlc3Npb24pIHtcbiAgICAgIHRoaXMuc2Vzc2lvbklkID0gdXVpZC52MSgpO1xuICAgIH1cblxuICAgIHRoaXMuY29ubmVjdGVkID0gZmFsc2U7XG4gICAgdGhpcy5xdWV1ZSA9IFtdO1xuXG4gICAgdGhpcy5jb21tYW5kcyA9IHt9O1xuXG4gICAgdGhpcy5jb25uZWN0aW9uID0gdGhpcy5jcmVhdGVDb25uZWN0aW9uKHtcbiAgICAgIHBvcnQsXG4gICAgICBob3N0LFxuICAgICAgcGF0aDogdGhpcy5vcHRpb25zLnBhdGgsXG4gICAgICBzc2w6IHRoaXMuc3NsXG4gICAgfSk7XG4gIH1cblxuICBjcmVhdGVDb25uZWN0aW9uKHsgcG9ydCwgaG9zdCwgcGF0aCwgc3NsIH0pIHtcbiAgICBjb25zdCBjb25uZWN0aW9uID0gbmV3IFdlYlNvY2tldEdyZW1saW5Db25uZWN0aW9uKHsgcG9ydCwgaG9zdCwgcGF0aCwgc3NsIH0pO1xuXG4gICAgY29ubmVjdGlvbi5vbignb3BlbicsICgpID0+IHRoaXMub25Db25uZWN0aW9uT3BlbigpKTtcbiAgICBjb25uZWN0aW9uLm9uKCdlcnJvcicsIChlcnJvcikgPT4gdGhpcy5oYW5kbGVFcnJvcihlcnJvcikpO1xuICAgIGNvbm5lY3Rpb24ub24oJ21lc3NhZ2UnLCAobWVzc2FnZSkgPT4gdGhpcy5oYW5kbGVQcm90b2NvbE1lc3NhZ2UobWVzc2FnZSkpO1xuICAgIGNvbm5lY3Rpb24ub24oJ2Nsb3NlJywgKGV2ZW50KSA9PiB0aGlzLmhhbmRsZURpc2Nvbm5lY3Rpb24oZXZlbnQpKVxuXG4gICAgcmV0dXJuIGNvbm5lY3Rpb247XG4gIH1cblxuICBoYW5kbGVFcnJvcihlcnIpIHtcbiAgICB0aGlzLmNvbm5lY3RlZCA9IGZhbHNlO1xuICAgIHRoaXMuZW1pdCgnZXJyb3InLCBlcnIpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByb2Nlc3MgYWxsIGluY29taW5nIHJhdyBtZXNzYWdlIGV2ZW50cyBzZW50IGJ5IEdyZW1saW4gU2VydmVyLCBhbmQgZGlzcGF0Y2hcbiAgICogdG8gdGhlIGFwcHJvcHJpYXRlIGNvbW1hbmQuXG4gICAqXG4gICAqIEBwYXJhbSB7TWVzc2FnZUV2ZW50fSBldmVudFxuICAgKi9cbiAgaGFuZGxlUHJvdG9jb2xNZXNzYWdlKG1lc3NhZ2UpIHtcbiAgICBjb25zdCB7IGRhdGEgfSA9IG1lc3NhZ2U7XG4gICAgY29uc3QgYnVmZmVyID0gbmV3IEJ1ZmZlcihkYXRhLCAnYmluYXJ5Jyk7XG4gICAgY29uc3QgcmF3TWVzc2FnZSA9IEpTT04ucGFyc2UoYnVmZmVyLnRvU3RyaW5nKCd1dGYtOCcpKTtcbiAgICBjb25zdCB7XG4gICAgICByZXF1ZXN0SWQsXG4gICAgICBzdGF0dXM6wqB7XG4gICAgICAgIGNvZGU6IHN0YXR1c0NvZGUsXG4gICAgICAgIG1lc3NhZ2U6IHN0YXR1c01lc3NhZ2VcbiAgICAgIH1cbiAgICB9ID0gcmF3TWVzc2FnZTtcblxuICAgIGNvbnN0IHsgbWVzc2FnZVN0cmVhbSB9ID0gdGhpcy5jb21tYW5kc1tyZXF1ZXN0SWRdO1xuXG4gICAgc3dpdGNoIChzdGF0dXNDb2RlKSB7XG4gICAgICBjYXNlIDIwMDogLy8gU1VDQ0VTU1xuICAgICAgICBkZWxldGUgdGhpcy5jb21tYW5kc1tyZXF1ZXN0SWRdOyAvLyBUT0RPOiBvcHRpbWl6ZSBwZXJmb3JtYW5jZVxuICAgICAgICBtZXNzYWdlU3RyZWFtLnB1c2gocmF3TWVzc2FnZSk7XG4gICAgICAgIG1lc3NhZ2VTdHJlYW0ucHVzaChudWxsKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlIDIwNDogLy8gTk9fQ09OVEVOVFxuICAgICAgICBkZWxldGUgdGhpcy5jb21tYW5kc1tyZXF1ZXN0SWRdO1xuICAgICAgICBtZXNzYWdlU3RyZWFtLnB1c2gobnVsbCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAyMDY6IC8vIFBBUlRJQUxfQ09OVEVOVFxuICAgICAgICBtZXNzYWdlU3RyZWFtLnB1c2gocmF3TWVzc2FnZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSA0MDc6IC8vIEFVVEhFTlRJQ0FURSBDSEFMTEFOR0VcbiAgICAgICAgY29uc3QgY2hhbGxlbmdlUmVzcG9uc2UgPSB0aGlzLmJ1aWxkQ2hhbGxlbmdlUmVzcG9uc2UocmVxdWVzdElkKTtcbiAgICAgICAgdGhpcy5zZW5kTWVzc2FnZShjaGFsbGVuZ2VSZXNwb25zZSk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgZGVsZXRlIHRoaXMuY29tbWFuZHNbcmVxdWVzdElkXTtcbiAgICAgICAgbWVzc2FnZVN0cmVhbS5lbWl0KCdlcnJvcicsIG5ldyBFcnJvcihzdGF0dXNNZXNzYWdlICsgJyAoRXJyb3IgJysgc3RhdHVzQ29kZSArJyknKSk7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgdGhlIFdlYlNvY2tldCBvbk9wZW4gZXZlbnQsIGZsYWcgdGhlIGNsaWVudCBhcyBjb25uZWN0ZWQgYW5kXG4gICAqIHByb2Nlc3MgY29tbWFuZCBxdWV1ZS5cbiAgICovXG4gIG9uQ29ubmVjdGlvbk9wZW4oKSB7XG4gICAgdGhpcy5jb25uZWN0ZWQgPSB0cnVlO1xuICAgIHRoaXMuZW1pdCgnY29ubmVjdCcpO1xuXG4gICAgdGhpcy5leGVjdXRlUXVldWUoKTtcbiAgfTtcblxuICAvKipcbiAgICogQHBhcmFtIHtDbG9zZUV2ZW50fSBldmVudFxuICAgKi9cbiAgaGFuZGxlRGlzY29ubmVjdGlvbihldmVudCkge1xuICAgIHRoaXMuY2FuY2VsUGVuZGluZ0NvbW1hbmRzKHtcbiAgICAgIG1lc3NhZ2U6ICdXZWJTb2NrZXQgY2xvc2VkJyxcbiAgICAgIGRldGFpbHM6IGV2ZW50XG4gICAgfSk7XG4gIH07XG5cbiAgLyoqXG4gICAqIFByb2Nlc3MgdGhlIGN1cnJlbnQgY29tbWFuZCBxdWV1ZSwgc2VuZGluZyBjb21tYW5kcyB0byBHcmVtbGluIFNlcnZlclxuICAgKiAoRmlyc3QgSW4sIEZpcnN0IE91dCkuXG4gICAqL1xuICBleGVjdXRlUXVldWUoKSB7XG4gICAgd2hpbGUgKHRoaXMucXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgbGV0IHsgbWVzc2FnZSB9ID0gdGhpcy5xdWV1ZS5zaGlmdCgpO1xuICAgICAgdGhpcy5zZW5kTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9XG4gIH07XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSByZWFzb25cbiAgICovXG4gIGNhbmNlbFBlbmRpbmdDb21tYW5kcyh7IG1lc3NhZ2UsIGRldGFpbHMgfSkge1xuICAgIGNvbnN0IGNvbW1hbmRzID0gdGhpcy5jb21tYW5kcztcbiAgICBsZXQgY29tbWFuZDtcbiAgICBjb25zdCBlcnJvciA9IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgICBlcnJvci5kZXRhaWxzID0gZGV0YWlscztcblxuICAgIC8vIEVtcHR5IHF1ZXVlXG4gICAgdGhpcy5xdWV1ZS5sZW5ndGggPSAwO1xuICAgIHRoaXMuY29tbWFuZHMgPSB7fTtcblxuICAgIE9iamVjdC5rZXlzKGNvbW1hbmRzKS5mb3JFYWNoKChrZXkpID0+IHtcbiAgICAgIGNvbW1hbmQgPSBjb21tYW5kc1trZXldO1xuICAgICAgY29tbWFuZC5tZXNzYWdlU3RyZWFtLmVtaXQoJ2Vycm9yJywgZXJyb3IpO1xuICAgIH0pO1xuICB9O1xuXG4gIC8qKlxuICAgKiBGb3IgYSBnaXZlbiBzY3JpcHQgc3RyaW5nIGFuZCBvcHRpb25hbCBib3VuZCBwYXJhbWV0ZXJzLCBidWlsZCBhIHByb3RvY29sXG4gICAqIG1lc3NhZ2Ugb2JqZWN0IHRvIGJlIHNlbnQgdG8gR3JlbWxpbiBTZXJ2ZXIuXG4gICAqXG4gICAqIEBwYXJhbSB7U3RyaW5nfEZ1bmN0aW9ufSBzY3JpcHRcbiAgICogQHBhcmFtIHtPYmplY3R9IGJpbmRpbmdzXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBtZXNzYWdlXG4gICAqL1xuICBidWlsZE1lc3NhZ2UocmF3U2NyaXB0LCByYXdCaW5kaW5ncyA9IHt9LCBiYXNlTWVzc2FnZSA9IHt9KSB7XG4gICAgbGV0IHsgZ3JlbWxpbiwgYmluZGluZ3MgfSA9IFV0aWxzLmJ1aWxkUXVlcnlGcm9tU2lnbmF0dXJlKHJhd1NjcmlwdCwgcmF3QmluZGluZ3MpO1xuICAgIGNvbnN0IHsgcHJvY2Vzc29yLCBvcCwgYWNjZXB0LCBsYW5ndWFnZSwgYWxpYXNlcyB9ID0gdGhpcy5vcHRpb25zO1xuXG4gICAgY29uc3QgYmFzZUFyZ3MgPSB7IGdyZW1saW4sIGJpbmRpbmdzLCBhY2NlcHQsIGxhbmd1YWdlLCBhbGlhc2VzIH07XG4gICAgY29uc3QgYXJncyA9IF8uZGVmYXVsdHMoYmFzZU1lc3NhZ2UuYXJncyB8fCB7fSwgYmFzZUFyZ3MpO1xuXG4gICAgY29uc3QgbWVzc2FnZSA9IHtcbiAgICAgIHJlcXVlc3RJZDogdXVpZC52MSgpLFxuICAgICAgcHJvY2Vzc29yLFxuICAgICAgb3AsXG4gICAgICBhcmdzLFxuICAgICAgLi4uYmFzZU1lc3NhZ2VcbiAgICB9O1xuXG4gICAgaWYgKHRoaXMudXNlU2Vzc2lvbikge1xuICAgICAgLy8gQXNzdW1lIHRoYXQgcGVvcGxlIHdhbnQgdG8gdXNlIHRoZSAnc2Vzc2lvbicgcHJvY2Vzc29yIHVubGVzcyBzcGVjaWZpZWRcbiAgICAgIG1lc3NhZ2UucHJvY2Vzc29yID0gbWVzc2FnZS5wcm9jZXNzb3IgfHwgcHJvY2Vzc29yIHx8ICdzZXNzaW9uJztcbiAgICAgIG1lc3NhZ2UuYXJncy5zZXNzaW9uID0gdGhpcy5zZXNzaW9uSWQ7XG4gICAgfVxuXG4gICAgcmV0dXJuIG1lc3NhZ2U7XG4gIH07XG5cbiAgYnVpbGRDaGFsbGVuZ2VSZXNwb25zZShyZXF1ZXN0SWQpIHtcbiAgICBjb25zdCB7IHByb2Nlc3Nvciwgb3AsIGFjY2VwdCwgbGFuZ3VhZ2UsIGFsaWFzZXMgfSA9IHRoaXMub3B0aW9ucztcbiAgICB2YXIgYXJncyA9IHsgU0FTTDogJ1xcMCcgKyB0aGlzLnVzZXIgKyAnXFwwJyArIHRoaXMucGFzc3dvcmQgfTtcbiAgICBcbiAgICBjb25zdCBtZXNzYWdlID0ge1xuICAgICAgcmVxdWVzdElkOiByZXF1ZXN0SWQsXG4gICAgICBwcm9jZXNzb3IsXG4gICAgICBvcDogJ2F1dGhlbnRpY2F0aW9uJyxcbiAgICAgIGFyZ3NcbiAgICB9O1xuXG4gICAgcmV0dXJuIG1lc3NhZ2U7XG4gIH07XG5cbiAgc2VuZE1lc3NhZ2UobWVzc2FnZSkge1xuICAgIGxldCBzZXJpYWxpemVkTWVzc2FnZSA9IHRoaXMub3B0aW9ucy5hY2NlcHQgKyBKU09OLnN0cmluZ2lmeShtZXNzYWdlKTtcbiAgICBzZXJpYWxpemVkTWVzc2FnZSA9IHVuZXNjYXBlKGVuY29kZVVSSUNvbXBvbmVudChzZXJpYWxpemVkTWVzc2FnZSkpO1xuXG4gICAgLy8gTGV0J3Mgc3RhcnQgcGFja2luZyB0aGUgbWVzc2FnZSBpbnRvIGJpbmFyeVxuICAgIC8vIG1pbWVMZW5ndGgoMSkgKyBtaW1lVHlwZSBMZW5ndGggKyBzZXJpYWxpemVkTWVzc2FnZSBMZW5ndGhcbiAgICBsZXQgYmluYXJ5TWVzc2FnZSA9IG5ldyBVaW50OEFycmF5KDEgKyBzZXJpYWxpemVkTWVzc2FnZS5sZW5ndGgpO1xuICAgIGJpbmFyeU1lc3NhZ2VbMF0gPSB0aGlzLm9wdGlvbnMuYWNjZXB0Lmxlbmd0aDtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc2VyaWFsaXplZE1lc3NhZ2UubGVuZ3RoOyBpKyspIHtcbiAgICAgIGJpbmFyeU1lc3NhZ2VbaSArIDFdID0gc2VyaWFsaXplZE1lc3NhZ2UuY2hhckNvZGVBdChpKTtcbiAgICB9XG5cbiAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZE1lc3NhZ2UoYmluYXJ5TWVzc2FnZSk7XG4gIH07XG5cbiAgLyoqXG4gICAqIEFzeW5jaHJvbm91c2x5IHNlbmQgYSBzY3JpcHQgdG8gR3JlbWxpbiBTZXJ2ZXIgZm9yIGV4ZWN1dGlvbiBhbmQgZmlyZVxuICAgKiB0aGUgcHJvdmlkZWQgY2FsbGJhY2sgd2hlbiBhbGwgcmVzdWx0cyBoYXZlIGJlZW4gZmV0Y2hlZC5cbiAgICpcbiAgICogVGhpcyBtZXRob2QgaW50ZXJuYWxseSB1c2VzIGEgc3RyZWFtIHRvIGhhbmRsZSB0aGUgcG90ZW50aWFsIGNvbmNhdGVuYXRpb25cbiAgICogb2YgcmVzdWx0cy5cbiAgICpcbiAgICogQ2FsbGJhY2sgc2lnbmF0dXJlOiAoRXJyb3IsIEFycmF5PHJlc3VsdD4pXG4gICAqXG4gICAqIEBwdWJsaWNcbiAgICogQHBhcmFtIHtTdHJpbmd8RnVuY3Rpb259IHNjcmlwdFxuICAgKiBAcGFyYW0ge09iamVjdH0gYmluZGluZ3NcbiAgICogQHBhcmFtIHtPYmplY3R9IG1lc3NhZ2VcbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gY2FsbGJhY2tcbiAgICovXG4gIGV4ZWN1dGUoc2NyaXB0LCBiaW5kaW5ncyA9IHt9LCBtZXNzYWdlID0ge30sIC4uLmFyZ3MpIHtcbiAgICBsZXQgY2FsbGJhY2sgPSBhcmdzW2FyZ3MubGVuZ3RoIC0gMV07XG5cbiAgICBpZiAodHlwZW9mIG1lc3NhZ2UgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGNhbGxiYWNrID0gbWVzc2FnZTtcbiAgICAgIG1lc3NhZ2UgPSB7fTtcbiAgICB9XG5cbiAgICBjb25zdCBtZXNzYWdlU3RyZWFtID0gdGhpcy5tZXNzYWdlU3RyZWFtKHNjcmlwdCwgYmluZGluZ3MsIG1lc3NhZ2UpO1xuXG4gICAgLy8gVE8gQ0hFQ0s6IGVycm9ycyBoYW5kbGluZyBjb3VsZCBiZSBpbXByb3ZlZFxuICAgIC8vIFNlZSBodHRwczovL2dyb3Vwcy5nb29nbGUuY29tL2QvbXNnL25vZGVqcy9sSllUOWhaeEZ1MC9MNTlDRmJxV0d5WUpcbiAgICAvLyBmb3IgYW4gZXhhbXBsZSB1c2luZyBkb21haW5zXG4gICAgY29uc3QgeyBleGVjdXRlSGFuZGxlciB9ID0gdGhpcy5vcHRpb25zO1xuXG4gICAgZXhlY3V0ZUhhbmRsZXIobWVzc2FnZVN0cmVhbSwgY2FsbGJhY2spO1xuICB9XG5cbiAgLyoqXG4gICAqIEV4ZWN1dGUgdGhlIHNjcmlwdCBhbmQgcmV0dXJuIGEgc3RyZWFtIG9mIGRpc3RpbmN0L3NpbmdsZSByZXN1bHRzLlxuICAgKiBUaGlzIG1ldGhvZCByZWVtaXRzIGEgZGlzdGluY3QgZGF0YSBldmVudCBmb3IgZWFjaCByZXR1cm5lZCByZXN1bHQsIHdoaWNoXG4gICAqIG1ha2VzIHRoZSBzdHJlYW0gYmVoYXZlIGFzIGlmIGByZXN1bHRJdGVyYXRpb25CYXRjaFNpemVgIHdhcyBzZXQgdG8gMS5cbiAgICpcbiAgICogSWYgeW91IGRvIG5vdCB3aXNoIHRoaXMgYmVoYXZpb3IsIHBsZWFzZSB1c2UgY2xpZW50Lm1lc3NhZ2VTdHJlYW0oKSBpbnN0ZWFkLlxuICAgKlxuICAgKiBFdmVuIHRob3VnaCB0aGlzIG1ldGhvZCB1c2VzIEhpZ2hsYW5kLmpzIGludGVybmFsbHksIGl0IGRvZXMgbm90IHJldHVyblxuICAgKiBhIGhpZ2ggbGV2ZWwgSGlnaGxhbmQgcmVhZGFibGUgc3RyZWFtIHNvIHdlIGRvIG5vdCByaXNrIGhhdmluZyB0byBkZWFsXG4gICAqIHdpdGggdW5leHBlY3RlZCBBUEkgYnJlYWtpbmcgY2hhbmdlcyBhcyBIaWdobGFuZC5qcyBldm9sdmVzLlxuICAgKlxuICAgKiBAcmV0dXJuIHtSZWFkYWJsZVN0cmVhbX0gQSBOb2RlLmpzIFN0cmVhbTJcbiAgICovXG4gIHN0cmVhbShzY3JpcHQsIGJpbmRpbmdzLCBtZXNzYWdlKSB7XG4gICAgY29uc3QgbWVzc2FnZVN0cmVhbSA9IHRoaXMubWVzc2FnZVN0cmVhbShzY3JpcHQsIGJpbmRpbmdzLCBtZXNzYWdlKTtcbiAgICBjb25zdCBfID0gaGlnaGxhbmQ7IC8vIG92ZXJyaWRlIGxvLWRhc2ggbG9jYWxseVxuXG4gICAgLy8gQ3JlYXRlIGEgbG9jYWwgaGlnaGxhbmQgJ3Rocm91Z2gnIHBpcGVsaW5lIHNvIHdlIGRvbid0IGV4cG9zZVxuICAgIC8vIGEgSGlnaGxhbmQgc3RyZWFtIHRvIHRoZSBlbmQgdXNlciwgYnV0IGEgc3RhbmRhcmQgTm9kZS5qcyBTdHJlYW0yXG4gICAgY29uc3QgdGhyb3VnaCA9IF8ucGlwZWxpbmUoXG4gICAgICBfLm1hcCgoeyByZXN1bHQ6IHsgZGF0YSB9fSkgPT4gZGF0YSksXG4gICAgICBfLnNlcXVlbmNlKClcbiAgICApO1xuXG4gICAgbGV0IHJhd1N0cmVhbSA9IG1lc3NhZ2VTdHJlYW0ucGlwZSh0aHJvdWdoKTtcblxuICAgIG1lc3NhZ2VTdHJlYW0ub24oJ2Vycm9yJywgKGUpID0+IHtcbiAgICAgIHJhd1N0cmVhbS5lbWl0KCdlcnJvcicsIG5ldyBFcnJvcihlKSk7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gcmF3U3RyZWFtO1xuICB9O1xuXG4gIC8qKlxuICAgKiBFeGVjdXRlIHRoZSBzY3JpcHQgYW5kIHJldHVybiBhIHN0cmVhbSBvZiByYXcgbWVzc2FnZXMgcmV0dXJuZWQgYnkgR3JlbWxpblxuICAgKiBTZXJ2ZXIuXG4gICAqIFRoaXMgbWV0aG9kIGRvZXMgbm90IHJlZW1pdCBvbmUgZGlzdGluY3QgZGF0YSBldmVudCBwZXIgcmVzdWx0LiBJdCBkaXJlY3RseVxuICAgKiBlbWl0cyB0aGUgcmF3IG1lc3NhZ2VzIHJldHVybmVkIGJ5IEdyZW1saW4gU2VydmVyIGFzIHRoZXkgYXJlIHJlY2VpdmVkLlxuICAgKlxuICAgKiBBbHRob3VnaCBwdWJsaWMsIHRoaXMgaXMgYSBsb3cgbGV2ZWwgbWV0aG9kIGludGVuZGVkIHRvIGJlIHVzZWQgZm9yXG4gICAqIGFkdmFuY2VkIHVzYWdlcy5cbiAgICpcbiAgICogQHB1YmxpY1xuICAgKiBAcGFyYW0ge1N0cmluZ3xGdW5jdGlvbn0gc2NyaXB0XG4gICAqIEBwYXJhbSB7T2JqZWN0fSBiaW5kaW5nc1xuICAgKiBAcGFyYW0ge09iamVjdH0gbWVzc2FnZVxuICAgKiBAcmV0dXJuIHtNZXNzYWdlU3RyZWFtfVxuICAgKi9cbiAgbWVzc2FnZVN0cmVhbShzY3JpcHQsIGJpbmRpbmdzLCByYXdNZXNzYWdlKSB7XG4gICAgbGV0IHN0cmVhbSA9IG5ldyBNZXNzYWdlU3RyZWFtKHsgb2JqZWN0TW9kZTogdHJ1ZSB9KTtcblxuICAgIGNvbnN0IGNvbW1hbmQgPSB7XG4gICAgICBtZXNzYWdlOiB0aGlzLmJ1aWxkTWVzc2FnZShzY3JpcHQsIGJpbmRpbmdzLCByYXdNZXNzYWdlKSxcbiAgICAgIG1lc3NhZ2VTdHJlYW06IHN0cmVhbVxuICAgIH07XG5cbiAgICB0aGlzLnNlbmRDb21tYW5kKGNvbW1hbmQpOyAvL3RvZG8gaW1wcm92ZSBmb3Igc3RyZWFtc1xuXG4gICAgcmV0dXJuIHN0cmVhbTtcbiAgfTtcblxuICAvKipcbiAgICogU2VuZCBhIGNvbW1hbmQgdG8gR3JlbWxpbiBTZXJ2ZXIsIG9yIGFkZCBpdCB0byBxdWV1ZSBpZiB0aGUgY29ubmVjdGlvblxuICAgKiBpcyBub3QgZXN0YWJsaXNoZWQuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBjb21tYW5kXG4gICAqL1xuICBzZW5kQ29tbWFuZChjb21tYW5kKSB7XG4gICAgY29uc3Qge1xuICAgICAgbWVzc2FnZSxcbiAgICAgIG1lc3NhZ2U6IHtcbiAgICAgICAgcmVxdWVzdElkXG4gICAgICB9XG4gICAgfSA9IGNvbW1hbmQ7XG5cbiAgICB0aGlzLmNvbW1hbmRzW3JlcXVlc3RJZF0gPSBjb21tYW5kO1xuXG4gICAgaWYgKHRoaXMuY29ubmVjdGVkKSB7XG4gICAgICB0aGlzLnNlbmRNZXNzYWdlKG1lc3NhZ2UpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnF1ZXVlLnB1c2goY29tbWFuZCk7XG4gICAgfVxuICB9O1xufVxuXG5leHBvcnQgZGVmYXVsdCBHcmVtbGluQ2xpZW50O1xuIl19