1 | ;
|
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 |
|
21 | var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
|
22 |
|
23 | var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
|
24 |
|
25 | var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
|
26 |
|
27 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
|
28 |
|
29 | var _createClass2 = require('babel-runtime/helpers/createClass');
|
30 |
|
31 | var _createClass3 = _interopRequireDefault(_createClass2);
|
32 |
|
33 | var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
|
34 |
|
35 | var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
|
36 |
|
37 | var _inherits2 = require('babel-runtime/helpers/inherits');
|
38 |
|
39 | var _inherits3 = _interopRequireDefault(_inherits2);
|
40 |
|
41 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
42 |
|
43 | var ArgumentsValidator = require('./ArgumentsValidator');
|
44 | var ChatServiceError = require('./ChatServiceError');
|
45 | var MemoryState = require('./MemoryState');
|
46 | var Promise = require('bluebird');
|
47 | var RecoveryAPI = require('./RecoveryAPI');
|
48 | var RedisState = require('./RedisState');
|
49 | var Room = require('./Room');
|
50 | var ServiceAPI = require('./ServiceAPI');
|
51 | var SocketIOClusterBus = require('./SocketIOClusterBus');
|
52 | var SocketIOTransport = require('./SocketIOTransport');
|
53 | var User = require('./User');
|
54 | var _ = require('lodash');
|
55 | var uid = require('uid-safe');
|
56 |
|
57 | var _require = require('events');
|
58 |
|
59 | var EventEmitter = _require.EventEmitter;
|
60 |
|
61 | var _require2 = require('./utils');
|
62 |
|
63 | var checkNameSymbols = _require2.checkNameSymbols;
|
64 | var _convertError = _require2.convertError;
|
65 | var execHook = _require2.execHook;
|
66 | var logError = _require2.logError;
|
67 |
|
68 | var _require3 = require('es6-mixin');
|
69 |
|
70 | var mixin = _require3.mixin;
|
71 |
|
72 |
|
73 | var 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 |
|
123 | var 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 |
|
519 | ChatService.ChatServiceError = ChatServiceError;
|
520 |
|
521 | // for transport plugin implementations
|
522 | ChatService.SocketIOClusterBus = SocketIOClusterBus;
|
523 |
|
524 | // for store plugin implementations
|
525 | ChatService.User = User;
|
526 | ChatService.Room = Room;
|
527 |
|
528 | module.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 |