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
JavaScript
;
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