UNPKG

45.9 kBJavaScriptView Raw
1'use strict';
2
3/**
4 * Node style callback. All callbacks are optional, promises may be
5 * used instead. But only one API must be used per invocation.
6 *
7 * @callback callback
8 * @param {Error} error
9 * @param {...*} results
10 */
11
12/**
13 * Server side related documentation.
14 *
15 * @example <caption>npm package usage</caption>
16 * let ChatService = require('chat-service')
17 *
18 * @namespace chat-service
19 */
20
21var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
22
23var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
24
25var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
26
27var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
28
29var _createClass2 = require('babel-runtime/helpers/createClass');
30
31var _createClass3 = _interopRequireDefault(_createClass2);
32
33var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
34
35var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
36
37var _inherits2 = require('babel-runtime/helpers/inherits');
38
39var _inherits3 = _interopRequireDefault(_inherits2);
40
41function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
42
43var ArgumentsValidator = require('./ArgumentsValidator');
44var ChatServiceError = require('./ChatServiceError');
45var MemoryState = require('./MemoryState');
46var Promise = require('bluebird');
47var RecoveryAPI = require('./RecoveryAPI');
48var RedisState = require('./RedisState');
49var Room = require('./Room');
50var ServiceAPI = require('./ServiceAPI');
51var SocketIOClusterBus = require('./SocketIOClusterBus');
52var SocketIOTransport = require('./SocketIOTransport');
53var User = require('./User');
54var _ = require('lodash');
55var uid = require('uid-safe');
56
57var _require = require('events');
58
59var EventEmitter = _require.EventEmitter;
60
61var _require2 = require('./utils');
62
63var checkNameSymbols = _require2.checkNameSymbols;
64var _convertError = _require2.convertError;
65var execHook = _require2.execHook;
66var logError = _require2.logError;
67
68var _require3 = require('es6-mixin');
69
70var mixin = _require3.mixin;
71
72
73var rpcRequestsNames = ['directAddToList', 'directGetAccessList', 'directGetWhitelistMode', 'directMessage', 'directRemoveFromList', 'directSetWhitelistMode', 'listOwnSockets', 'roomAddToList', 'roomCreate', 'roomDelete', 'roomGetAccessList', 'roomGetOwner', 'roomGetWhitelistMode', 'roomHistoryGet', 'roomHistoryInfo', 'roomJoin', 'roomLeave', 'roomMessage', 'roomNotificationsInfo', 'roomRecentHistory', 'roomRemoveFromList', 'roomSetWhitelistMode', 'roomUserSeen', 'systemMessage'];
74
75/**
76 * Service class, is the package exported object.
77 *
78 * @extends EventEmitter
79 *
80 * @mixes chat-service.ServiceAPI
81 * @mixes chat-service.RecoveryAPI
82 *
83 * @fires chat-service.ChatService.ready
84 * @fires chat-service.ChatService.closed
85 * @fires chat-service.ChatService.storeConsistencyFailure
86 * @fires chat-service.ChatService.transportConsistencyFailure
87 * @fires chat-service.ChatService.lockTimeExceeded
88 *
89 * @example <caption>starting a server</caption>
90 * let ChatService = require('chat-service')
91 * let service = new ChatService(options, hooks)
92 *
93 * @example <caption>server-side: adding a room</caption>
94 * let owner = 'admin'
95 * let whitelistOnly = true
96 * let whitelist = [ 'user' ]
97 * let state = { owner, whitelistOnly, whitelist }
98 * chatService.addRoom('someRoom', state).then(fn)
99 *
100 * @example <caption>server-side: sending a room message</caption>
101 * let room = 'someRoom'
102 * let msg = { textMessage: 'some message' }
103 * let context = {
104 * userName: 'system',
105 * bypassPermissions: true
106 * }
107 * chatService.execUserCommand(context, 'roomMessage', room, msg)
108 * .then(fn)
109 *
110 * @example <caption>server-side: joining an user socket to a room</caption>
111 * let room = 'someRoom'
112 * let context = {
113 * userName: 'user',
114 * id: id // socket id
115 * }
116 * chatService.execUserCommand(context, 'roomJoin', room)
117 * .then(fn) // real sockets will get a notification
118 *
119 * @memberof chat-service
120 *
121 */
122
123var ChatService = function (_EventEmitter) {
124 (0, _inherits3.default)(ChatService, _EventEmitter);
125
126 /**
127 * Crates an object and starts a new service instance. The {@link
128 * chat-service.ChatService#close} method __MUST__ be called before
129 * the node process exit.
130 *
131 * @param {chat-service.config.options} [options] Service
132 * configuration options.
133 *
134 * @param {chat-service.hooks.HooksInterface} [hooks] Service
135 * customisation hooks.
136 */
137 function ChatService() {
138 var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
139 var hooks = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
140 (0, _classCallCheck3.default)(this, ChatService);
141
142 var _this = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(ChatService).call(this));
143
144 _this.options = options;
145 _this.hooks = hooks;
146 _this.initVariables();
147 _this.setOptions();
148 _this.setIntegraionOptions();
149 _this.setComponents();
150 _this.attachBusListeners();
151 mixin(_this, ServiceAPI, _this.state, function () {
152 return new User(_this);
153 }, _this.clusterBus);
154 mixin(_this, RecoveryAPI, _this.state, _this.transport, _this.execUserCommand.bind(_this), _this.instanceUID);
155 _this.startServer();
156 return _this;
157 }
158
159 /**
160 * ChatService errors constructor. This errors are intended to be
161 * returned to clients as a part of a normal service functioning
162 * (something like 403 errors). Can be also used to create custom
163 * errors subclasses.
164 *
165 * @name ChatServiceError
166 * @type Class
167 * @static
168 * @readonly
169 *
170 * @memberof chat-service.ChatService
171 *
172 * @see rpc.datatypes.ChatServiceError
173 */
174
175 /**
176 * Service instance UID.
177 *
178 * @name chat-service.ChatService#instanceUID
179 * @type string
180 * @readonly
181 */
182
183 /**
184 * Cluster communication via an adapter. Emits messages to all
185 * services nodes, including the sender node.
186 *
187 * @name chat-service.ChatService#clusterBus
188 * @type EventEmitter
189 * @readonly
190 */
191
192 /**
193 * Transport object.
194 *
195 * @name chat-service.ChatService#transport
196 * @type chat-service.TransportInterface
197 * @readonly
198 */
199
200 /**
201 * Service is ready, state and transport are up.
202 * @event ready
203 *
204 * @memberof chat-service.ChatService
205 */
206
207 /**
208 * Service is closed, state and transport are closed.
209 * @event closed
210 * @param {Error} [error] If was closed due to an error.
211 *
212 * @memberof chat-service.ChatService
213 */
214
215 /**
216 * State store failed to be updated to reflect an user's connection
217 * or presence state.
218 *
219 * @event storeConsistencyFailure
220 * @param {Error} error Error.
221 * @param {Object} operationInfo Operation details.
222 * @property {string} operationInfo.userName User name.
223 * @property {string} operationInfo.opType Operation type.
224 * @property {string} [operationInfo.roomName] Room name.
225 * @property {string} [operationInfo.id] Socket id.
226 *
227 * @see chat-service.RecoveryAPI
228 *
229 * @memberof chat-service.ChatService
230 */
231
232 /**
233 * Failed to teardown a transport connection.
234 *
235 * @event transportConsistencyFailure
236 *
237 * @param {Error} error Error.
238 * @param {Object} operationInfo Operation details.
239 * @property {string} operationInfo.userName User name.
240 * @property {string} operationInfo.opType Operation type.
241 * @property {string} [operationInfo.roomName] Room name.
242 * @property {string} [operationInfo.id] Socket id.
243 *
244 * @memberof chat-service.ChatService
245 */
246
247 /**
248 * Lock was hold longer than a lock ttl.
249 *
250 * @event lockTimeExceeded
251 *
252 * @param {string} id Lock id.
253 * @param {Object} lockInfo Lock resource details.
254 * @property {string} [lockInfo.userName] User name.
255 * @property {string} [lockInfo.roomName] Room name.
256 *
257 * @see chat-service.RecoveryAPI
258 *
259 * @memberof chat-service.ChatService
260 */
261
262 /**
263 * Exposes an internal arguments validation method, it is run
264 * automatically by all client request (command) handlers.
265 *
266 * @method chat-service.ChatService#checkArguments
267 *
268 * @param {string} name Command name.
269 * @param {...*} args Command arguments.
270 * @param {callback} [cb] Optional callback.
271 *
272 * @return {Promise<undefined>} Promise that resolves without any
273 * data if validation is successful, otherwise a promise is
274 * rejected.
275 */
276
277 (0, _createClass3.default)(ChatService, [{
278 key: 'initVariables',
279 value: function initVariables() {
280 this.instanceUID = uid.sync(18);
281 this.runningCommands = 0;
282 this.rpcRequestsNames = rpcRequestsNames;
283 this.closed = false;
284 // constants
285 this.ChatServiceError = ChatServiceError;
286 this.SocketIOClusterBus = SocketIOClusterBus;
287 this.User = User;
288 this.Room = Room;
289 }
290 }, {
291 key: 'setOptions',
292 value: function setOptions() {
293 this.closeTimeout = this.options.closeTimeout || 15000;
294 this.busAckTimeout = this.options.busAckTimeout || 5000;
295 this.heartbeatRate = this.options.heartbeatRate || 10000;
296 this.heartbeatTimeout = this.options.heartbeatTimeout || 30000;
297 this.directListSizeLimit = this.options.directListSizeLimit || 1000;
298 this.roomListSizeLimit = this.options.roomListSizeLimit || 10000;
299 this.enableAccessListsUpdates = this.options.enableAccessListsUpdates || false;
300 this.enableDirectMessages = this.options.enableDirectMessages || false;
301 this.enableRoomsManagement = this.options.enableRoomsManagement || false;
302 this.enableUserlistUpdates = this.options.enableUserlistUpdates || false;
303 this.historyMaxGetMessages = this.options.historyMaxGetMessages;
304 if (!_.isNumber(this.historyMaxGetMessages) || this.historyMaxGetMessages < 0) {
305 this.historyMaxGetMessages = 100;
306 }
307 this.historyMaxSize = this.options.historyMaxSize;
308 if (!_.isNumber(this.historyMaxSize) || this.historyMaxSize < 0) {
309 this.historyMaxSize = 10000;
310 }
311 this.port = this.options.port || 8000;
312 this.directMessagesChecker = this.hooks.directMessagesChecker;
313 this.roomMessagesChecker = this.hooks.roomMessagesChecker;
314 this.useRawErrorObjects = this.options.useRawErrorObjects || false;
315 }
316 }, {
317 key: 'setIntegraionOptions',
318 value: function setIntegraionOptions() {
319 this.adapterConstructor = this.options.adapter || 'memory';
320 this.adapterOptions = _.castArray(this.options.adapterOptions);
321
322 this.stateConstructor = this.options.state || 'memory';
323 this.stateOptions = this.options.stateOptions || {};
324
325 this.transportConstructor = this.options.transport || 'socket.io';
326 this.transportOptions = this.options.transportOptions || {};
327 }
328 }, {
329 key: 'setComponents',
330 value: function setComponents() {
331 var _this2 = this;
332
333 var State = function () {
334 switch (true) {
335 case _this2.stateConstructor === 'memory':
336 return MemoryState;
337 case _this2.stateConstructor === 'redis':
338 return RedisState;
339 case _.isFunction(_this2.stateConstructor):
340 return _this2.stateConstructor;
341 default:
342 throw new Error('Invalid state: ' + _this2.stateConstructor);
343 }
344 }();
345 var Transport = function () {
346 switch (true) {
347 case _this2.transportConstructor === 'socket.io':
348 return SocketIOTransport;
349 case _.isFunction(_this2.transportConstructor):
350 return _this2.transportConstructor;
351 default:
352 throw new Error('Invalid transport: ' + _this2.transportConstructor);
353 }
354 }();
355 this.validator = new ArgumentsValidator(this);
356 this.checkArguments = this.validator.checkArguments.bind(this.validator);
357 this.state = new State(this, this.stateOptions);
358 this.transport = new Transport(this, this.transportOptions, this.adapterConstructor, this.adapterOptions);
359 this.clusterBus = this.transport.clusterBus;
360 }
361 }, {
362 key: 'attachBusListeners',
363 value: function attachBusListeners() {
364 var _this3 = this;
365
366 this.clusterBus.on('roomLeaveSocket', function (id, roomName) {
367 return _this3.transport.leaveChannel(id, roomName).then(function () {
368 return _this3.clusterBus.emit('socketRoomLeft', id, roomName);
369 }).catchReturn();
370 });
371 this.clusterBus.on('disconnectUserSockets', function (userName) {
372 return _this3.state.getUser(userName).then(function (user) {
373 return user.disconnectInstanceSockets();
374 }).catchReturn();
375 });
376 }
377
378 // for transport plugins integration
379
380 }, {
381 key: 'convertError',
382 value: function convertError(error) {
383 return _convertError(error, this.useRawErrorObjects);
384 }
385
386 // for transport plugins integration
387
388 }, {
389 key: 'onConnect',
390 value: function onConnect(id) {
391 var _this4 = this;
392
393 if (this.hooks.onConnect) {
394 return Promise.try(function () {
395 return execHook(_this4.hooks.onConnect, _this4, id);
396 }).then(function (loginData) {
397 loginData = _.castArray(loginData);
398 return Promise.resolve(loginData);
399 }).catch(function (error) {
400 return logError(error);
401 });
402 } else {
403 return Promise.resolve([]);
404 }
405 }
406
407 // for transport plugins integration
408
409 }, {
410 key: 'registerClient',
411 value: function registerClient(userName, id) {
412 var _this5 = this;
413
414 return checkNameSymbols(userName).then(function () {
415 return _this5.state.getOrAddUser(userName);
416 }).then(function (user) {
417 return user.registerSocket(id);
418 }).catch(function (error) {
419 return logError(error);
420 });
421 }
422 }, {
423 key: 'waitCommands',
424 value: function waitCommands() {
425 var _this6 = this;
426
427 if (this.runningCommands > 0) {
428 return Promise.fromCallback(function (cb) {
429 return _this6.once('commandsFinished', cb);
430 });
431 } else {
432 return Promise.resolve();
433 }
434 }
435 }, {
436 key: 'closeTransport',
437 value: function closeTransport() {
438 var _this7 = this;
439
440 return this.transport.close().then(function () {
441 return _this7.waitCommands();
442 }).timeout(this.closeTimeout);
443 }
444 }, {
445 key: 'startServer',
446 value: function startServer() {
447 var _this8 = this;
448
449 return Promise.try(function () {
450 if (_this8.hooks.onStart) {
451 return _this8.clusterBus.listen().then(function () {
452 return execHook(_this8.hooks.onStart, _this8);
453 }).then(function () {
454 return _this8.transport.setEvents();
455 });
456 } else {
457 // tests spec compatibility
458 _this8.transport.setEvents();
459 return _this8.clusterBus.listen();
460 }
461 }).then(function () {
462 _this8.state.updateHeartbeat();
463 var hbupdater = _this8.state.updateHeartbeat.bind(_this8.state);
464 _this8.hbtimer = setInterval(hbupdater, _this8.heartbeatRate);
465 return _this8.emit('ready');
466 }).catch(function (error) {
467 _this8.closed = true;
468 return _this8.closeTransport().then(function () {
469 return _this8.state.close();
470 }).finally(function () {
471 return _this8.emit('closed', error);
472 });
473 });
474 }
475
476 /**
477 * Closes server.
478 * @note __MUST__ be called before node process shutdown to correctly
479 * update the state.
480 * @param {callback} [cb] Optional callback.
481 * @return {Promise<undefined>} Promise that resolves without any data.
482 */
483
484 }, {
485 key: 'close',
486 value: function close(cb) {
487 var _this9 = this;
488
489 if (this.closed) {
490 return Promise.resolve();
491 }
492 this.closed = true;
493 clearInterval(this.hbtimer);
494 var closeError = null;
495 return this.closeTransport().then(function () {
496 return execHook(_this9.hooks.onClose, _this9, null);
497 }, function (error) {
498 if (_this9.hooks.onClose) {
499 return execHook(_this9.hooks.onClose, _this9, error);
500 } else {
501 return Promise.reject(error);
502 }
503 }).catch(function (error) {
504 closeError = error;
505 return Promise.reject(error);
506 }).finally(function () {
507 return _this9.state.close().finally(function () {
508 return _this9.emit('closed', closeError);
509 });
510 }).asCallback(cb);
511 }
512 }]);
513 return ChatService;
514}(EventEmitter);
515
516// for custom errors
517
518
519ChatService.ChatServiceError = ChatServiceError;
520
521// for transport plugin implementations
522ChatService.SocketIOClusterBus = SocketIOClusterBus;
523
524// for store plugin implementations
525ChatService.User = User;
526ChatService.Room = Room;
527
528module.exports = ChatService;
529//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9DaGF0U2VydmljZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7QUFFQTs7Ozs7Ozs7O0FBU0E7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFTQSxJQUFNLHFCQUFxQixRQUFRLHNCQUFSLENBQTNCO0FBQ0EsSUFBTSxtQkFBbUIsUUFBUSxvQkFBUixDQUF6QjtBQUNBLElBQU0sY0FBYyxRQUFRLGVBQVIsQ0FBcEI7QUFDQSxJQUFNLFVBQVUsUUFBUSxVQUFSLENBQWhCO0FBQ0EsSUFBTSxjQUFjLFFBQVEsZUFBUixDQUFwQjtBQUNBLElBQU0sYUFBYSxRQUFRLGNBQVIsQ0FBbkI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLGFBQWEsUUFBUSxjQUFSLENBQW5CO0FBQ0EsSUFBTSxxQkFBcUIsUUFBUSxzQkFBUixDQUEzQjtBQUNBLElBQU0sb0JBQW9CLFFBQVEscUJBQVIsQ0FBMUI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLElBQUksUUFBUSxRQUFSLENBQVY7QUFDQSxJQUFNLE1BQU0sUUFBUSxVQUFSLENBQVo7O2VBQ3lCLFFBQVEsUUFBUixDOztJQUFqQixZLFlBQUEsWTs7Z0JBRUEsUUFBUSxTQUFSLEM7O0lBREEsZ0IsYUFBQSxnQjtJQUFrQixhLGFBQUEsWTtJQUFjLFEsYUFBQSxRO0lBQVUsUSxhQUFBLFE7O2dCQUVoQyxRQUFRLFdBQVIsQzs7SUFBVixLLGFBQUEsSzs7O0FBRVIsSUFBTSxtQkFBbUIsQ0FDdkIsaUJBRHVCLEVBRXZCLHFCQUZ1QixFQUd2Qix3QkFIdUIsRUFJdkIsZUFKdUIsRUFLdkIsc0JBTHVCLEVBTXZCLHdCQU51QixFQU92QixnQkFQdUIsRUFRdkIsZUFSdUIsRUFTdkIsWUFUdUIsRUFVdkIsWUFWdUIsRUFXdkIsbUJBWHVCLEVBWXZCLGNBWnVCLEVBYXZCLHNCQWJ1QixFQWN2QixnQkFkdUIsRUFldkIsaUJBZnVCLEVBZ0J2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QUFFQTtBQUNBLFlBQVksa0JBQVosR0FBaUMsa0JBQWpDOztBQUVBO0FBQ0EsWUFBWSxJQUFaLEdBQW1CLElBQW5CO0FBQ0EsWUFBWSxJQUFaLEdBQW1CLElBQW5COztBQUVBLE9BQU8sT0FBUCxHQUFpQixXQUFqQiIsImZpbGUiOiJDaGF0U2VydmljZS5qcyIsInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc3RyaWN0J1xuXG4vKipcbiAqIE5vZGUgc3R5bGUgY2FsbGJhY2suIEFsbCBjYWxsYmFja3MgYXJlIG9wdGlvbmFsLCBwcm9taXNlcyBtYXkgYmVcbiAqIHVzZWQgaW5zdGVhZC4gQnV0IG9ubHkgb25lIEFQSSBtdXN0IGJlIHVzZWQgcGVyIGludm9jYXRpb24uXG4gKlxuICogQGNhbGxiYWNrIGNhbGxiYWNrXG4gKiBAcGFyYW0ge0Vycm9yfSBlcnJvclxuICogQHBhcmFtIHsuLi4qfSByZXN1bHRzXG4gKi9cblxuLyoqXG4gKiBTZXJ2ZXIgc2lkZSByZWxhdGVkIGRvY3VtZW50YXRpb24uXG4gKlxuICogQGV4YW1wbGUgPGNhcHRpb24+bnBtIHBhY2thZ2UgdXNhZ2U8L2NhcHRpb24+XG4gKiAgIGxldCBDaGF0U2VydmljZSA9IHJlcXVpcmUoJ2NoYXQtc2VydmljZScpXG4gKlxuICogQG5hbWVzcGFjZSBjaGF0LXNlcnZpY2VcbiAqL1xuXG5jb25zdCBBcmd1bWVudHNWYWxpZGF0b3IgPSByZXF1aXJlKCcuL0FyZ3VtZW50c1ZhbGlkYXRvcicpXG5jb25zdCBDaGF0U2VydmljZUVycm9yID0gcmVxdWlyZSgnLi9DaGF0U2VydmljZUVycm9yJylcbmNvbnN0IE1lbW9yeVN0YXRlID0gcmVxdWlyZSgnLi9NZW1vcnlTdGF0ZScpXG5jb25zdCBQcm9taXNlID0gcmVxdWlyZSgnYmx1ZWJpcmQnKVxuY29uc3QgUmVjb3ZlcnlBUEkgPSByZXF1aXJlKCcuL1JlY292ZXJ5QVBJJylcbmNvbnN0IFJlZGlzU3RhdGUgPSByZXF1aXJlKCcuL1JlZGlzU3RhdGUnKVxuY29uc3QgUm9vbSA9IHJlcXVpcmUoJy4vUm9vbScpXG5jb25zdCBTZXJ2aWNlQVBJID0gcmVxdWlyZSgnLi9TZXJ2aWNlQVBJJylcbmNvbnN0IFNvY2tldElPQ2x1c3RlckJ1cyA9IHJlcXVpcmUoJy4vU29ja2V0SU9DbHVzdGVyQnVzJylcbmNvbnN0IFNvY2tldElPVHJhbnNwb3J0ID0gcmVxdWlyZSgnLi9Tb2NrZXRJT1RyYW5zcG9ydCcpXG5jb25zdCBVc2VyID0gcmVxdWlyZSgnLi9Vc2VyJylcbmNvbnN0IF8gPSByZXF1aXJlKCdsb2Rhc2gnKVxuY29uc3QgdWlkID0gcmVxdWlyZSgndWlkLXNhZmUnKVxuY29uc3QgeyBFdmVudEVtaXR0ZXIgfSA9IHJlcXVpcmUoJ2V2ZW50cycpXG5jb25zdCB7IGNoZWNrTmFtZVN5bWJvbHMsIGNvbnZlcnRFcnJvciwgZXhlY0hvb2ssIGxvZ0Vycm9yIH0gPVxuICAgICAgICByZXF1aXJlKCcuL3V0aWxzJylcbmNvbnN0IHsgbWl4aW4gfSA9IHJlcXVpcmUoJ2VzNi1taXhpbicpXG5cbmNvbnN0IHJwY1JlcXVlc3RzTmFtZXMgPSBbXG4gICdkaXJlY3RBZGRUb0xpc3QnLFxuICAnZGlyZWN0R2V0QWNjZXNzTGlzdCcsXG4gICdkaXJlY3RHZXRXaGl0ZWxpc3RNb2RlJyxcbiAgJ2RpcmVjdE1lc3NhZ2UnLFxuICAnZGlyZWN0UmVtb3ZlRnJvbUxpc3QnLFxuICAnZGlyZWN0U2V0V2hpdGVsaXN0TW9kZScsXG4gICdsaXN0T3duU29ja2V0cycsXG4gICdyb29tQWRkVG9MaXN0JyxcbiAgJ3Jvb21DcmVhdGUnLFxuICAncm9vbURlbGV0ZScsXG4gICdyb29tR2V0QWNjZXNzTGlzdCcsXG4gICdyb29tR2V0T3duZXInLFxuICAncm9vbUdldFdoaXRlbGlzdE1vZGUnLFxuICAncm9vbUhpc3RvcnlHZXQnLFxuICAncm9vbUhpc3RvcnlJbmZvJyxcbiAgJ3Jvb21Kb2luJyxcbiAgJ3Jvb21MZWF2ZScsXG4gICdyb29tTWVzc2FnZScsXG4gICdyb29tTm90aWZpY2F0aW9uc0luZm8nLFxuICAncm9vbVJlY2VudEhpc3RvcnknLFxuICAncm9vbVJlbW92ZUZyb21MaXN0JyxcbiAgJ3Jvb21TZXRXaGl0ZWxpc3RNb2RlJyxcbiAgJ3Jvb21Vc2VyU2VlbicsXG4gICdzeXN0ZW1NZXNzYWdlJ1xuXVxuXG4vKipcbiAqIFNlcnZpY2UgY2xhc3MsIGlzIHRoZSBwYWNrYWdlIGV4cG9ydGVkIG9iamVjdC5cbiAqXG4gKiBAZXh0ZW5kcyBFdmVudEVtaXR0ZXJcbiAqXG4gKiBAbWl4ZXMgY2hhdC1zZXJ2aWNlLlNlcnZpY2VBUElcbiAqIEBtaXhlcyBjaGF0LXNlcnZpY2UuUmVjb3ZlcnlBUElcbiAqXG4gKiBAZmlyZXMgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlLnJlYWR5XG4gKiBAZmlyZXMgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlLmNsb3NlZFxuICogQGZpcmVzIGNoYXQtc2VydmljZS5DaGF0U2VydmljZS5zdG9yZUNvbnNpc3RlbmN5RmFpbHVyZVxuICogQGZpcmVzIGNoYXQtc2VydmljZS5DaGF0U2VydmljZS50cmFuc3BvcnRDb25zaXN0ZW5jeUZhaWx1cmVcbiAqIEBmaXJlcyBjaGF0LXNlcnZpY2UuQ2hhdFNlcnZpY2UubG9ja1RpbWVFeGNlZWRlZFxuICpcbiAqIEBleGFtcGxlIDxjYXB0aW9uPnN0YXJ0aW5nIGEgc2VydmVyPC9jYXB0aW9uPlxuICogICBsZXQgQ2hhdFNlcnZpY2UgPSByZXF1aXJlKCdjaGF0LXNlcnZpY2UnKVxuICogICBsZXQgc2VydmljZSA9IG5ldyBDaGF0U2VydmljZShvcHRpb25zLCBob29rcylcbiAqXG4gKiBAZXhhbXBsZSA8Y2FwdGlvbj5zZXJ2ZXItc2lkZTogYWRkaW5nIGEgcm9vbTwvY2FwdGlvbj5cbiAqICAgbGV0IG93bmVyID0gJ2FkbWluJ1xuICogICBsZXQgd2hpdGVsaXN0T25seSA9IHRydWVcbiAqICAgbGV0IHdoaXRlbGlzdCA9IFsgJ3VzZXInIF1cbiAqICAgbGV0IHN0YXRlID0geyBvd25lciwgd2hpdGVsaXN0T25seSwgd2hpdGVsaXN0IH1cbiAqICAgY2hhdFNlcnZpY2UuYWRkUm9vbSgnc29tZVJvb20nLCBzdGF0ZSkudGhlbihmbilcbiAqXG4gKiBAZXhhbXBsZSA8Y2FwdGlvbj5zZXJ2ZXItc2lkZTogc2VuZGluZyBhIHJvb20gbWVzc2FnZTwvY2FwdGlvbj5cbiAqICAgbGV0IHJvb20gPSAnc29tZVJvb20nXG4gKiAgIGxldCBtc2cgPSB7IHRleHRNZXNzYWdlOiAnc29tZSBtZXNzYWdlJyB9XG4gKiAgIGxldCBjb250ZXh0ID0ge1xuICogICAgIHVzZXJOYW1lOiAnc3lzdGVtJyxcbiAqICAgICBieXBhc3NQZXJtaXNzaW9uczogdHJ1ZVxuICogICB9XG4gKiAgIGNoYXRTZXJ2aWNlLmV4ZWNVc2VyQ29tbWFuZChjb250ZXh0LCAncm9vbU1lc3NhZ2UnLCByb29tLCBtc2cpXG4gKiAgICAgLnRoZW4oZm4pXG4gKlxuICogQGV4YW1wbGUgPGNhcHRpb24+c2VydmVyLXNpZGU6IGpvaW5pbmcgYW4gdXNlciBzb2NrZXQgdG8gYSByb29tPC9jYXB0aW9uPlxuICogICBsZXQgcm9vbSA9ICdzb21lUm9vbSdcbiAqICAgbGV0IGNvbnRleHQgPSB7XG4gKiAgICAgdXNlck5hbWU6ICd1c2VyJyxcbiAqICAgICBpZDogaWQgLy8gc29ja2V0IGlkXG4gKiAgIH1cbiAqICAgY2hhdFNlcnZpY2UuZXhlY1VzZXJDb21tYW5kKGNvbnRleHQsICdyb29tSm9pbicsIHJvb20pXG4gKiAgICAgLnRoZW4oZm4pIC8vIHJlYWwgc29ja2V0cyB3aWxsIGdldCBhIG5vdGlmaWNhdGlvblxuICpcbiAqIEBtZW1iZXJvZiBjaGF0LXNlcnZpY2VcbiAqXG4gKi9cbmNsYXNzIENoYXRTZXJ2aWNlIGV4dGVuZHMgRXZlbnRFbWl0dGVyIHtcblxuICAvKipcbiAgICogQ3JhdGVzIGFuIG9iamVjdCBhbmQgc3RhcnRzIGEgbmV3IHNlcnZpY2UgaW5zdGFuY2UuIFRoZSB7QGxpbmtcbiAgICogY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlI2Nsb3NlfSBtZXRob2QgX19NVVNUX18gYmUgY2FsbGVkIGJlZm9yZVxuICAgKiB0aGUgbm9kZSBwcm9jZXNzIGV4aXQuXG4gICAqXG4gICAqIEBwYXJhbSB7Y2hhdC1zZXJ2aWNlLmNvbmZpZy5vcHRpb25zfSBbb3B0aW9uc10gU2VydmljZVxuICAgKiBjb25maWd1cmF0aW9uIG9wdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSB7Y2hhdC1zZXJ2aWNlLmhvb2tzLkhvb2tzSW50ZXJmYWNlfSBbaG9va3NdIFNlcnZpY2VcbiAgICogY3VzdG9taXNhdGlvbiBob29rcy5cbiAgICovXG4gIGNvbnN0cnVjdG9yIChvcHRpb25zID0ge30sIGhvb2tzID0ge30pIHtcbiAgICBzdXBlcigpXG4gICAgdGhpcy5vcHRpb25zID0gb3B0aW9uc1xuICAgIHRoaXMuaG9va3MgPSBob29rc1xuICAgIHRoaXMuaW5pdFZhcmlhYmxlcygpXG4gICAgdGhpcy5zZXRPcHRpb25zKClcbiAgICB0aGlzLnNldEludGVncmFpb25PcHRpb25zKClcbiAgICB0aGlzLnNldENvbXBvbmVudHMoKVxuICAgIHRoaXMuYXR0YWNoQnVzTGlzdGVuZXJzKClcbiAgICBtaXhpbih0aGlzLCBTZXJ2aWNlQVBJLCB0aGlzLnN0YXRlLFxuICAgICAgICAgICgpID0+IG5ldyBVc2VyKHRoaXMpLCB0aGlzLmNsdXN0ZXJCdXMpXG4gICAgbWl4aW4odGhpcywgUmVjb3ZlcnlBUEksIHRoaXMuc3RhdGUsIHRoaXMudHJhbnNwb3J0LFxuICAgICAgICAgIHRoaXMuZXhlY1VzZXJDb21tYW5kLmJpbmQodGhpcyksIHRoaXMuaW5zdGFuY2VVSUQpXG4gICAgdGhpcy5zdGFydFNlcnZlcigpXG4gIH1cblxuICAvKipcbiAgICogQ2hhdFNlcnZpY2UgZXJyb3JzIGNvbnN0cnVjdG9yLiBUaGlzIGVycm9ycyBhcmUgaW50ZW5kZWQgdG8gYmVcbiAgICogcmV0dXJuZWQgdG8gY2xpZW50cyBhcyBhIHBhcnQgb2YgYSBub3JtYWwgc2VydmljZSBmdW5jdGlvbmluZ1xuICAgKiAoc29tZXRoaW5nIGxpa2UgNDAzIGVycm9ycykuIENhbiBiZSBhbHNvIHVzZWQgdG8gY3JlYXRlIGN1c3RvbVxuICAgKiBlcnJvcnMgc3ViY2xhc3Nlcy5cbiAgICpcbiAgICogQG5hbWUgQ2hhdFNlcnZpY2VFcnJvclxuICAgKiBAdHlwZSBDbGFzc1xuICAgKiBAc3RhdGljXG4gICAqIEByZWFkb25seVxuICAgKlxuICAgKiBAbWVtYmVyb2YgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlXG4gICAqXG4gICAqIEBzZWUgcnBjLmRhdGF0eXBlcy5DaGF0U2VydmljZUVycm9yXG4gICAqL1xuXG4gIC8qKlxuICAgKiBTZXJ2aWNlIGluc3RhbmNlIFVJRC5cbiAgICpcbiAgICogQG5hbWUgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlI2luc3RhbmNlVUlEXG4gICAqIEB0eXBlIHN0cmluZ1xuICAgKiBAcmVhZG9ubHlcbiAgICovXG5cbiAgLyoqXG4gICAqIENsdXN0ZXIgY29tbXVuaWNhdGlvbiB2aWEgYW4gYWRhcHRlci4gRW1pdHMgbWVzc2FnZXMgdG8gYWxsXG4gICAqIHNlcnZpY2VzIG5vZGVzLCBpbmNsdWRpbmcgdGhlIHNlbmRlciBub2RlLlxuICAgKlxuICAgKiBAbmFtZSBjaGF0LXNlcnZpY2UuQ2hhdFNlcnZpY2UjY2x1c3RlckJ1c1xuICAgKiBAdHlwZSBFdmVudEVtaXR0ZXJcbiAgICogQHJlYWRvbmx5XG4gICAqL1xuXG4gIC8qKlxuICAgKiBUcmFuc3BvcnQgb2JqZWN0LlxuICAgKlxuICAgKiBAbmFtZSBjaGF0LXNlcnZpY2UuQ2hhdFNlcnZpY2UjdHJhbnNwb3J0XG4gICAqIEB0eXBlIGNoYXQtc2VydmljZS5UcmFuc3BvcnRJbnRlcmZhY2VcbiAgICogQHJlYWRvbmx5XG4gICAqL1xuXG4gIC8qKlxuICAgKiBTZXJ2aWNlIGlzIHJlYWR5LCBzdGF0ZSBhbmQgdHJhbnNwb3J0IGFyZSB1cC5cbiAgICogQGV2ZW50IHJlYWR5XG4gICAqXG4gICAqIEBtZW1iZXJvZiBjaGF0LXNlcnZpY2UuQ2hhdFNlcnZpY2VcbiAgICovXG5cbiAgLyoqXG4gICAqIFNlcnZpY2UgaXMgY2xvc2VkLCBzdGF0ZSBhbmQgdHJhbnNwb3J0IGFyZSBjbG9zZWQuXG4gICAqIEBldmVudCBjbG9zZWRcbiAgICogQHBhcmFtIHtFcnJvcn0gW2Vycm9yXSBJZiB3YXMgY2xvc2VkIGR1ZSB0byBhbiBlcnJvci5cbiAgICpcbiAgICogQG1lbWJlcm9mIGNoYXQtc2VydmljZS5DaGF0U2VydmljZVxuICAgKi9cblxuICAvKipcbiAgICogU3RhdGUgc3RvcmUgZmFpbGVkIHRvIGJlIHVwZGF0ZWQgdG8gcmVmbGVjdCBhbiB1c2VyJ3MgY29ubmVjdGlvblxuICAgKiBvciBwcmVzZW5jZSBzdGF0ZS5cbiAgICpcbiAgICogQGV2ZW50IHN0b3JlQ29uc2lzdGVuY3lGYWlsdXJlXG4gICAqIEBwYXJhbSB7RXJyb3J9IGVycm9yIEVycm9yLlxuICAgKiBAcGFyYW0ge09iamVjdH0gb3BlcmF0aW9uSW5mbyBPcGVyYXRpb24gZGV0YWlscy5cbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IG9wZXJhdGlvbkluZm8udXNlck5hbWUgVXNlciBuYW1lLlxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gb3BlcmF0aW9uSW5mby5vcFR5cGUgT3BlcmF0aW9uIHR5cGUuXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbb3BlcmF0aW9uSW5mby5yb29tTmFtZV0gUm9vbSBuYW1lLlxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gW29wZXJhdGlvbkluZm8uaWRdIFNvY2tldCBpZC5cbiAgICpcbiAgICogQHNlZSBjaGF0LXNlcnZpY2UuUmVjb3ZlcnlBUElcbiAgICpcbiAgICogQG1lbWJlcm9mIGNoYXQtc2VydmljZS5DaGF0U2VydmljZVxuICAgKi9cblxuICAvKipcbiAgICogRmFpbGVkIHRvIHRlYXJkb3duIGEgdHJhbnNwb3J0IGNvbm5lY3Rpb24uXG4gICAqXG4gICAqIEBldmVudCB0cmFuc3BvcnRDb25zaXN0ZW5jeUZhaWx1cmVcbiAgICpcbiAgICogQHBhcmFtIHtFcnJvcn0gZXJyb3IgRXJyb3IuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBvcGVyYXRpb25JbmZvIE9wZXJhdGlvbiBkZXRhaWxzLlxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gb3BlcmF0aW9uSW5mby51c2VyTmFtZSBVc2VyIG5hbWUuXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBvcGVyYXRpb25JbmZvLm9wVHlwZSBPcGVyYXRpb24gdHlwZS5cbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IFtvcGVyYXRpb25JbmZvLnJvb21OYW1lXSBSb29tIG5hbWUuXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbb3BlcmF0aW9uSW5mby5pZF0gU29ja2V0IGlkLlxuICAgKlxuICAgKiBAbWVtYmVyb2YgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlXG4gICAqL1xuXG4gIC8qKlxuICAgKiBMb2NrIHdhcyBob2xkIGxvbmdlciB0aGFuIGEgbG9jayB0dGwuXG4gICAqXG4gICAqIEBldmVudCBsb2NrVGltZUV4Y2VlZGVkXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpZCBMb2NrIGlkLlxuICAgKiBAcGFyYW0ge09iamVjdH0gbG9ja0luZm8gTG9jayByZXNvdXJjZSBkZXRhaWxzLlxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gW2xvY2tJbmZvLnVzZXJOYW1lXSBVc2VyIG5hbWUuXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbbG9ja0luZm8ucm9vbU5hbWVdIFJvb20gbmFtZS5cbiAgICpcbiAgICogQHNlZSBjaGF0LXNlcnZpY2UuUmVjb3ZlcnlBUElcbiAgICpcbiAgICogQG1lbWJlcm9mIGNoYXQtc2VydmljZS5DaGF0U2VydmljZVxuICAgKi9cblxuICAvKipcbiAgICogRXhwb3NlcyBhbiBpbnRlcm5hbCBhcmd1bWVudHMgdmFsaWRhdGlvbiBtZXRob2QsIGl0IGlzIHJ1blxuICAgKiBhdXRvbWF0aWNhbGx5IGJ5IGFsbCBjbGllbnQgcmVxdWVzdCAoY29tbWFuZCkgaGFuZGxlcnMuXG4gICAqXG4gICAqIEBtZXRob2QgY2hhdC1zZXJ2aWNlLkNoYXRTZXJ2aWNlI2NoZWNrQXJndW1lbnRzXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIENvbW1hbmQgbmFtZS5cbiAgICogQHBhcmFtIHsuLi4qfSBhcmdzIENvbW1hbmQgYXJndW1lbnRzLlxuICAgKiBAcGFyYW0ge2NhbGxiYWNrfSBbY2JdIE9wdGlvbmFsIGNhbGxiYWNrLlxuICAgKlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPHVuZGVmaW5lZD59IFByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRob3V0IGFueVxuICAgKiBkYXRhIGlmIHZhbGlkYXRpb24gaXMgc3VjY2Vzc2Z1bCwgb3RoZXJ3aXNlIGEgcHJvbWlzZSBpc1xuICAgKiByZWplY3RlZC5cbiAgICovXG5cbiAgaW5pdFZhcmlhYmxlcyAoKSB7XG4gICAgdGhpcy5pbnN0YW5jZVVJRCA9IHVpZC5zeW5jKDE4KVxuICAgIHRoaXMucnVubmluZ0NvbW1hbmRzID0gMFxuICAgIHRoaXMucnBjUmVxdWVzdHNOYW1lcyA9IHJwY1JlcXVlc3RzTmFtZXNcbiAgICB0aGlzLmNsb3NlZCA9IGZhbHNlXG4gICAgLy8gY29uc3RhbnRzXG4gICAgdGhpcy5DaGF0U2VydmljZUVycm9yID0gQ2hhdFNlcnZpY2VFcnJvclxuICAgIHRoaXMuU29ja2V0SU9DbHVzdGVyQnVzID0gU29ja2V0SU9DbHVzdGVyQnVzXG4gICAgdGhpcy5Vc2VyID0gVXNlclxuICAgIHRoaXMuUm9vbSA9IFJvb21cbiAgfVxuXG4gIHNldE9wdGlvbnMgKCkge1xuICAgIHRoaXMuY2xvc2VUaW1lb3V0ID0gdGhpcy5vcHRpb25zLmNsb3NlVGltZW91dCB8fCAxNTAwMFxuICAgIHRoaXMuYnVzQWNrVGltZW91dCA9IHRoaXMub3B0aW9ucy5idXNBY2tUaW1lb3V0IHx8IDUwMDBcbiAgICB0aGlzLmhlYXJ0YmVhdFJhdGUgPSB0aGlzLm9wdGlvbnMuaGVhcnRiZWF0UmF0ZSB8fCAxMDAwMFxuICAgIHRoaXMuaGVhcnRiZWF0VGltZW91dCA9IHRoaXMub3B0aW9ucy5oZWFydGJlYXRUaW1lb3V0IHx8IDMwMDAwXG4gICAgdGhpcy5kaXJlY3RMaXN0U2l6ZUxpbWl0ID0gdGhpcy5vcHRpb25zLmRpcmVjdExpc3RTaXplTGltaXQgfHwgMTAwMFxuICAgIHRoaXMucm9vbUxpc3RTaXplTGltaXQgPSB0aGlzLm9wdGlvbnMucm9vbUxpc3RTaXplTGltaXQgfHwgMTAwMDBcbiAgICB0aGlzLmVuYWJsZUFjY2Vzc0xpc3RzVXBkYXRlcyA9XG4gICAgICB0aGlzLm9wdGlvbnMuZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzIHx8IGZhbHNlXG4gICAgdGhpcy5lbmFibGVEaXJlY3RNZXNzYWdlcyA9IHRoaXMub3B0aW9ucy5lbmFibGVEaXJlY3RNZXNzYWdlcyB8fCBmYWxzZVxuICAgIHRoaXMuZW5hYmxlUm9vbXNNYW5hZ2VtZW50ID0gdGhpcy5vcHRpb25zLmVuYWJsZVJvb21zTWFuYWdlbWVudCB8fCBmYWxzZVxuICAgIHRoaXMuZW5hYmxlVXNlcmxpc3RVcGRhdGVzID0gdGhpcy5vcHRpb25zLmVuYWJsZVVzZXJsaXN0VXBkYXRlcyB8fCBmYWxzZVxuICAgIHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzID0gdGhpcy5vcHRpb25zLmhpc3RvcnlNYXhHZXRNZXNzYWdlc1xuICAgIGlmICghXy5pc051bWJlcih0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcykgfHxcbiAgICAgICAgdGhpcy5oaXN0b3J5TWF4R2V0TWVzc2FnZXMgPCAwKSB7XG4gICAgICB0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcyA9IDEwMFxuICAgIH1cbiAgICB0aGlzLmhpc3RvcnlNYXhTaXplID0gdGhpcy5vcHRpb25zLmhpc3RvcnlNYXhTaXplXG4gICAgaWYgKCFfLmlzTnVtYmVyKHRoaXMuaGlzdG9yeU1heFNpemUpIHx8XG4gICAgICAgIHRoaXMuaGlzdG9yeU1heFNpemUgPCAwKSB7XG4gICAgICB0aGlzLmhpc3RvcnlNYXhTaXplID0gMTAwMDBcbiAgICB9XG4gICAgdGhpcy5wb3J0ID0gdGhpcy5vcHRpb25zLnBvcnQgfHwgODAwMFxuICAgIHRoaXMuZGlyZWN0TWVzc2FnZXNDaGVja2VyID0gdGhpcy5ob29rcy5kaXJlY3RNZXNzYWdlc0NoZWNrZXJcbiAgICB0aGlzLnJvb21NZXNzYWdlc0NoZWNrZXIgPSB0aGlzLmhvb2tzLnJvb21NZXNzYWdlc0NoZWNrZXJcbiAgICB0aGlzLnVzZVJhd0Vycm9yT2JqZWN0cyA9IHRoaXMub3B0aW9ucy51c2VSYXdFcnJvck9iamVjdHMgfHwgZmFsc2VcbiAgfVxuXG4gIHNldEludGVncmFpb25PcHRpb25zICgpIHtcbiAgICB0aGlzLmFkYXB0ZXJDb25zdHJ1Y3RvciA9IHRoaXMub3B0aW9ucy5hZGFwdGVyIHx8ICdtZW1vcnknXG4gICAgdGhpcy5hZGFwdGVyT3B0aW9ucyA9IF8uY2FzdEFycmF5KHRoaXMub3B0aW9ucy5hZGFwdGVyT3B0aW9ucylcblxuICAgIHRoaXMuc3RhdGVDb25zdHJ1Y3RvciA9IHRoaXMub3B0aW9ucy5zdGF0ZSB8fCAnbWVtb3J5J1xuICAgIHRoaXMuc3RhdGVPcHRpb25zID0gdGhpcy5vcHRpb25zLnN0YXRlT3B0aW9ucyB8fCB7fVxuXG4gICAgdGhpcy50cmFuc3BvcnRDb25zdHJ1Y3RvciA9IHRoaXMub3B0aW9ucy50cmFuc3BvcnQgfHwgJ3NvY2tldC5pbydcbiAgICB0aGlzLnRyYW5zcG9ydE9wdGlvbnMgPSB0aGlzLm9wdGlvbnMudHJhbnNwb3J0T3B0aW9ucyB8fCB7fVxuICB9XG5cbiAgc2V0Q29tcG9uZW50cyAoKSB7XG4gICAgbGV0IFN0YXRlID0gKCgpID0+IHtcbiAgICAgIHN3aXRjaCAodHJ1ZSkge1xuICAgICAgICBjYXNlIHRoaXMuc3RhdGVDb25zdHJ1Y3RvciA9PT0gJ21lbW9yeSc6XG4gICAgICAgICAgcmV0dXJuIE1lbW9yeVN0YXRlXG4gICAgICAgIGNhc2UgdGhpcy5zdGF0ZUNvbnN0cnVjdG9yID09PSAncmVkaXMnOlxuICAgICAgICAgIHJldHVybiBSZWRpc1N0YXRlXG4gICAgICAgIGNhc2UgXy5pc0Z1bmN0aW9uKHRoaXMuc3RhdGVDb25zdHJ1Y3Rvcik6XG4gICAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGVDb25zdHJ1Y3RvclxuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBzdGF0ZTogJHt0aGlzLnN0YXRlQ29uc3RydWN0b3J9YClcbiAgICAgIH1cbiAgICB9KSgpXG4gICAgbGV0IFRyYW5zcG9ydCA9ICgoKSA9PiB7XG4gICAgICBzd2l0Y2ggKHRydWUpIHtcbiAgICAgICAgY2FzZSB0aGlzLnRyYW5zcG9ydENvbnN0cnVjdG9yID09PSAnc29ja2V0LmlvJzpcbiAgICAgICAgICByZXR1cm4gU29ja2V0SU9UcmFuc3BvcnRcbiAgICAgICAgY2FzZSBfLmlzRnVuY3Rpb24odGhpcy50cmFuc3BvcnRDb25zdHJ1Y3Rvcik6XG4gICAgICAgICAgcmV0dXJuIHRoaXMudHJhbnNwb3J0Q29uc3RydWN0b3JcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgdHJhbnNwb3J0OiAke3RoaXMudHJhbnNwb3J0Q29uc3RydWN0b3J9YClcbiAgICAgIH1cbiAgICB9KSgpXG4gICAgdGhpcy52YWxpZGF0b3IgPSBuZXcgQXJndW1lbnRzVmFsaWRhdG9yKHRoaXMpXG4gICAgdGhpcy5jaGVja0FyZ3VtZW50cyA9IHRoaXMudmFsaWRhdG9yLmNoZWNrQXJndW1lbnRzLmJpbmQodGhpcy52YWxpZGF0b3IpXG4gICAgdGhpcy5zdGF0ZSA9IG5ldyBTdGF0ZSh0aGlzLCB0aGlzLnN0YXRlT3B0aW9ucylcbiAgICB0aGlzLnRyYW5zcG9ydCA9IG5ldyBUcmFuc3BvcnQoXG4gICAgICB0aGlzLCB0aGlzLnRyYW5zcG9ydE9wdGlvbnMsXG4gICAgICB0aGlzLmFkYXB0ZXJDb25zdHJ1Y3RvciwgdGhpcy5hZGFwdGVyT3B0aW9ucylcbiAgICB0aGlzLmNsdXN0ZXJCdXMgPSB0aGlzLnRyYW5zcG9ydC5jbHVzdGVyQnVzXG4gIH1cblxuICBhdHRhY2hCdXNMaXN0ZW5lcnMgKCkge1xuICAgIHRoaXMuY2x1c3RlckJ1cy5vbigncm9vbUxlYXZlU29ja2V0JywgKGlkLCByb29tTmFtZSkgPT4ge1xuICAgICAgcmV0dXJuIHRoaXMudHJhbnNwb3J0LmxlYXZlQ2hhbm5lbChpZCwgcm9vbU5hbWUpXG4gICAgICAgIC50aGVuKCgpID0+IHRoaXMuY2x1c3RlckJ1cy5lbWl0KCdzb2NrZXRSb29tTGVmdCcsIGlkLCByb29tTmFtZSkpXG4gICAgICAgIC5jYXRjaFJldHVybigpXG4gICAgfSlcbiAgICB0aGlzLmNsdXN0ZXJCdXMub24oJ2Rpc2Nvbm5lY3RVc2VyU29ja2V0cycsIHVzZXJOYW1lID0+IHtcbiAgICAgIHJldHVybiB0aGlzLnN0YXRlLmdldFVzZXIodXNlck5hbWUpXG4gICAgICAgIC50aGVuKHVzZXIgPT4gdXNlci5kaXNjb25uZWN0SW5zdGFuY2VTb2NrZXRzKCkpXG4gICAgICAgIC5jYXRjaFJldHVybigpXG4gICAgfSlcbiAgfVxuXG4gIC8vIGZvciB0cmFuc3BvcnQgcGx1Z2lucyBpbnRlZ3JhdGlvblxuICBjb252ZXJ0RXJyb3IgKGVycm9yKSB7XG4gICAgcmV0dXJuIGNvbnZlcnRFcnJvcihlcnJvciwgdGhpcy51c2VSYXdFcnJvck9iamVjdHMpXG4gIH1cblxuICAvLyBmb3IgdHJhbnNwb3J0IHBsdWdpbnMgaW50ZWdyYXRpb25cbiAgb25Db25uZWN0IChpZCkge1xuICAgIGlmICh0aGlzLmhvb2tzLm9uQ29ubmVjdCkge1xuICAgICAgcmV0dXJuIFByb21pc2UudHJ5KCgpID0+IHtcbiAgICAgICAgcmV0dXJuIGV4ZWNIb29rKHRoaXMuaG9va3Mub25Db25uZWN0LCB0aGlzLCBpZClcbiAgICAgIH0pLnRoZW4obG9naW5EYXRhID0+IHtcbiAgICAgICAgbG9naW5EYXRhID0gXy5jYXN0QXJyYXkobG9naW5EYXRhKVxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGxvZ2luRGF0YSlcbiAgICAgIH0pLmNhdGNoKGVycm9yID0+IGxvZ0Vycm9yKGVycm9yKSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShbXSlcbiAgICB9XG4gIH1cblxuICAvLyBmb3IgdHJhbnNwb3J0IHBsdWdpbnMgaW50ZWdyYXRpb25cbiAgcmVnaXN0ZXJDbGllbnQgKHVzZXJOYW1lLCBpZCkge1xuICAgIHJldHVybiBjaGVja05hbWVTeW1ib2xzKHVzZXJOYW1lKVxuICAgICAgLnRoZW4oKCkgPT4gdGhpcy5zdGF0ZS5nZXRPckFkZFVzZXIodXNlck5hbWUpKVxuICAgICAgLnRoZW4odXNlciA9PiB1c2VyLnJlZ2lzdGVyU29ja2V0KGlkKSlcbiAgICAgIC5jYXRjaChlcnJvciA9PiBsb2dFcnJvcihlcnJvcikpXG4gIH1cblxuICB3YWl0Q29tbWFuZHMgKCkge1xuICAgIGlmICh0aGlzLnJ1bm5pbmdDb21tYW5kcyA+IDApIHtcbiAgICAgIHJldHVybiBQcm9taXNlLmZyb21DYWxsYmFjayhjYiA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLm9uY2UoJ2NvbW1hbmRzRmluaXNoZWQnLCBjYilcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICAgIH1cbiAgfVxuXG4gIGNsb3NlVHJhbnNwb3J0ICgpIHtcbiAgICByZXR1cm4gdGhpcy50cmFuc3BvcnQuY2xvc2UoKVxuICAgICAgLnRoZW4oKCkgPT4gdGhpcy53YWl0Q29tbWFuZHMoKSlcbiAgICAgIC50aW1lb3V0KHRoaXMuY2xvc2VUaW1lb3V0KVxuICB9XG5cbiAgc3RhcnRTZXJ2ZXIgKCkge1xuICAgIHJldHVybiBQcm9taXNlLnRyeSgoKSA9PiB7XG4gICAgICBpZiAodGhpcy5ob29rcy5vblN0YXJ0KSB7XG4gICAgICAgIHJldHVybiB0aGlzLmNsdXN0ZXJCdXMubGlzdGVuKClcbiAgICAgICAgICAudGhlbigoKSA9PiBleGVjSG9vayh0aGlzLmhvb2tzLm9uU3RhcnQsIHRoaXMpKVxuICAgICAgICAgIC50aGVuKCgpID0+IHRoaXMudHJhbnNwb3J0LnNldEV2ZW50cygpKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gdGVzdHMgc3BlYyBjb21wYXRpYmlsaXR5XG4gICAgICAgIHRoaXMudHJhbnNwb3J0LnNldEV2ZW50cygpXG4gICAgICAgIHJldHVybiB0aGlzLmNsdXN0ZXJCdXMubGlzdGVuKClcbiAgICAgIH1cbiAgICB9KS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMuc3RhdGUudXBkYXRlSGVhcnRiZWF0KClcbiAgICAgIGxldCBoYnVwZGF0ZXIgPSB0aGlzLnN0YXRlLnVwZGF0ZUhlYXJ0YmVhdC5iaW5kKHRoaXMuc3RhdGUpXG4gICAgICB0aGlzLmhidGltZXIgPSBzZXRJbnRlcnZhbChoYnVwZGF0ZXIsIHRoaXMuaGVhcnRiZWF0UmF0ZSlcbiAgICAgIHJldHVybiB0aGlzLmVtaXQoJ3JlYWR5JylcbiAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICB0aGlzLmNsb3NlZCA9IHRydWVcbiAgICAgIHJldHVybiB0aGlzLmNsb3NlVHJhbnNwb3J0KClcbiAgICAgICAgLnRoZW4oKCkgPT4gdGhpcy5zdGF0ZS5jbG9zZSgpKVxuICAgICAgICAuZmluYWxseSgoKSA9PiB0aGlzLmVtaXQoJ2Nsb3NlZCcsIGVycm9yKSlcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyBzZXJ2ZXIuXG4gICAqIEBub3RlIF9fTVVTVF9fIGJlIGNhbGxlZCBiZWZvcmUgbm9kZSBwcm9jZXNzIHNodXRkb3duIHRvIGNvcnJlY3RseVxuICAgKiAgIHVwZGF0ZSB0aGUgc3RhdGUuXG4gICAqIEBwYXJhbSB7Y2FsbGJhY2t9IFtjYl0gT3B0aW9uYWwgY2FsbGJhY2suXG4gICAqIEByZXR1cm4ge1Byb21pc2U8dW5kZWZpbmVkPn0gUHJvbWlzZSB0aGF0IHJlc29sdmVzIHdpdGhvdXQgYW55IGRhdGEuXG4gICAqL1xuICBjbG9zZSAoY2IpIHtcbiAgICBpZiAodGhpcy5jbG9zZWQpIHsgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpIH1cbiAgICB0aGlzLmNsb3NlZCA9IHRydWVcbiAgICBjbGVhckludGVydmFsKHRoaXMuaGJ0aW1lcilcbiAgICBsZXQgY2xvc2VFcnJvciA9IG51bGxcbiAgICByZXR1cm4gdGhpcy5jbG9zZVRyYW5zcG9ydCgpLnRoZW4oXG4gICAgICAoKSA9PiBleGVjSG9vayh0aGlzLmhvb2tzLm9uQ2xvc2UsIHRoaXMsIG51bGwpLFxuICAgICAgZXJyb3IgPT4ge1xuICAgICAgICBpZiAodGhpcy5ob29rcy5vbkNsb3NlKSB7XG4gICAgICAgICAgcmV0dXJuIGV4ZWNIb29rKHRoaXMuaG9va3Mub25DbG9zZSwgdGhpcywgZXJyb3IpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKVxuICAgICAgICB9XG4gICAgICB9KS5jYXRjaChlcnJvciA9PiB7XG4gICAgICAgIGNsb3NlRXJyb3IgPSBlcnJvclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICB9KS5maW5hbGx5KCgpID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGUuY2xvc2UoKVxuICAgICAgICAgIC5maW5hbGx5KCgpID0+IHRoaXMuZW1pdCgnY2xvc2VkJywgY2xvc2VFcnJvcikpXG4gICAgICB9KS5hc0NhbGxiYWNrKGNiKVxuICB9XG59XG5cbi8vIGZvciBjdXN0b20gZXJyb3JzXG5DaGF0U2VydmljZS5DaGF0U2VydmljZUVycm9yID0gQ2hhdFNlcnZpY2VFcnJvclxuXG4vLyBmb3IgdHJhbnNwb3J0IHBsdWdpbiBpbXBsZW1lbnRhdGlvbnNcbkNoYXRTZXJ2aWNlLlNvY2tldElPQ2x1c3RlckJ1cyA9IFNvY2tldElPQ2x1c3RlckJ1c1xuXG4vLyBmb3Igc3RvcmUgcGx1Z2luIGltcGxlbWVudGF0aW9uc1xuQ2hhdFNlcnZpY2UuVXNlciA9IFVzZXJcbkNoYXRTZXJ2aWNlLlJvb20gPSBSb29tXG5cbm1vZHVsZS5leHBvcnRzID0gQ2hhdFNlcnZpY2VcbiJdfQ==
\No newline at end of file