UNPKG

87.8 kBJavaScriptView Raw
1'use strict';
2
3var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
4
5var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
6
7var _getIterator2 = require('babel-runtime/core-js/get-iterator');
8
9var _getIterator3 = _interopRequireDefault(_getIterator2);
10
11var _stringify = require('babel-runtime/core-js/json/stringify');
12
13var _stringify2 = _interopRequireDefault(_stringify);
14
15var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray');
16
17var _slicedToArray3 = _interopRequireDefault(_slicedToArray2);
18
19var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
20
21var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
22
23var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
24
25var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
26
27var _inherits2 = require('babel-runtime/helpers/inherits');
28
29var _inherits3 = _interopRequireDefault(_inherits2);
30
31var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
32
33var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
34
35var _createClass2 = require('babel-runtime/helpers/createClass');
36
37var _createClass3 = _interopRequireDefault(_createClass2);
38
39function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
40
41var ChatServiceError = require('./ChatServiceError');
42var Promise = require('bluebird');
43var Redis = require('ioredis');
44var Room = require('./Room');
45var User = require('./User');
46var _ = require('lodash');
47var promiseRetry = require('promise-retry');
48var uid = require('uid-safe');
49
50var _require = require('es6-mixin');
51
52var mixin = _require.mixin;
53
54
55var namespace = 'chatservice';
56
57function initSet(redis, set, values) {
58 return redis.del(set).then(function () {
59 if (!values) {
60 return Promise.resolve();
61 } else {
62 return redis.sadd(set, values);
63 }
64 });
65}
66
67// State init/remove operations.
68
69var StateOperations = function () {
70 function StateOperations(name, exitsErrorName, redis, makeKeyName, stateReset) {
71 (0, _classCallCheck3.default)(this, StateOperations);
72
73 this.name = name;
74 this.exitsErrorName = exitsErrorName;
75 this.redis = redis;
76 this.makeKeyName = makeKeyName;
77 this.stateReset = stateReset;
78 }
79
80 (0, _createClass3.default)(StateOperations, [{
81 key: 'initState',
82 value: function initState(state) {
83 var _this = this;
84
85 return this.redis.setnx(this.makeKeyName('exists'), true).then(function (isnew) {
86 if (!isnew) {
87 var error = new ChatServiceError(_this.exitsErrorName, _this.name);
88 return Promise.reject(error);
89 } else {
90 return Promise.resolve();
91 }
92 }).then(function () {
93 return _this.stateReset(state);
94 }).then(function () {
95 return _this.redis.setnx(_this.makeKeyName('isInit'), true);
96 });
97 }
98 }, {
99 key: 'removeState',
100 value: function removeState() {
101 var _this2 = this;
102
103 return this.stateReset().then(function () {
104 return _this2.redis.del(_this2.makeKeyName('exists'), _this2.makeKeyName('isInit'));
105 });
106 }
107 }, {
108 key: 'startRemoving',
109 value: function startRemoving() {
110 return this.redis.del(this.makeKeyName('isInit'));
111 }
112 }]);
113 return StateOperations;
114}();
115
116// Redis lock operations.
117
118
119var LockOperations = function () {
120 function LockOperations(redis) {
121 (0, _classCallCheck3.default)(this, LockOperations);
122
123 this.redis = redis;
124 }
125
126 (0, _createClass3.default)(LockOperations, [{
127 key: 'lock',
128 value: function lock(key, val, ttl) {
129 var _this3 = this;
130
131 return promiseRetry({ minTimeout: 100, retries: 10, factor: 1.5, randomize: true }, function (retry, n) {
132 return _this3.redis.set(key, val, 'NX', 'PX', ttl).then(function (res) {
133 if (!res) {
134 var error = new ChatServiceError('timeout');
135 return retry(error);
136 } else {
137 return null;
138 }
139 }).catch(retry);
140 });
141 }
142 }, {
143 key: 'unlock',
144 value: function unlock(key, val) {
145 return this.redis.unlock(key, val);
146 }
147 }]);
148 return LockOperations;
149}();
150
151// Redis scripts.
152
153
154var luaCommands = {
155 unlock: {
156 numberOfKeys: 1,
157 lua: '\nif redis.call("get",KEYS[1]) == ARGV[1] then\n return redis.call("del",KEYS[1])\nelse\n return 0\nend'
158 },
159
160 messageAdd: {
161 numberOfKeys: 5,
162 lua: '\nlocal msg = ARGV[1]\nlocal ts = ARGV[2]\n\nlocal lastMessageId = KEYS[1]\nlocal historyMaxSize = KEYS[2]\nlocal messagesIds = KEYS[3]\nlocal messagesTimestamps = KEYS[4]\nlocal messagesHistory = KEYS[5]\n\nlocal id = tonumber(redis.call(\'INCR\', lastMessageId))\nlocal maxsz = tonumber(redis.call(\'GET\', historyMaxSize))\n\nredis.call(\'LPUSH\', messagesIds, id)\nredis.call(\'LPUSH\', messagesTimestamps, ts)\nredis.call(\'LPUSH\', messagesHistory, msg)\n\nlocal sz = tonumber(redis.call(\'LLEN\', messagesHistory))\n\nif sz > maxsz then\n redis.call(\'RPOP\', messagesIds)\n redis.call(\'RPOP\', messagesTimestamps)\n redis.call(\'RPOP\', messagesHistory)\nend\n\nreturn {id}'
163 },
164
165 messagesGet: {
166 numberOfKeys: 5,
167 lua: '\nlocal id = ARGV[1]\nlocal maxlen = ARGV[2]\n\nlocal lastMessageId = KEYS[1]\nlocal historyMaxSize = KEYS[2]\nlocal messagesIds = KEYS[3]\nlocal messagesTimestamps = KEYS[4]\nlocal messagesHistory = KEYS[5]\n\nlocal lastid = tonumber(redis.call(\'GET\', lastMessageId))\nlocal maxsz = tonumber(redis.call(\'GET\', historyMaxSize))\nlocal id = math.min(id, lastid)\nlocal endp = lastid - id\nlocal len = math.min(maxlen, endp)\nlocal start = math.max(0, endp - len)\n\nif start >= endp then\n return {}\nend\n\nendp = endp - 1\nlocal msgs = redis.call(\'LRANGE\', messagesHistory, start, endp)\nlocal tss = redis.call(\'LRANGE\', messagesTimestamps, start, endp)\nlocal ids = redis.call(\'LRANGE\', messagesIds, start, endp)\n\nreturn {msgs, tss, ids}'
168 },
169
170 getSocketsToRooms: {
171 numberOfKeys: 1,
172 lua: '\nlocal result = {}\nlocal sockets = KEYS[1]\nlocal prefix = ARGV[1]\nlocal ids = redis.call(\'HKEYS\', sockets)\n\nif table.getn(ids) == 0 then\n local jsonResult = cjson.encode(cjson.null)\n return {jsonResult}\nend\n\nfor i, id in pairs(ids) do\n local joined = redis.call(\'SMEMBERS\', prefix .. id)\n result[id] = joined\nend\n\nlocal jsonResult = cjson.encode(result)\nreturn {jsonResult}'
173 },
174
175 removeAllSocketsFromRoom: {
176 numberOfKeys: 1,
177 lua: '\nlocal room = KEYS[1]\nlocal prefix = ARGV[1]\nlocal roomName = ARGV[2]\nlocal ids = redis.call(\'SMEMBERS\', room)\n\nif table.getn(ids) == 0 then\n local jsonResult = cjson.encode(cjson.null)\n return {jsonResult}\nend\n\nredis.call(\'DEL\', room)\n\nfor i, id in pairs(ids) do\n redis.call(\'SREM\', prefix .. id, roomName)\nend\n\nlocal jsonResult = cjson.encode(ids)\nreturn {jsonResult}'
178 },
179
180 removeSocket: {
181 numberOfKeys: 2,
182 lua: '\nlocal id = KEYS[1]\nlocal sockets = KEYS[2]\nlocal prefix = ARGV[1]\nlocal socketid = ARGV[2]\n\nlocal rooms = redis.call(\'SMEMBERS\', id)\nredis.call(\'DEL\', id)\n\nredis.call(\'HDEL\', sockets, socketid)\nlocal nconnected = redis.call(\'HLEN\', sockets)\n\nlocal removedRooms = {}\nlocal joinedSockets = {}\n\nfor i, room in pairs(rooms) do\n local ismember = redis.call(\'SISMEMBER\', prefix .. room, socketid)\n if ismember == 1 then\n redis.call(\'SREM\', prefix .. room, socketid)\n local njoined = redis.call(\'SCARD\', prefix .. room)\n table.insert(removedRooms, room)\n table.insert(joinedSockets, njoined)\n end\nend\n\nif table.getn(removedRooms) == 0 or table.getn(rooms) == 0 then\n local jsonResult = cjson.encode({cjson.null, cjson.null, nconnected})\n return {jsonResult}\nend\n\nlocal jsonResult = cjson.encode({removedRooms, joinedSockets, nconnected})\nreturn {jsonResult}'
183 }
184
185};
186
187// Implements state API lists management.
188
189var ListsStateRedis = function () {
190 function ListsStateRedis() {
191 (0, _classCallCheck3.default)(this, ListsStateRedis);
192 }
193
194 (0, _createClass3.default)(ListsStateRedis, [{
195 key: 'makeKeyName',
196 value: function makeKeyName(keyName) {
197 return namespace + ':' + this.prefix + ':{' + this.name + '}:' + keyName;
198 }
199 }, {
200 key: 'checkList',
201 value: function checkList(listName, num, limit) {
202 if (!this.hasList(listName)) {
203 var error = new ChatServiceError('noList', listName);
204 return Promise.reject(error);
205 }
206 if (listName === 'userlist') {
207 return Promise.resolve();
208 }
209 return this.redis.scard(listName).then(function (sz) {
210 if (sz + num > limit) {
211 var _error = new ChatServiceError('listLimitExceeded', listName);
212 return Promise.reject(_error);
213 } else {
214 return Promise.resolve();
215 }
216 });
217 }
218 }, {
219 key: 'addToList',
220 value: function addToList(listName, elems, limit) {
221 var _this4 = this;
222
223 var num = elems.length;
224 return this.checkList(listName, num, limit).then(function () {
225 return _this4.redis.sadd(_this4.makeKeyName(listName), elems);
226 });
227 }
228 }, {
229 key: 'removeFromList',
230 value: function removeFromList(listName, elems) {
231 var _this5 = this;
232
233 return this.checkList(listName).then(function () {
234 return _this5.redis.srem(_this5.makeKeyName(listName), elems);
235 });
236 }
237 }, {
238 key: 'getList',
239 value: function getList(listName) {
240 var _this6 = this;
241
242 return this.checkList(listName).then(function () {
243 return _this6.redis.smembers(_this6.makeKeyName(listName));
244 });
245 }
246 }, {
247 key: 'hasInList',
248 value: function hasInList(listName, elem) {
249 var _this7 = this;
250
251 return this.checkList(listName).then(function () {
252 return _this7.redis.sismember(_this7.makeKeyName(listName), elem);
253 }).then(function (data) {
254 return Promise.resolve(Boolean(data));
255 });
256 }
257 }, {
258 key: 'whitelistOnlySet',
259 value: function whitelistOnlySet(mode) {
260 var whitelistOnly = mode ? true : '';
261 return this.redis.set(this.makeKeyName('whitelistMode'), whitelistOnly);
262 }
263 }, {
264 key: 'whitelistOnlyGet',
265 value: function whitelistOnlyGet() {
266 return this.redis.get(this.makeKeyName('whitelistMode')).then(function (data) {
267 return Promise.resolve(Boolean(data));
268 });
269 }
270 }]);
271 return ListsStateRedis;
272}();
273
274// Implements room state API.
275
276
277var RoomStateRedis = function (_ListsStateRedis) {
278 (0, _inherits3.default)(RoomStateRedis, _ListsStateRedis);
279
280 function RoomStateRedis(server, roomName) {
281 (0, _classCallCheck3.default)(this, RoomStateRedis);
282
283 var _this8 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(RoomStateRedis).call(this));
284
285 _this8.server = server;
286 _this8.roomName = roomName;
287 _this8.name = _this8.roomName;
288 _this8.historyMaxGetMessages = _this8.server.historyMaxGetMessages;
289 _this8.redis = _this8.server.redis;
290 _this8.exitsErrorName = 'roomExists';
291 _this8.prefix = 'rooms';
292 mixin(_this8, StateOperations, _this8.name, _this8.exitsErrorName, _this8.redis, _this8.makeKeyName.bind(_this8), _this8.stateReset.bind(_this8));
293 return _this8;
294 }
295
296 (0, _createClass3.default)(RoomStateRedis, [{
297 key: 'stateReset',
298 value: function stateReset(state) {
299 state = state || {};
300 var _state = state;
301 var whitelist = _state.whitelist;
302 var blacklist = _state.blacklist;
303 var adminlist = _state.adminlist;
304 var whitelistOnly = _state.whitelistOnly;
305 var owner = _state.owner;
306 var historyMaxSize = _state.historyMaxSize;
307 var _state$enableAccessLi = _state.enableAccessListsUpdates;
308 var enableAccessListsUpdates = _state$enableAccessLi === undefined ? this.server.enableAccessListsUpdates : _state$enableAccessLi;
309 var _state$enableUserlist = _state.enableUserlistUpdates;
310 var enableUserlistUpdates = _state$enableUserlist === undefined ? this.server.enableUserlistUpdates : _state$enableUserlist;
311
312 if (!owner) {
313 owner = '';
314 }
315 return Promise.all([initSet(this.redis, this.makeKeyName('whitelist'), whitelist), initSet(this.redis, this.makeKeyName('blacklist'), blacklist), initSet(this.redis, this.makeKeyName('adminlist'), adminlist), initSet(this.redis, this.makeKeyName('userlist'), null), this.redis.del(this.makeKeyName('messagesHistory')), this.redis.del(this.makeKeyName('messagesTimestamps')), this.redis.del(this.makeKeyName('messagesIds')), this.redis.del(this.makeKeyName('usersseen')), this.redis.set(this.makeKeyName('lastMessageId'), 0), this.redis.set(this.makeKeyName('owner'), owner), this.whitelistOnlySet(whitelistOnly), this.accessListsUpdatesSet(enableAccessListsUpdates), this.userlistUpdatesSet(enableUserlistUpdates), this.historyMaxSizeSet(historyMaxSize)]).return();
316 }
317 }, {
318 key: 'hasList',
319 value: function hasList(listName) {
320 return listName === 'adminlist' || listName === 'whitelist' || listName === 'blacklist' || listName === 'userlist';
321 }
322 }, {
323 key: 'ownerGet',
324 value: function ownerGet() {
325 return this.redis.get(this.makeKeyName('owner'));
326 }
327 }, {
328 key: 'ownerSet',
329 value: function ownerSet(owner) {
330 return this.redis.set(this.makeKeyName('owner'), owner);
331 }
332 }, {
333 key: 'accessListsUpdatesSet',
334 value: function accessListsUpdatesSet(enableAccessListsUpdates) {
335 enableAccessListsUpdates = enableAccessListsUpdates ? true : '';
336 return this.redis.set(this.makeKeyName('enableAccessListsUpdates'), enableAccessListsUpdates);
337 }
338 }, {
339 key: 'accessListsUpdatesGet',
340 value: function accessListsUpdatesGet() {
341 return this.redis.get(this.makeKeyName('enableAccessListsUpdates')).then(function (data) {
342 return Promise.resolve(Boolean(data));
343 });
344 }
345 }, {
346 key: 'userlistUpdatesSet',
347 value: function userlistUpdatesSet(enableUserlistUpdates) {
348 enableUserlistUpdates = enableUserlistUpdates ? true : '';
349 return this.redis.set(this.makeKeyName('enableUserlistUpdates'), enableUserlistUpdates);
350 }
351 }, {
352 key: 'userlistUpdatesGet',
353 value: function userlistUpdatesGet() {
354 return this.redis.get(this.makeKeyName('enableUserlistUpdates')).then(function (data) {
355 return Promise.resolve(Boolean(data));
356 });
357 }
358 }, {
359 key: 'historyMaxSizeSet',
360 value: function historyMaxSizeSet(historyMaxSize) {
361 var limit = historyMaxSize;
362 if (!(_.isNumber(historyMaxSize) && historyMaxSize >= 0)) {
363 limit = this.server.historyMaxSize;
364 }
365 if (limit === 0) {
366 return this.redis.multi().set(this.makeKeyName('historyMaxSize'), limit).del(this.makeKeyName('messagesHistory')).del(this.makeKeyName('messagesTimestamps')).del(this.makeKeyName('messagesIds')).exec();
367 } else {
368 var last = limit - 1;
369 return this.redis.multi().set(this.makeKeyName('historyMaxSize'), limit).ltrim(this.makeKeyName('messagesHistory'), 0, last).ltrim(this.makeKeyName('messagesTimestamps'), 0, last).ltrim(this.makeKeyName('messagesIds'), 0, last).exec();
370 }
371 }
372 }, {
373 key: 'historyInfo',
374 value: function historyInfo() {
375 var _this9 = this;
376
377 return this.redis.multi().get(this.makeKeyName('historyMaxSize')).llen(this.makeKeyName('messagesHistory')).get(this.makeKeyName('lastMessageId')).exec().spread(function (_ref, _ref2, _ref3) {
378 var _ref6 = (0, _slicedToArray3.default)(_ref, 2);
379
380 var historyMaxSize = _ref6[1];
381
382 var _ref5 = (0, _slicedToArray3.default)(_ref2, 2);
383
384 var historySize = _ref5[1];
385
386 var _ref4 = (0, _slicedToArray3.default)(_ref3, 2);
387
388 var lastMessageId = _ref4[1];
389
390 historySize = parseInt(historySize);
391 historyMaxSize = parseFloat(historyMaxSize);
392 lastMessageId = parseInt(lastMessageId);
393 var info = { historySize: historySize,
394 historyMaxSize: historyMaxSize,
395 historyMaxGetMessages: _this9.historyMaxGetMessages,
396 lastMessageId: lastMessageId };
397 return Promise.resolve(info);
398 });
399 }
400 }, {
401 key: 'getCommonUsers',
402 value: function getCommonUsers() {
403 return this.redis.sdiff(this.makeKeyName('userlist'), this.makeKeyName('whitelist'), this.makeKeyName('adminlist'));
404 }
405 }, {
406 key: 'messageAdd',
407 value: function messageAdd(msg) {
408 var timestamp = _.now();
409 var smsg = (0, _stringify2.default)(msg);
410 return this.redis.messageAdd(this.makeKeyName('lastMessageId'), this.makeKeyName('historyMaxSize'), this.makeKeyName('messagesIds'), this.makeKeyName('messagesTimestamps'), this.makeKeyName('messagesHistory'), smsg, timestamp).spread(function (id) {
411 msg.id = id;
412 msg.timestamp = timestamp;
413 return Promise.resolve(msg);
414 });
415 }
416 }, {
417 key: 'convertMessages',
418 value: function convertMessages(msgs, tss, ids) {
419 var data = [];
420 if (!msgs) {
421 return Promise.resolve(data);
422 }
423 for (var idx = 0; idx < msgs.length; idx++) {
424 var msg = msgs[idx];
425 var obj = JSON.parse(msg, function (key, val) {
426 if (val && val.type === 'Buffer') {
427 return new Buffer(val.data);
428 } else {
429 return val;
430 }
431 });
432 obj.timestamp = parseInt(tss[idx]);
433 obj.id = parseInt(ids[idx]);
434 data[idx] = obj;
435 }
436 return Promise.resolve(data);
437 }
438 }, {
439 key: 'messagesGetRecent',
440 value: function messagesGetRecent() {
441 var _this10 = this;
442
443 if (this.historyMaxGetMessages <= 0) {
444 return Promise.resolve([]);
445 }
446 var limit = this.historyMaxGetMessages - 1;
447 return this.redis.multi().lrange(this.makeKeyName('messagesHistory'), 0, limit).lrange(this.makeKeyName('messagesTimestamps'), 0, limit).lrange(this.makeKeyName('messagesIds'), 0, limit).exec().spread(function (_ref7, _ref8, _ref9) {
448 var _ref12 = (0, _slicedToArray3.default)(_ref7, 2);
449
450 var msgs = _ref12[1];
451
452 var _ref11 = (0, _slicedToArray3.default)(_ref8, 2);
453
454 var tss = _ref11[1];
455
456 var _ref10 = (0, _slicedToArray3.default)(_ref9, 2);
457
458 var ids = _ref10[1];
459
460 return _this10.convertMessages(msgs, tss, ids);
461 });
462 }
463 }, {
464 key: 'messagesGet',
465 value: function messagesGet(id) {
466 var _this11 = this;
467
468 var maxMessages = arguments.length <= 1 || arguments[1] === undefined ? this.historyMaxGetMessages : arguments[1];
469
470 if (maxMessages <= 0) {
471 return Promise.resolve([]);
472 }
473 id = _.max([0, id]);
474 return this.redis.messagesGet(this.makeKeyName('lastMessageId'), this.makeKeyName('historyMaxSize'), this.makeKeyName('messagesIds'), this.makeKeyName('messagesTimestamps'), this.makeKeyName('messagesHistory'), id, maxMessages).spread(function (msgs, tss, ids) {
475 return _this11.convertMessages(msgs, tss, ids);
476 });
477 }
478 }, {
479 key: 'userSeenGet',
480 value: function userSeenGet(userName) {
481 return this.redis.multi().hget(this.makeKeyName('usersseen'), userName).sismember(this.makeKeyName('userlist'), userName).exec().spread(function (_ref13, _ref14) {
482 var _ref16 = (0, _slicedToArray3.default)(_ref13, 2);
483
484 var ts = _ref16[1];
485
486 var _ref15 = (0, _slicedToArray3.default)(_ref14, 2);
487
488 var isjoined = _ref15[1];
489
490 var joined = Boolean(isjoined);
491 var timestamp = ts ? parseInt(ts) : null;
492 return { joined: joined, timestamp: timestamp };
493 });
494 }
495 }, {
496 key: 'userSeenUpdate',
497 value: function userSeenUpdate(userName) {
498 var timestamp = _.now();
499 return this.redis.hset(this.makeKeyName('usersseen'), userName, timestamp);
500 }
501 }]);
502 return RoomStateRedis;
503}(ListsStateRedis);
504
505// Implements direct messaging state API.
506
507
508var DirectMessagingStateRedis = function (_ListsStateRedis2) {
509 (0, _inherits3.default)(DirectMessagingStateRedis, _ListsStateRedis2);
510
511 function DirectMessagingStateRedis(server, userName) {
512 (0, _classCallCheck3.default)(this, DirectMessagingStateRedis);
513
514 var _this12 = (0, _possibleConstructorReturn3.default)(this, (0, _getPrototypeOf2.default)(DirectMessagingStateRedis).call(this));
515
516 _this12.server = server;
517 _this12.userName = userName;
518 _this12.name = _this12.userName;
519 _this12.prefix = 'users';
520 _this12.exitsErrorName = 'userExists';
521 _this12.redis = _this12.server.redis;
522 mixin(_this12, StateOperations, _this12.name, _this12.exitsErrorName, _this12.redis, _this12.makeKeyName.bind(_this12), _this12.stateReset.bind(_this12));
523 return _this12;
524 }
525
526 (0, _createClass3.default)(DirectMessagingStateRedis, [{
527 key: 'hasList',
528 value: function hasList(listName) {
529 return listName === 'whitelist' || listName === 'blacklist';
530 }
531 }, {
532 key: 'stateReset',
533 value: function stateReset(state) {
534 state = state || {};
535 var _state2 = state;
536 var whitelist = _state2.whitelist;
537 var blacklist = _state2.blacklist;
538 var whitelistOnly = _state2.whitelistOnly;
539
540 whitelistOnly = whitelistOnly ? true : '';
541 return Promise.all([initSet(this.redis, this.makeKeyName('whitelist'), whitelist), initSet(this.redis, this.makeKeyName('blacklist'), blacklist), this.redis.set(this.makeKeyName('whitelistMode'), whitelistOnly)]).return();
542 }
543 }]);
544 return DirectMessagingStateRedis;
545}(ListsStateRedis);
546
547// Implements user state API.
548
549
550var UserStateRedis = function () {
551 function UserStateRedis(server, userName) {
552 (0, _classCallCheck3.default)(this, UserStateRedis);
553
554 this.server = server;
555 this.userName = userName;
556 this.name = this.userName;
557 this.prefix = 'users';
558 this.redis = this.server.redis;
559 mixin(this, LockOperations, this.redis);
560 }
561
562 (0, _createClass3.default)(UserStateRedis, [{
563 key: 'makeKeyName',
564 value: function makeKeyName(keyName) {
565 return namespace + ':' + this.prefix + ':{' + this.name + '}:' + keyName;
566 }
567 }, {
568 key: 'makeSocketToRooms',
569 value: function makeSocketToRooms() {
570 var id = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
571
572 return this.makeKeyName('socketsToRooms:' + id);
573 }
574 }, {
575 key: 'makeRoomToSockets',
576 value: function makeRoomToSockets() {
577 var room = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
578
579 return this.makeKeyName('roomsToSockets:' + room);
580 }
581 }, {
582 key: 'makeRoomLock',
583 value: function makeRoomLock(room) {
584 return this.makeKeyName('roomLock:' + room);
585 }
586 }, {
587 key: 'addSocket',
588 value: function addSocket(id, uid) {
589 return this.redis.multi().hset(this.makeKeyName('sockets'), id, uid).hlen(this.makeKeyName('sockets')).exec().spread(function (_, _ref17) {
590 var _ref18 = (0, _slicedToArray3.default)(_ref17, 2);
591
592 var nconnected = _ref18[1];
593 return Promise.resolve(nconnected);
594 });
595 }
596 }, {
597 key: 'getAllSockets',
598 value: function getAllSockets() {
599 return this.redis.hkeys(this.makeKeyName('sockets'));
600 }
601 }, {
602 key: 'getSocketsToInstance',
603 value: function getSocketsToInstance() {
604 return this.redis.hgetall(this.makeKeyName('sockets'));
605 }
606 }, {
607 key: 'getRoomToSockets',
608 value: function getRoomToSockets(roomName) {
609 return this.redis.smembers(this.makeRoomToSockets(roomName));
610 }
611 }, {
612 key: 'getSocketsToRooms',
613 value: function getSocketsToRooms() {
614 return this.redis.getSocketsToRooms(this.makeKeyName('sockets'), this.makeSocketToRooms()).spread(function (result) {
615 var data = JSON.parse(result) || {};
616 var _iteratorNormalCompletion = true;
617 var _didIteratorError = false;
618 var _iteratorError = undefined;
619
620 try {
621 for (var _iterator = (0, _getIterator3.default)(_.toPairs(data)), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
622 var _step$value = (0, _slicedToArray3.default)(_step.value, 2);
623
624 var k = _step$value[0];
625 var v = _step$value[1];
626
627 if (_.isEmpty(v)) {
628 data[k] = [];
629 }
630 }
631 } catch (err) {
632 _didIteratorError = true;
633 _iteratorError = err;
634 } finally {
635 try {
636 if (!_iteratorNormalCompletion && _iterator.return) {
637 _iterator.return();
638 }
639 } finally {
640 if (_didIteratorError) {
641 throw _iteratorError;
642 }
643 }
644 }
645
646 return Promise.resolve(data);
647 });
648 }
649 }, {
650 key: 'addSocketToRoom',
651 value: function addSocketToRoom(id, roomName) {
652 return this.redis.multi().sadd(this.makeSocketToRooms(id), roomName).sadd(this.makeRoomToSockets(roomName), id).scard(this.makeRoomToSockets(roomName)).exec().then(function (_ref19) {
653 var _ref20 = (0, _slicedToArray3.default)(_ref19, 3);
654
655 var _ref20$ = (0, _slicedToArray3.default)(_ref20[2], 2);
656
657 var njoined = _ref20$[1];
658 return Promise.resolve(njoined);
659 });
660 }
661 }, {
662 key: 'removeSocketFromRoom',
663 value: function removeSocketFromRoom(id, roomName) {
664 return this.redis.multi().scard(this.makeRoomToSockets(roomName)).srem(this.makeSocketToRooms(id), roomName).srem(this.makeRoomToSockets(roomName), id).scard(this.makeRoomToSockets(roomName)).exec().then(function (_ref21) {
665 var _ref22 = (0, _slicedToArray3.default)(_ref21, 4);
666
667 var _ref22$ = (0, _slicedToArray3.default)(_ref22[0], 2);
668
669 var wasjoined = _ref22$[1];
670
671 var _ref22$2 = (0, _slicedToArray3.default)(_ref22[3], 2);
672
673 var njoined = _ref22$2[1];
674
675 var hasChanged = njoined !== wasjoined;
676 return Promise.resolve([njoined, hasChanged]);
677 });
678 }
679 }, {
680 key: 'removeAllSocketsFromRoom',
681 value: function removeAllSocketsFromRoom(roomName) {
682 return this.redis.removeAllSocketsFromRoom(this.makeRoomToSockets(roomName), this.makeSocketToRooms(), roomName).spread(function (result) {
683 return Promise.resolve(JSON.parse(result));
684 });
685 }
686 }, {
687 key: 'removeSocket',
688 value: function removeSocket(id) {
689 return this.redis.removeSocket(this.makeSocketToRooms(id), this.makeKeyName('sockets'), this.makeRoomToSockets(), id).spread(function (result) {
690 return Promise.resolve(JSON.parse(result));
691 });
692 }
693 }, {
694 key: 'lockToRoom',
695 value: function lockToRoom(roomName, ttl) {
696 var _this13 = this;
697
698 return uid(18).then(function (val) {
699 var start = _.now();
700 return _this13.lock(_this13.makeRoomLock(roomName), val, ttl).then(function () {
701 return Promise.resolve().disposer(function () {
702 if (start + ttl < _.now()) {
703 _this13.server.emit('lockTimeExceeded', val, { userName: _this13.userName, roomName: roomName });
704 }
705 return _this13.unlock(_this13.makeRoomLock(roomName), val);
706 });
707 });
708 });
709 }
710 }]);
711 return UserStateRedis;
712}();
713
714// Implements global state API.
715
716
717var RedisState = function () {
718 function RedisState(server, options) {
719 (0, _classCallCheck3.default)(this, RedisState);
720
721 this.server = server;
722 this.options = options;
723 this.closed = false;
724 if (this.options.useCluster) {
725 this.redis = new (Function.prototype.bind.apply(Redis.Cluster, [null].concat((0, _toConsumableArray3.default)(this.options.redisOptions))))();
726 } else {
727 var redisOptions = _.castArray(this.options.redisOptions);
728 this.redis = new (Function.prototype.bind.apply(Redis, [null].concat((0, _toConsumableArray3.default)(redisOptions))))();
729 }
730 this.RoomState = RoomStateRedis;
731 this.UserState = UserStateRedis;
732 this.DirectMessagingState = DirectMessagingStateRedis;
733 this.lockTTL = this.options.lockTTL || 10000;
734 this.instanceUID = this.server.instanceUID;
735 this.server.redis = this.redis;
736 var _iteratorNormalCompletion2 = true;
737 var _didIteratorError2 = false;
738 var _iteratorError2 = undefined;
739
740 try {
741 for (var _iterator2 = (0, _getIterator3.default)(_.toPairs(luaCommands)), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
742 var _step2$value = (0, _slicedToArray3.default)(_step2.value, 2);
743
744 var cmd = _step2$value[0];
745 var def = _step2$value[1];
746
747 this.redis.defineCommand(cmd, {
748 numberOfKeys: def.numberOfKeys,
749 lua: def.lua
750 });
751 }
752 } catch (err) {
753 _didIteratorError2 = true;
754 _iteratorError2 = err;
755 } finally {
756 try {
757 if (!_iteratorNormalCompletion2 && _iterator2.return) {
758 _iterator2.return();
759 }
760 } finally {
761 if (_didIteratorError2) {
762 throw _iteratorError2;
763 }
764 }
765 }
766 }
767
768 (0, _createClass3.default)(RedisState, [{
769 key: 'makeKeyName',
770 value: function makeKeyName(prefix, name, keyName) {
771 return namespace + ':' + prefix + ':{' + name + '}:' + keyName;
772 }
773 }, {
774 key: 'hasRoom',
775 value: function hasRoom(name) {
776 return this.redis.get(this.makeKeyName('rooms', name, 'isInit'));
777 }
778 }, {
779 key: 'hasUser',
780 value: function hasUser(name) {
781 return this.redis.get(this.makeKeyName('users', name, 'isInit'));
782 }
783 }, {
784 key: 'close',
785 value: function close() {
786 this.closed = true;
787 return this.redis.quit().return();
788 }
789 }, {
790 key: 'getRoom',
791 value: function getRoom(name) {
792 var isPredicate = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
793
794 var room = new Room(this.server, name);
795 return this.hasRoom(name).then(function (exists) {
796 if (!exists) {
797 if (isPredicate) {
798 return Promise.resolve(null);
799 } else {
800 var error = new ChatServiceError('noRoom', name);
801 return Promise.reject(error);
802 }
803 }
804 return Promise.resolve(room);
805 });
806 }
807 }, {
808 key: 'addRoom',
809 value: function addRoom(name, state) {
810 var room = new Room(this.server, name);
811 return room.initState(state).return(room);
812 }
813 }, {
814 key: 'removeRoom',
815 value: function removeRoom(name) {
816 return Promise.resolve();
817 }
818 }, {
819 key: 'addSocket',
820 value: function addSocket(id, userName) {
821 return this.redis.hset(this.makeKeyName('instances', this.instanceUID, 'sockets'), id, userName);
822 }
823 }, {
824 key: 'removeSocket',
825 value: function removeSocket(id) {
826 return this.redis.hdel(this.makeKeyName('instances', this.instanceUID, 'sockets'), id);
827 }
828 }, {
829 key: 'getInstanceSockets',
830 value: function getInstanceSockets() {
831 var uid = arguments.length <= 0 || arguments[0] === undefined ? this.instanceUID : arguments[0];
832
833 return this.redis.hgetall(this.makeKeyName('instances', uid, 'sockets'));
834 }
835 }, {
836 key: 'updateHeartbeat',
837 value: function updateHeartbeat() {
838 return this.redis.set(this.makeKeyName('instances', this.instanceUID, 'heartbeat'), _.now()).catchReturn();
839 }
840 }, {
841 key: 'getInstanceHeartbeat',
842 value: function getInstanceHeartbeat() {
843 var uid = arguments.length <= 0 || arguments[0] === undefined ? this.instanceUID : arguments[0];
844
845 return this.redis.get(this.makeKeyName('instances', uid, 'heartbeat')).then(function (ts) {
846 return ts ? parseInt(ts) : null;
847 });
848 }
849 }, {
850 key: 'getOrAddUser',
851 value: function getOrAddUser(name, state) {
852 var user = new User(this.server, name);
853 return this.hasUser(name).then(function (exists) {
854 if (!exists) {
855 return user.initState(state);
856 } else {
857 return Promise.resolve();
858 }
859 }).catch(ChatServiceError, function (e) {
860 return user;
861 }).return(user);
862 }
863 }, {
864 key: 'getUser',
865 value: function getUser(name) {
866 var isPredicate = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
867
868 var user = new User(this.server, name);
869 return this.hasUser(name).then(function (exists) {
870 if (!exists) {
871 if (isPredicate) {
872 return Promise.resolve(null);
873 } else {
874 var error = new ChatServiceError('noUser', name);
875 return Promise.reject(error);
876 }
877 }
878 return Promise.resolve(user);
879 });
880 }
881 }, {
882 key: 'addUser',
883 value: function addUser(name, state) {
884 var user = new User(this.server, name);
885 return user.initState(state).return(user);
886 }
887 }, {
888 key: 'removeUser',
889 value: function removeUser(name) {
890 return Promise.resolve();
891 }
892 }]);
893 return RedisState;
894}();
895
896module.exports = RedisState;
897//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9SZWRpc1N0YXRlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsSUFBTSxtQkFBbUIsUUFBUSxvQkFBUixDQUF6QjtBQUNBLElBQU0sVUFBVSxRQUFRLFVBQVIsQ0FBaEI7QUFDQSxJQUFNLFFBQVEsUUFBUSxTQUFSLENBQWQ7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLE9BQU8sUUFBUSxRQUFSLENBQWI7QUFDQSxJQUFNLElBQUksUUFBUSxRQUFSLENBQVY7QUFDQSxJQUFNLGVBQWUsUUFBUSxlQUFSLENBQXJCO0FBQ0EsSUFBTSxNQUFNLFFBQVEsVUFBUixDQUFaOztlQUNrQixRQUFRLFdBQVIsQzs7SUFBVixLLFlBQUEsSzs7O0FBRVIsSUFBSSxZQUFZLGFBQWhCOztBQUVBLFNBQVMsT0FBVCxDQUFrQixLQUFsQixFQUF5QixHQUF6QixFQUE4QixNQUE5QixFQUFzQztBQUNwQyxTQUFPLE1BQU0sR0FBTixDQUFVLEdBQVYsRUFBZSxJQUFmLENBQW9CLFlBQU07QUFDL0IsUUFBSSxDQUFDLE1BQUwsRUFBYTtBQUNYLGFBQU8sUUFBUSxPQUFSLEVBQVA7QUFDRCxLQUZELE1BRU87QUFDTCxhQUFPLE1BQU0sSUFBTixDQUFXLEdBQVgsRUFBZ0IsTUFBaEIsQ0FBUDtBQUNEO0FBQ0YsR0FOTSxDQUFQO0FBT0Q7O0FBR0Q7O0lBQ00sZTtBQUVKLDJCQUFhLElBQWIsRUFBbUIsY0FBbkIsRUFBbUMsS0FBbkMsRUFBMEMsV0FBMUMsRUFBdUQsVUFBdkQsRUFBbUU7QUFBQTs7QUFDakUsU0FBSyxJQUFMLEdBQVksSUFBWjtBQUNBLFNBQUssY0FBTCxHQUFzQixjQUF0QjtBQUNBLFNBQUssS0FBTCxHQUFhLEtBQWI7QUFDQSxTQUFLLFdBQUwsR0FBbUIsV0FBbkI7QUFDQSxTQUFLLFVBQUwsR0FBa0IsVUFBbEI7QUFDRDs7Ozs4QkFF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O0FBQUEsVUFBckIsV0FBcUIseURBQVAsS0FBTzs7QUFDbEMsVUFBSSxPQUFPLElBQUksSUFBSixDQUFTLEtBQUssTUFBZCxFQUFzQixJQUF0QixDQUFYO0FBQ0EsYUFBTyxLQUFLLE9BQUwsQ0FBYSxJQUFiLEVBQW1CLElBQW5CLENBQXdCLGtCQUFVO0FBQ3ZDLFlBQUksQ0FBQyxNQUFMLEVBQWE7QUFDWCxjQUFJLFdBQUosRUFBaUI7QUFDZixtQkFBTyxRQUFRLE9BQVIsQ0FBZ0IsSUFBaEIsQ0FBUDtBQUNELFdBRkQsTUFFTztBQUNMLGdCQUFJLFFBQVEsSUFBSSxnQkFBSixDQUFxQixRQUFyQixFQUErQixJQUEvQixDQUFaO0FBQ0EsbUJBQU8sUUFBUSxNQUFSLENBQWUsS0FBZixDQUFQO0FBQ0Q7QUFDRjtBQUNELGVBQU8sUUFBUSxPQUFSLENBQWdCLElBQWhCLENBQVA7QUFDRCxPQVZNLENBQVA7QUFXRDs7OzRCQUVRLEksRUFBTSxLLEVBQU87QUFDcEIsVUFBSSxPQUFPLElBQUksSUFBSixDQUFTLEtBQUssTUFBZCxFQUFzQixJQUF0QixDQUFYO0FBQ0EsYUFBTyxLQUFLLFNBQUwsQ0FBZSxLQUFmLEVBQXNCLE1BQXRCLENBQTZCLElBQTdCLENBQVA7QUFDRDs7OytCQUVXLEksRUFBTTtBQUNoQixhQUFPLFFBQVEsT0FBUixFQUFQO0FBQ0Q7Ozs7O0FBSUgsT0FBTyxPQUFQLEdBQWlCLFVBQWpCIiwiZmlsZSI6IlJlZGlzU3RhdGUuanMiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCdcblxuY29uc3QgQ2hhdFNlcnZpY2VFcnJvciA9IHJlcXVpcmUoJy4vQ2hhdFNlcnZpY2VFcnJvcicpXG5jb25zdCBQcm9taXNlID0gcmVxdWlyZSgnYmx1ZWJpcmQnKVxuY29uc3QgUmVkaXMgPSByZXF1aXJlKCdpb3JlZGlzJylcbmNvbnN0IFJvb20gPSByZXF1aXJlKCcuL1Jvb20nKVxuY29uc3QgVXNlciA9IHJlcXVpcmUoJy4vVXNlcicpXG5jb25zdCBfID0gcmVxdWlyZSgnbG9kYXNoJylcbmNvbnN0IHByb21pc2VSZXRyeSA9IHJlcXVpcmUoJ3Byb21pc2UtcmV0cnknKVxuY29uc3QgdWlkID0gcmVxdWlyZSgndWlkLXNhZmUnKVxuY29uc3QgeyBtaXhpbiB9ID0gcmVxdWlyZSgnZXM2LW1peGluJylcblxubGV0IG5hbWVzcGFjZSA9ICdjaGF0c2VydmljZSdcblxuZnVuY3Rpb24gaW5pdFNldCAocmVkaXMsIHNldCwgdmFsdWVzKSB7XG4gIHJldHVybiByZWRpcy5kZWwoc2V0KS50aGVuKCgpID0+IHtcbiAgICBpZiAoIXZhbHVlcykge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiByZWRpcy5zYWRkKHNldCwgdmFsdWVzKVxuICAgIH1cbiAgfSlcbn1cblxuXG4vLyBTdGF0ZSBpbml0L3JlbW92ZSBvcGVyYXRpb25zLlxuY2xhc3MgU3RhdGVPcGVyYXRpb25zIHtcblxuICBjb25zdHJ1Y3RvciAobmFtZSwgZXhpdHNFcnJvck5hbWUsIHJlZGlzLCBtYWtlS2V5TmFtZSwgc3RhdGVSZXNldCkge1xuICAgIHRoaXMubmFtZSA9IG5hbWVcbiAgICB0aGlzLmV4aXRzRXJyb3JOYW1lID0gZXhpdHNFcnJvck5hbWVcbiAgICB0aGlzLnJlZGlzID0gcmVkaXNcbiAgICB0aGlzLm1ha2VLZXlOYW1lID0gbWFrZUtleU5hbWVcbiAgICB0aGlzLnN0YXRlUmVzZXQgPSBzdGF0ZVJlc2V0XG4gIH1cblxuICBpbml0U3RhdGUgKHN0YXRlKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0bngodGhpcy5tYWtlS2V5TmFtZSgnZXhpc3RzJyksIHRydWUpLnRoZW4oaXNuZXcgPT4ge1xuICAgICAgaWYgKCFpc25ldykge1xuICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcih0aGlzLmV4aXRzRXJyb3JOYW1lLCB0aGlzLm5hbWUpXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICAgICAgfVxuICAgIH0pLnRoZW4oKCkgPT4gdGhpcy5zdGF0ZVJlc2V0KHN0YXRlKSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc2V0bngodGhpcy5tYWtlS2V5TmFtZSgnaXNJbml0JyksIHRydWUpKVxuICB9XG5cbiAgcmVtb3ZlU3RhdGUgKCkge1xuICAgIHJldHVybiB0aGlzLnN0YXRlUmVzZXQoKS50aGVuKCgpID0+IHtcbiAgICAgIHJldHVybiB0aGlzLnJlZGlzLmRlbChcbiAgICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnZXhpc3RzJyksIHRoaXMubWFrZUtleU5hbWUoJ2lzSW5pdCcpKVxuICAgIH0pXG4gIH1cblxuICBzdGFydFJlbW92aW5nICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnaXNJbml0JykpXG4gIH1cblxufVxuXG4vLyBSZWRpcyBsb2NrIG9wZXJhdGlvbnMuXG5jbGFzcyBMb2NrT3BlcmF0aW9ucyB7XG5cbiAgY29uc3RydWN0b3IgKHJlZGlzKSB7XG4gICAgdGhpcy5yZWRpcyA9IHJlZGlzXG4gIH1cblxuICBsb2NrIChrZXksIHZhbCwgdHRsKSB7XG4gICAgcmV0dXJuIHByb21pc2VSZXRyeShcbiAgICAgIHttaW5UaW1lb3V0OiAxMDAsIHJldHJpZXM6IDEwLCBmYWN0b3I6IDEuNSwgcmFuZG9taXplOiB0cnVlfSxcbiAgICAgIChyZXRyeSwgbikgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQoa2V5LCB2YWwsICdOWCcsICdQWCcsIHR0bCkudGhlbihyZXMgPT4ge1xuICAgICAgICAgIGlmICghcmVzKSB7XG4gICAgICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcigndGltZW91dCcpXG4gICAgICAgICAgICByZXR1cm4gcmV0cnkoZXJyb3IpXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBudWxsXG4gICAgICAgICAgfVxuICAgICAgICB9KS5jYXRjaChyZXRyeSlcbiAgICAgIH0pXG4gIH1cblxuICB1bmxvY2sgKGtleSwgdmFsKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMudW5sb2NrKGtleSwgdmFsKVxuICB9XG5cbn1cblxuLy8gUmVkaXMgc2NyaXB0cy5cbmxldCBsdWFDb21tYW5kcyA9IHtcbiAgdW5sb2NrOiB7XG4gICAgbnVtYmVyT2ZLZXlzOiAxLFxuICAgIGx1YTogYFxuaWYgcmVkaXMuY2FsbChcImdldFwiLEtFWVNbMV0pID09IEFSR1ZbMV0gdGhlblxuICByZXR1cm4gcmVkaXMuY2FsbChcImRlbFwiLEtFWVNbMV0pXG5lbHNlXG4gIHJldHVybiAwXG5lbmRgXG4gIH0sXG5cbiAgbWVzc2FnZUFkZDoge1xuICAgIG51bWJlck9mS2V5czogNSxcbiAgICBsdWE6IGBcbmxvY2FsIG1zZyA9IEFSR1ZbMV1cbmxvY2FsIHRzID0gQVJHVlsyXVxuXG5sb2NhbCBsYXN0TWVzc2FnZUlkID0gS0VZU1sxXVxubG9jYWwgaGlzdG9yeU1heFNpemUgPSBLRVlTWzJdXG5sb2NhbCBtZXNzYWdlc0lkcyA9IEtFWVNbM11cbmxvY2FsIG1lc3NhZ2VzVGltZXN0YW1wcyA9IEtFWVNbNF1cbmxvY2FsIG1lc3NhZ2VzSGlzdG9yeSA9IEtFWVNbNV1cblxubG9jYWwgaWQgPSB0b251bWJlcihyZWRpcy5jYWxsKCdJTkNSJywgbGFzdE1lc3NhZ2VJZCkpXG5sb2NhbCBtYXhzeiA9IHRvbnVtYmVyKHJlZGlzLmNhbGwoJ0dFVCcsIGhpc3RvcnlNYXhTaXplKSlcblxucmVkaXMuY2FsbCgnTFBVU0gnLCBtZXNzYWdlc0lkcywgaWQpXG5yZWRpcy5jYWxsKCdMUFVTSCcsIG1lc3NhZ2VzVGltZXN0YW1wcywgdHMpXG5yZWRpcy5jYWxsKCdMUFVTSCcsIG1lc3NhZ2VzSGlzdG9yeSwgbXNnKVxuXG5sb2NhbCBzeiA9IHRvbnVtYmVyKHJlZGlzLmNhbGwoJ0xMRU4nLCBtZXNzYWdlc0hpc3RvcnkpKVxuXG5pZiBzeiA+IG1heHN6IHRoZW5cbiAgcmVkaXMuY2FsbCgnUlBPUCcsIG1lc3NhZ2VzSWRzKVxuICByZWRpcy5jYWxsKCdSUE9QJywgbWVzc2FnZXNUaW1lc3RhbXBzKVxuICByZWRpcy5jYWxsKCdSUE9QJywgbWVzc2FnZXNIaXN0b3J5KVxuZW5kXG5cbnJldHVybiB7aWR9YFxuICB9LFxuXG4gIG1lc3NhZ2VzR2V0OiB7XG4gICAgbnVtYmVyT2ZLZXlzOiA1LFxuICAgIGx1YTogYFxubG9jYWwgaWQgPSBBUkdWWzFdXG5sb2NhbCBtYXhsZW4gPSBBUkdWWzJdXG5cbmxvY2FsIGxhc3RNZXNzYWdlSWQgPSBLRVlTWzFdXG5sb2NhbCBoaXN0b3J5TWF4U2l6ZSA9IEtFWVNbMl1cbmxvY2FsIG1lc3NhZ2VzSWRzID0gS0VZU1szXVxubG9jYWwgbWVzc2FnZXNUaW1lc3RhbXBzID0gS0VZU1s0XVxubG9jYWwgbWVzc2FnZXNIaXN0b3J5ID0gS0VZU1s1XVxuXG5sb2NhbCBsYXN0aWQgPSB0b251bWJlcihyZWRpcy5jYWxsKCdHRVQnLCBsYXN0TWVzc2FnZUlkKSlcbmxvY2FsIG1heHN6ID0gdG9udW1iZXIocmVkaXMuY2FsbCgnR0VUJywgaGlzdG9yeU1heFNpemUpKVxubG9jYWwgaWQgPSBtYXRoLm1pbihpZCwgbGFzdGlkKVxubG9jYWwgZW5kcCA9IGxhc3RpZCAtIGlkXG5sb2NhbCBsZW4gPSBtYXRoLm1pbihtYXhsZW4sIGVuZHApXG5sb2NhbCBzdGFydCA9IG1hdGgubWF4KDAsIGVuZHAgLSBsZW4pXG5cbmlmIHN0YXJ0ID49IGVuZHAgdGhlblxuICByZXR1cm4ge31cbmVuZFxuXG5lbmRwID0gZW5kcCAtIDFcbmxvY2FsIG1zZ3MgPSByZWRpcy5jYWxsKCdMUkFOR0UnLCBtZXNzYWdlc0hpc3RvcnksIHN0YXJ0LCBlbmRwKVxubG9jYWwgdHNzID0gcmVkaXMuY2FsbCgnTFJBTkdFJywgbWVzc2FnZXNUaW1lc3RhbXBzLCBzdGFydCwgZW5kcClcbmxvY2FsIGlkcyA9IHJlZGlzLmNhbGwoJ0xSQU5HRScsIG1lc3NhZ2VzSWRzLCBzdGFydCwgZW5kcClcblxucmV0dXJuIHttc2dzLCB0c3MsIGlkc31gXG4gIH0sXG5cbiAgZ2V0U29ja2V0c1RvUm9vbXM6IHtcbiAgICBudW1iZXJPZktleXM6IDEsXG4gICAgbHVhOiBgXG5sb2NhbCByZXN1bHQgPSB7fVxubG9jYWwgc29ja2V0cyA9IEtFWVNbMV1cbmxvY2FsIHByZWZpeCA9IEFSR1ZbMV1cbmxvY2FsIGlkcyA9IHJlZGlzLmNhbGwoJ0hLRVlTJywgc29ja2V0cylcblxuaWYgdGFibGUuZ2V0bihpZHMpID09IDAgdGhlblxuICBsb2NhbCBqc29uUmVzdWx0ID0gY2pzb24uZW5jb2RlKGNqc29uLm51bGwpXG4gIHJldHVybiB7anNvblJlc3VsdH1cbmVuZFxuXG5mb3IgaSwgaWQgaW4gcGFpcnMoaWRzKSBkb1xuICBsb2NhbCBqb2luZWQgPSByZWRpcy5jYWxsKCdTTUVNQkVSUycsIHByZWZpeCAuLiBpZClcbiAgcmVzdWx0W2lkXSA9IGpvaW5lZFxuZW5kXG5cbmxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUocmVzdWx0KVxucmV0dXJuIHtqc29uUmVzdWx0fWBcbiAgfSxcblxuICByZW1vdmVBbGxTb2NrZXRzRnJvbVJvb206IHtcbiAgICBudW1iZXJPZktleXM6IDEsXG4gICAgbHVhOiBgXG5sb2NhbCByb29tID0gS0VZU1sxXVxubG9jYWwgcHJlZml4ID0gQVJHVlsxXVxubG9jYWwgcm9vbU5hbWUgPSBBUkdWWzJdXG5sb2NhbCBpZHMgPSByZWRpcy5jYWxsKCdTTUVNQkVSUycsIHJvb20pXG5cbmlmIHRhYmxlLmdldG4oaWRzKSA9PSAwIHRoZW5cbiAgbG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZShjanNvbi5udWxsKVxuICByZXR1cm4ge2pzb25SZXN1bHR9XG5lbmRcblxucmVkaXMuY2FsbCgnREVMJywgcm9vbSlcblxuZm9yIGksIGlkIGluIHBhaXJzKGlkcykgZG9cbiAgcmVkaXMuY2FsbCgnU1JFTScsIHByZWZpeCAuLiBpZCwgcm9vbU5hbWUpXG5lbmRcblxubG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZShpZHMpXG5yZXR1cm4ge2pzb25SZXN1bHR9YFxuICB9LFxuXG4gIHJlbW92ZVNvY2tldDoge1xuICAgIG51bWJlck9mS2V5czogMixcbiAgICBsdWE6IGBcbmxvY2FsIGlkID0gS0VZU1sxXVxubG9jYWwgc29ja2V0cyA9IEtFWVNbMl1cbmxvY2FsIHByZWZpeCA9IEFSR1ZbMV1cbmxvY2FsIHNvY2tldGlkID0gQVJHVlsyXVxuXG5sb2NhbCByb29tcyA9IHJlZGlzLmNhbGwoJ1NNRU1CRVJTJywgaWQpXG5yZWRpcy5jYWxsKCdERUwnLCBpZClcblxucmVkaXMuY2FsbCgnSERFTCcsIHNvY2tldHMsIHNvY2tldGlkKVxubG9jYWwgbmNvbm5lY3RlZCA9IHJlZGlzLmNhbGwoJ0hMRU4nLCBzb2NrZXRzKVxuXG5sb2NhbCByZW1vdmVkUm9vbXMgPSB7fVxubG9jYWwgam9pbmVkU29ja2V0cyA9IHt9XG5cbmZvciBpLCByb29tIGluIHBhaXJzKHJvb21zKSBkb1xuICBsb2NhbCBpc21lbWJlciA9IHJlZGlzLmNhbGwoJ1NJU01FTUJFUicsIHByZWZpeCAuLiByb29tLCBzb2NrZXRpZClcbiAgaWYgaXNtZW1iZXIgPT0gMSB0aGVuXG4gICAgcmVkaXMuY2FsbCgnU1JFTScsIHByZWZpeCAuLiByb29tLCBzb2NrZXRpZClcbiAgICBsb2NhbCBuam9pbmVkID0gcmVkaXMuY2FsbCgnU0NBUkQnLCBwcmVmaXggLi4gcm9vbSlcbiAgICB0YWJsZS5pbnNlcnQocmVtb3ZlZFJvb21zLCByb29tKVxuICAgIHRhYmxlLmluc2VydChqb2luZWRTb2NrZXRzLCBuam9pbmVkKVxuICBlbmRcbmVuZFxuXG5pZiB0YWJsZS5nZXRuKHJlbW92ZWRSb29tcykgPT0gMCBvciB0YWJsZS5nZXRuKHJvb21zKSA9PSAwIHRoZW5cbiAgbG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZSh7Y2pzb24ubnVsbCwgY2pzb24ubnVsbCwgbmNvbm5lY3RlZH0pXG4gIHJldHVybiB7anNvblJlc3VsdH1cbmVuZFxuXG5sb2NhbCBqc29uUmVzdWx0ID0gY2pzb24uZW5jb2RlKHtyZW1vdmVkUm9vbXMsIGpvaW5lZFNvY2tldHMsIG5jb25uZWN0ZWR9KVxucmV0dXJuIHtqc29uUmVzdWx0fWBcbiAgfVxuXG59XG5cbi8vIEltcGxlbWVudHMgc3RhdGUgQVBJIGxpc3RzIG1hbmFnZW1lbnQuXG5jbGFzcyBMaXN0c1N0YXRlUmVkaXMge1xuXG4gIG1ha2VLZXlOYW1lIChrZXlOYW1lKSB7XG4gICAgcmV0dXJuIGAke25hbWVzcGFjZX06JHt0aGlzLnByZWZpeH06eyR7dGhpcy5uYW1lfX06JHtrZXlOYW1lfWBcbiAgfVxuXG4gIGNoZWNrTGlzdCAobGlzdE5hbWUsIG51bSwgbGltaXQpIHtcbiAgICBpZiAoIXRoaXMuaGFzTGlzdChsaXN0TmFtZSkpIHtcbiAgICAgIGxldCBlcnJvciA9IG5ldyBDaGF0U2VydmljZUVycm9yKCdub0xpc3QnLCBsaXN0TmFtZSlcbiAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgICB9XG4gICAgaWYgKGxpc3ROYW1lID09PSAndXNlcmxpc3QnKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2NhcmQobGlzdE5hbWUpLnRoZW4oc3ogPT4ge1xuICAgICAgaWYgKHN6ICsgbnVtID4gbGltaXQpIHtcbiAgICAgICAgbGV0IGVycm9yID0gbmV3IENoYXRTZXJ2aWNlRXJyb3IoJ2xpc3RMaW1pdEV4Y2VlZGVkJywgbGlzdE5hbWUpXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBhZGRUb0xpc3QgKGxpc3ROYW1lLCBlbGVtcywgbGltaXQpIHtcbiAgICBsZXQgbnVtID0gZWxlbXMubGVuZ3RoXG4gICAgcmV0dXJuIHRoaXMuY2hlY2tMaXN0KGxpc3ROYW1lLCBudW0sIGxpbWl0KVxuICAgICAgLnRoZW4oKCkgPT4gdGhpcy5yZWRpcy5zYWRkKHRoaXMubWFrZUtleU5hbWUobGlzdE5hbWUpLCBlbGVtcykpXG4gIH1cblxuICByZW1vdmVGcm9tTGlzdCAobGlzdE5hbWUsIGVsZW1zKSB7XG4gICAgcmV0dXJuIHRoaXMuY2hlY2tMaXN0KGxpc3ROYW1lKVxuICAgICAgLnRoZW4oKCkgPT4gdGhpcy5yZWRpcy5zcmVtKHRoaXMubWFrZUtleU5hbWUobGlzdE5hbWUpLCBlbGVtcykpXG4gIH1cblxuICBnZXRMaXN0IChsaXN0TmFtZSkge1xuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc21lbWJlcnModGhpcy5tYWtlS2V5TmFtZShsaXN0TmFtZSkpKVxuICB9XG5cbiAgaGFzSW5MaXN0IChsaXN0TmFtZSwgZWxlbSkge1xuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc2lzbWVtYmVyKHRoaXMubWFrZUtleU5hbWUobGlzdE5hbWUpLCBlbGVtKSlcbiAgICAgIC50aGVuKGRhdGEgPT4gUHJvbWlzZS5yZXNvbHZlKEJvb2xlYW4oZGF0YSkpKVxuICB9XG5cbiAgd2hpdGVsaXN0T25seVNldCAobW9kZSkge1xuICAgIGxldCB3aGl0ZWxpc3RPbmx5ID0gbW9kZSA/IHRydWUgOiAnJ1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3RNb2RlJyksIHdoaXRlbGlzdE9ubHkpXG4gIH1cblxuICB3aGl0ZWxpc3RPbmx5R2V0ICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgnd2hpdGVsaXN0TW9kZScpKVxuICAgICAgLnRoZW4oZGF0YSA9PiBQcm9taXNlLnJlc29sdmUoQm9vbGVhbihkYXRhKSkpXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIHJvb20gc3RhdGUgQVBJLlxuY2xhc3MgUm9vbVN0YXRlUmVkaXMgZXh0ZW5kcyBMaXN0c1N0YXRlUmVkaXMge1xuXG4gIGNvbnN0cnVjdG9yIChzZXJ2ZXIsIHJvb21OYW1lKSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuc2VydmVyID0gc2VydmVyXG4gICAgdGhpcy5yb29tTmFtZSA9IHJvb21OYW1lXG4gICAgdGhpcy5uYW1lID0gdGhpcy5yb29tTmFtZVxuICAgIHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzID0gdGhpcy5zZXJ2ZXIuaGlzdG9yeU1heEdldE1lc3NhZ2VzXG4gICAgdGhpcy5yZWRpcyA9IHRoaXMuc2VydmVyLnJlZGlzXG4gICAgdGhpcy5leGl0c0Vycm9yTmFtZSA9ICdyb29tRXhpc3RzJ1xuICAgIHRoaXMucHJlZml4ID0gJ3Jvb21zJ1xuICAgIG1peGluKHRoaXMsIFN0YXRlT3BlcmF0aW9ucywgdGhpcy5uYW1lLCB0aGlzLmV4aXRzRXJyb3JOYW1lLCB0aGlzLnJlZGlzLFxuICAgICAgICAgIHRoaXMubWFrZUtleU5hbWUuYmluZCh0aGlzKSwgdGhpcy5zdGF0ZVJlc2V0LmJpbmQodGhpcykpXG4gIH1cblxuICBzdGF0ZVJlc2V0IChzdGF0ZSkge1xuICAgIHN0YXRlID0gc3RhdGUgfHwge31cbiAgICBsZXQgeyB3aGl0ZWxpc3QsIGJsYWNrbGlzdCwgYWRtaW5saXN0LFxuICAgICAgICAgIHdoaXRlbGlzdE9ubHksIG93bmVyLCBoaXN0b3J5TWF4U2l6ZSxcbiAgICAgICAgICBlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMgPSB0aGlzLnNlcnZlci5lbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMsXG4gICAgICAgICAgZW5hYmxlVXNlcmxpc3RVcGRhdGVzID0gdGhpcy5zZXJ2ZXIuZW5hYmxlVXNlcmxpc3RVcGRhdGVzXG4gICAgICAgIH0gPSBzdGF0ZVxuICAgIGlmICghb3duZXIpIHsgb3duZXIgPSAnJyB9XG4gICAgcmV0dXJuIFByb21pc2UuYWxsKFtcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgnd2hpdGVsaXN0JyksIHdoaXRlbGlzdCksXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ2JsYWNrbGlzdCcpLCBibGFja2xpc3QpLFxuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCdhZG1pbmxpc3QnKSwgYWRtaW5saXN0KSxcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgndXNlcmxpc3QnKSwgbnVsbCksXG4gICAgICB0aGlzLnJlZGlzLmRlbCh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0hpc3RvcnknKSksXG4gICAgICB0aGlzLnJlZGlzLmRlbCh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSksXG4gICAgICB0aGlzLnJlZGlzLmRlbCh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0lkcycpKSxcbiAgICAgIHRoaXMucmVkaXMuZGVsKHRoaXMubWFrZUtleU5hbWUoJ3VzZXJzc2VlbicpKSxcbiAgICAgIHRoaXMucmVkaXMuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2xhc3RNZXNzYWdlSWQnKSwgMCksXG4gICAgICB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdvd25lcicpLCBvd25lciksXG4gICAgICB0aGlzLndoaXRlbGlzdE9ubHlTZXQod2hpdGVsaXN0T25seSksXG4gICAgICB0aGlzLmFjY2Vzc0xpc3RzVXBkYXRlc1NldChlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMpLFxuICAgICAgdGhpcy51c2VybGlzdFVwZGF0ZXNTZXQoZW5hYmxlVXNlcmxpc3RVcGRhdGVzKSxcbiAgICAgIHRoaXMuaGlzdG9yeU1heFNpemVTZXQoaGlzdG9yeU1heFNpemUpXG4gICAgXSkucmV0dXJuKClcbiAgfVxuXG4gIGhhc0xpc3QgKGxpc3ROYW1lKSB7XG4gICAgcmV0dXJuIGxpc3ROYW1lID09PSAnYWRtaW5saXN0JyB8fCBsaXN0TmFtZSA9PT0gJ3doaXRlbGlzdCcgfHxcbiAgICAgIGxpc3ROYW1lID09PSAnYmxhY2tsaXN0JyB8fCBsaXN0TmFtZSA9PT0gJ3VzZXJsaXN0J1xuICB9XG5cbiAgb3duZXJHZXQgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdvd25lcicpKVxuICB9XG5cbiAgb3duZXJTZXQgKG93bmVyKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KHRoaXMubWFrZUtleU5hbWUoJ293bmVyJyksIG93bmVyKVxuICB9XG5cbiAgYWNjZXNzTGlzdHNVcGRhdGVzU2V0IChlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMpIHtcbiAgICBlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMgPSBlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMgPyB0cnVlIDogJydcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZUFjY2Vzc0xpc3RzVXBkYXRlcylcbiAgfVxuXG4gIGFjY2Vzc0xpc3RzVXBkYXRlc0dldCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ2VuYWJsZUFjY2Vzc0xpc3RzVXBkYXRlcycpKVxuICAgICAgLnRoZW4oZGF0YSA9PiBQcm9taXNlLnJlc29sdmUoQm9vbGVhbihkYXRhKSkpXG4gIH1cblxuICB1c2VybGlzdFVwZGF0ZXNTZXQgKGVuYWJsZVVzZXJsaXN0VXBkYXRlcykge1xuICAgIGVuYWJsZVVzZXJsaXN0VXBkYXRlcyA9IGVuYWJsZVVzZXJsaXN0VXBkYXRlcyA/IHRydWUgOiAnJ1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdlbmFibGVVc2VybGlzdFVwZGF0ZXMnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgZW5hYmxlVXNlcmxpc3RVcGRhdGVzKVxuICB9XG5cbiAgdXNlcmxpc3RVcGRhdGVzR2V0ICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgnZW5hYmxlVXNlcmxpc3RVcGRhdGVzJykpXG4gICAgICAudGhlbihkYXRhID0+IFByb21pc2UucmVzb2x2ZShCb29sZWFuKGRhdGEpKSlcbiAgfVxuXG4gIGhpc3RvcnlNYXhTaXplU2V0IChoaXN0b3J5TWF4U2l6ZSkge1xuICAgIGxldCBsaW1pdCA9IGhpc3RvcnlNYXhTaXplXG4gICAgaWYgKCEoXy5pc051bWJlcihoaXN0b3J5TWF4U2l6ZSkgJiYgaGlzdG9yeU1heFNpemUgPj0gMCkpIHtcbiAgICAgIGxpbWl0ID0gdGhpcy5zZXJ2ZXIuaGlzdG9yeU1heFNpemVcbiAgICB9XG4gICAgaWYgKGxpbWl0ID09PSAwKSB7XG4gICAgICByZXR1cm4gdGhpcy5yZWRpcy5tdWx0aSgpXG4gICAgICAgIC5zZXQodGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSwgbGltaXQpXG4gICAgICAgIC5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JykpXG4gICAgICAgIC5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJykpXG4gICAgICAgIC5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNJZHMnKSlcbiAgICAgICAgLmV4ZWMoKVxuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgbGFzdCA9IGxpbWl0IC0gMVxuICAgICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgICAuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2hpc3RvcnlNYXhTaXplJyksIGxpbWl0KVxuICAgICAgICAubHRyaW0odGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIDAsIGxhc3QpXG4gICAgICAgIC5sdHJpbSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSwgMCwgbGFzdClcbiAgICAgICAgLmx0cmltKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIDAsIGxhc3QpXG4gICAgICAgIC5leGVjKClcbiAgICB9XG4gIH1cblxuICBoaXN0b3J5SW5mbyAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdoaXN0b3J5TWF4U2l6ZScpKVxuICAgICAgLmxsZW4odGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JykpXG4gICAgICAuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ2xhc3RNZXNzYWdlSWQnKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC5zcHJlYWQoKFssIGhpc3RvcnlNYXhTaXplXSwgWywgaGlzdG9yeVNpemVdLCBbLCBsYXN0TWVzc2FnZUlkXSkgPT4ge1xuICAgICAgICBoaXN0b3J5U2l6ZSA9IHBhcnNlSW50KGhpc3RvcnlTaXplKVxuICAgICAgICBoaXN0b3J5TWF4U2l6ZSA9IHBhcnNlRmxvYXQoaGlzdG9yeU1heFNpemUpXG4gICAgICAgIGxhc3RNZXNzYWdlSWQgPSBwYXJzZUludChsYXN0TWVzc2FnZUlkKVxuICAgICAgICBsZXQgaW5mbyA9IHsgaGlzdG9yeVNpemUsXG4gICAgICAgICAgICAgICAgICAgICBoaXN0b3J5TWF4U2l6ZSxcbiAgICAgICAgICAgICAgICAgICAgIGhpc3RvcnlNYXhHZXRNZXNzYWdlczogdGhpcy5oaXN0b3J5TWF4R2V0TWVzc2FnZXMsXG4gICAgICAgICAgICAgICAgICAgICBsYXN0TWVzc2FnZUlkIH1cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShpbmZvKVxuICAgICAgfSlcbiAgfVxuXG4gIGdldENvbW1vblVzZXJzICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZGlmZih0aGlzLm1ha2VLZXlOYW1lKCd1c2VybGlzdCcpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdCcpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2FkbWlubGlzdCcpKVxuICB9XG5cbiAgbWVzc2FnZUFkZCAobXNnKSB7XG4gICAgbGV0IHRpbWVzdGFtcCA9IF8ubm93KClcbiAgICBsZXQgc21zZyA9IEpTT04uc3RyaW5naWZ5KG1zZylcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5tZXNzYWdlQWRkKFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnbGFzdE1lc3NhZ2VJZCcpLCB0aGlzLm1ha2VLZXlOYW1lKCdoaXN0b3J5TWF4U2l6ZScpLFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNJZHMnKSwgdGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJyksXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0hpc3RvcnknKSwgc21zZywgdGltZXN0YW1wKVxuICAgICAgLnNwcmVhZChpZCA9PiB7XG4gICAgICAgIG1zZy5pZCA9IGlkXG4gICAgICAgIG1zZy50aW1lc3RhbXAgPSB0aW1lc3RhbXBcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShtc2cpXG4gICAgICB9KVxuICB9XG5cbiAgY29udmVydE1lc3NhZ2VzIChtc2dzLCB0c3MsIGlkcykge1xuICAgIGxldCBkYXRhID0gW11cbiAgICBpZiAoIW1zZ3MpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGF0YSlcbiAgICB9XG4gICAgZm9yIChsZXQgaWR4ID0gMDsgaWR4IDwgbXNncy5sZW5ndGg7IGlkeCsrKSB7XG4gICAgICBsZXQgbXNnID0gbXNnc1tpZHhdXG4gICAgICBsZXQgb2JqID0gSlNPTi5wYXJzZShtc2csIChrZXksIHZhbCkgPT4ge1xuICAgICAgICBpZiAodmFsICYmIHZhbC50eXBlID09PSAnQnVmZmVyJykge1xuICAgICAgICAgIHJldHVybiBuZXcgQnVmZmVyKHZhbC5kYXRhKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiB2YWxcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICAgIG9iai50aW1lc3RhbXAgPSBwYXJzZUludCh0c3NbaWR4XSlcbiAgICAgIG9iai5pZCA9IHBhcnNlSW50KGlkc1tpZHhdKVxuICAgICAgZGF0YVtpZHhdID0gb2JqXG4gICAgfVxuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGF0YSlcbiAgfVxuXG4gIG1lc3NhZ2VzR2V0UmVjZW50ICgpIHtcbiAgICBpZiAodGhpcy5oaXN0b3J5TWF4R2V0TWVzc2FnZXMgPD0gMCkgeyByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKFtdKSB9XG4gICAgbGV0IGxpbWl0ID0gdGhpcy5oaXN0b3J5TWF4R2V0TWVzc2FnZXMgLSAxXG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLmxyYW5nZSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0hpc3RvcnknKSwgMCwgbGltaXQpXG4gICAgICAubHJhbmdlKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpLCAwLCBsaW1pdClcbiAgICAgIC5scmFuZ2UodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNJZHMnKSwgMCwgbGltaXQpXG4gICAgICAuZXhlYygpXG4gICAgICAuc3ByZWFkKChbLCBtc2dzXSwgWywgdHNzXSwgWywgaWRzXSkgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jb252ZXJ0TWVzc2FnZXMobXNncywgdHNzLCBpZHMpXG4gICAgICB9KVxuICB9XG5cbiAgbWVzc2FnZXNHZXQgKGlkLCBtYXhNZXNzYWdlcyA9IHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzKSB7XG4gICAgaWYgKG1heE1lc3NhZ2VzIDw9IDApIHsgcmV0dXJuIFByb21pc2UucmVzb2x2ZShbXSkgfVxuICAgIGlkID0gXy5tYXgoWzAsIGlkXSlcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5tZXNzYWdlc0dldChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2xhc3RNZXNzYWdlSWQnKSwgdGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSxcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpLFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIGlkLCBtYXhNZXNzYWdlcylcbiAgICAgIC5zcHJlYWQoKG1zZ3MsIHRzcywgaWRzKSA9PiB7XG4gICAgICAgIHJldHVybiB0aGlzLmNvbnZlcnRNZXNzYWdlcyhtc2dzLCB0c3MsIGlkcylcbiAgICAgIH0pXG4gIH1cblxuICB1c2VyU2VlbkdldCAodXNlck5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5tdWx0aSgpXG4gICAgICAuaGdldCh0aGlzLm1ha2VLZXlOYW1lKCd1c2Vyc3NlZW4nKSwgdXNlck5hbWUpXG4gICAgICAuc2lzbWVtYmVyKHRoaXMubWFrZUtleU5hbWUoJ3VzZXJsaXN0JyksIHVzZXJOYW1lKVxuICAgICAgLmV4ZWMoKVxuICAgICAgLnNwcmVhZCgoWywgdHNdLCBbLCBpc2pvaW5lZF0pID0+IHtcbiAgICAgICAgbGV0IGpvaW5lZCA9IEJvb2xlYW4oaXNqb2luZWQpXG4gICAgICAgIGxldCB0aW1lc3RhbXAgPSB0cyA/IHBhcnNlSW50KHRzKSA6IG51bGxcbiAgICAgICAgcmV0dXJuIHtqb2luZWQsIHRpbWVzdGFtcH1cbiAgICAgIH0pXG4gIH1cblxuICB1c2VyU2VlblVwZGF0ZSAodXNlck5hbWUpIHtcbiAgICBsZXQgdGltZXN0YW1wID0gXy5ub3coKVxuICAgIHJldHVybiB0aGlzLnJlZGlzLmhzZXQodGhpcy5tYWtlS2V5TmFtZSgndXNlcnNzZWVuJyksIHVzZXJOYW1lLCB0aW1lc3RhbXApXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIGRpcmVjdCBtZXNzYWdpbmcgc3RhdGUgQVBJLlxuY2xhc3MgRGlyZWN0TWVzc2FnaW5nU3RhdGVSZWRpcyBleHRlbmRzIExpc3RzU3RhdGVSZWRpcyB7XG5cbiAgY29uc3RydWN0b3IgKHNlcnZlciwgdXNlck5hbWUpIHtcbiAgICBzdXBlcigpXG4gICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXJcbiAgICB0aGlzLnVzZXJOYW1lID0gdXNlck5hbWVcbiAgICB0aGlzLm5hbWUgPSB0aGlzLnVzZXJOYW1lXG4gICAgdGhpcy5wcmVmaXggPSAndXNlcnMnXG4gICAgdGhpcy5leGl0c0Vycm9yTmFtZSA9ICd1c2VyRXhpc3RzJ1xuICAgIHRoaXMucmVkaXMgPSB0aGlzLnNlcnZlci5yZWRpc1xuICAgIG1peGluKHRoaXMsIFN0YXRlT3BlcmF0aW9ucywgdGhpcy5uYW1lLCB0aGlzLmV4aXRzRXJyb3JOYW1lLCB0aGlzLnJlZGlzLFxuICAgICAgICAgIHRoaXMubWFrZUtleU5hbWUuYmluZCh0aGlzKSwgdGhpcy5zdGF0ZVJlc2V0LmJpbmQodGhpcykpXG4gIH1cblxuICBoYXNMaXN0IChsaXN0TmFtZSkge1xuICAgIHJldHVybiBsaXN0TmFtZSA9PT0gJ3doaXRlbGlzdCcgfHwgbGlzdE5hbWUgPT09ICdibGFja2xpc3QnXG4gIH1cblxuICBzdGF0ZVJlc2V0IChzdGF0ZSkge1xuICAgIHN0YXRlID0gc3RhdGUgfHwge31cbiAgICBsZXQgeyB3aGl0ZWxpc3QsIGJsYWNrbGlzdCwgd2hpdGVsaXN0T25seSB9ID0gc3RhdGVcbiAgICB3aGl0ZWxpc3RPbmx5ID0gd2hpdGVsaXN0T25seSA/IHRydWUgOiAnJ1xuICAgIHJldHVybiBQcm9taXNlLmFsbChbXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdCcpLCB3aGl0ZWxpc3QpLFxuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCdibGFja2xpc3QnKSwgYmxhY2tsaXN0KSxcbiAgICAgIHRoaXMucmVkaXMuc2V0KHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdE1vZGUnKSwgd2hpdGVsaXN0T25seSlcbiAgICBdKS5yZXR1cm4oKVxuICB9XG5cbn1cblxuLy8gSW1wbGVtZW50cyB1c2VyIHN0YXRlIEFQSS5cbmNsYXNzIFVzZXJTdGF0ZVJlZGlzIHtcblxuICBjb25zdHJ1Y3RvciAoc2VydmVyLCB1c2VyTmFtZSkge1xuICAgIHRoaXMuc2VydmVyID0gc2VydmVyXG4gICAgdGhpcy51c2VyTmFtZSA9IHVzZXJOYW1lXG4gICAgdGhpcy5uYW1lID0gdGhpcy51c2VyTmFtZVxuICAgIHRoaXMucHJlZml4ID0gJ3VzZXJzJ1xuICAgIHRoaXMucmVkaXMgPSB0aGlzLnNlcnZlci5yZWRpc1xuICAgIG1peGluKHRoaXMsIExvY2tPcGVyYXRpb25zLCB0aGlzLnJlZGlzKVxuICB9XG5cbiAgbWFrZUtleU5hbWUgKGtleU5hbWUpIHtcbiAgICByZXR1cm4gYCR7bmFtZXNwYWNlfToke3RoaXMucHJlZml4fTp7JHt0aGlzLm5hbWV9fToke2tleU5hbWV9YFxuICB9XG5cbiAgbWFrZVNvY2tldFRvUm9vbXMgKGlkID0gJycpIHtcbiAgICByZXR1cm4gdGhpcy5tYWtlS2V5TmFtZShgc29ja2V0c1RvUm9vbXM6JHtpZH1gKVxuICB9XG5cbiAgbWFrZVJvb21Ub1NvY2tldHMgKHJvb20gPSAnJykge1xuICAgIHJldHVybiB0aGlzLm1ha2VLZXlOYW1lKGByb29tc1RvU29ja2V0czoke3Jvb219YClcbiAgfVxuXG4gIG1ha2VSb29tTG9jayAocm9vbSkge1xuICAgIHJldHVybiB0aGlzLm1ha2VLZXlOYW1lKGByb29tTG9jazoke3Jvb219YClcbiAgfVxuXG4gIGFkZFNvY2tldCAoaWQsIHVpZCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5oc2V0KHRoaXMubWFrZUtleU5hbWUoJ3NvY2tldHMnKSwgaWQsIHVpZClcbiAgICAgIC5obGVuKHRoaXMubWFrZUtleU5hbWUoJ3NvY2tldHMnKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC5zcHJlYWQoKF8sIFssIG5jb25uZWN0ZWRdKSA9PiBQcm9taXNlLnJlc29sdmUobmNvbm5lY3RlZCkpXG4gIH1cblxuICBnZXRBbGxTb2NrZXRzICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5oa2V5cyh0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJykpXG4gIH1cblxuICBnZXRTb2NrZXRzVG9JbnN0YW5jZSAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGdldGFsbCh0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJykpXG4gIH1cblxuICBnZXRSb29tVG9Tb2NrZXRzIChyb29tTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNtZW1iZXJzKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpKVxuICB9XG5cbiAgZ2V0U29ja2V0c1RvUm9vbXMgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldFNvY2tldHNUb1Jvb21zKFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpLCB0aGlzLm1ha2VTb2NrZXRUb1Jvb21zKCkpXG4gICAgICAuc3ByZWFkKHJlc3VsdCA9PiB7XG4gICAgICAgIGxldCBkYXRhID0gSlNPTi5wYXJzZShyZXN1bHQpIHx8IHt9XG4gICAgICAgIGZvciAobGV0IFtrLCB2XSBvZiBfLnRvUGFpcnMoZGF0YSkpIHtcbiAgICAgICAgICBpZiAoXy5pc0VtcHR5KHYpKSB7IGRhdGFba10gPSBbXSB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShkYXRhKVxuICAgICAgfSlcbiAgfVxuXG4gIGFkZFNvY2tldFRvUm9vbSAoaWQsIHJvb21OYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLnNhZGQodGhpcy5tYWtlU29ja2V0VG9Sb29tcyhpZCksIHJvb21OYW1lKVxuICAgICAgLnNhZGQodGhpcy5tYWtlUm9vbVRvU29ja2V0cyhyb29tTmFtZSksIGlkKVxuICAgICAgLnNjYXJkKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpKVxuICAgICAgLmV4ZWMoKVxuICAgICAgLnRoZW4oKFssICwgWywgbmpvaW5lZF1dKSA9PiBQcm9taXNlLnJlc29sdmUobmpvaW5lZCkpXG4gIH1cblxuICByZW1vdmVTb2NrZXRGcm9tUm9vbSAoaWQsIHJvb21OYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLnNjYXJkKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpKVxuICAgICAgLnNyZW0odGhpcy5tYWtlU29ja2V0VG9Sb29tcyhpZCksIHJvb21OYW1lKVxuICAgICAgLnNyZW0odGhpcy5tYWtlUm9vbVRvU29ja2V0cyhyb29tTmFtZSksIGlkKVxuICAgICAgLnNjYXJkKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpKVxuICAgICAgLmV4ZWMoKVxuICAgICAgLnRoZW4oKFtbLCB3YXNqb2luZWRdLCAsICwgWywgbmpvaW5lZF1dKSA9PiB7XG4gICAgICAgIGxldCBoYXNDaGFuZ2VkID0gbmpvaW5lZCAhPT0gd2Fzam9pbmVkXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoW25qb2luZWQsIGhhc0NoYW5nZWRdKVxuICAgICAgfSlcbiAgfVxuXG4gIHJlbW92ZUFsbFNvY2tldHNGcm9tUm9vbSAocm9vbU5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5yZW1vdmVBbGxTb2NrZXRzRnJvbVJvb20oXG4gICAgICB0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSwgdGhpcy5tYWtlU29ja2V0VG9Sb29tcygpLCByb29tTmFtZSlcbiAgICAgIC5zcHJlYWQocmVzdWx0ID0+IFByb21pc2UucmVzb2x2ZShKU09OLnBhcnNlKHJlc3VsdCkpKVxuICB9XG5cbiAgcmVtb3ZlU29ja2V0IChpZCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnJlbW92ZVNvY2tldChcbiAgICAgIHRoaXMubWFrZVNvY2tldFRvUm9vbXMoaWQpLCB0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJyksXG4gICAgICB0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKCksIGlkKVxuICAgICAgLnNwcmVhZChyZXN1bHQgPT4gUHJvbWlzZS5yZXNvbHZlKEpTT04ucGFyc2UocmVzdWx0KSkpXG4gIH1cblxuICBsb2NrVG9Sb29tIChyb29tTmFtZSwgdHRsKSB7XG4gICAgcmV0dXJuIHVpZCgxOCkudGhlbih2YWwgPT4ge1xuICAgICAgbGV0IHN0YXJ0ID0gXy5ub3coKVxuICAgICAgcmV0dXJuIHRoaXMubG9jayh0aGlzLm1ha2VSb29tTG9jayhyb29tTmFtZSksIHZhbCwgdHRsKS50aGVuKCgpID0+IHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpLmRpc3Bvc2VyKCgpID0+IHtcbiAgICAgICAgICBpZiAoc3RhcnQgKyB0dGwgPCBfLm5vdygpKSB7XG4gICAgICAgICAgICB0aGlzLnNlcnZlci5lbWl0KFxuICAgICAgICAgICAgICAnbG9ja1RpbWVFeGNlZWRlZCcsIHZhbCwge3VzZXJOYW1lOiB0aGlzLnVzZXJOYW1lLCByb29tTmFtZX0pXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiB0aGlzLnVubG9jayh0aGlzLm1ha2VSb29tTG9jayhyb29tTmFtZSksIHZhbClcbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG59XG5cbi8vIEltcGxlbWVudHMgZ2xvYmFsIHN0YXRlIEFQSS5cbmNsYXNzIFJlZGlzU3RhdGUge1xuXG4gIGNvbnN0cnVjdG9yIChzZXJ2ZXIsIG9wdGlvbnMpIHtcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlclxuICAgIHRoaXMub3B0aW9ucyA9IG9wdGlvbnNcbiAgICB0aGlzLmNsb3NlZCA9IGZhbHNlXG4gICAgaWYgKHRoaXMub3B0aW9ucy51c2VDbHVzdGVyKSB7XG4gICAgICB0aGlzLnJlZGlzID0gbmV3IFJlZGlzLkNsdXN0ZXIoLi4udGhpcy5vcHRpb25zLnJlZGlzT3B0aW9ucylcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IHJlZGlzT3B0aW9ucyA9IF8uY2FzdEFycmF5KHRoaXMub3B0aW9ucy5yZWRpc09wdGlvbnMpXG4gICAgICB0aGlzLnJlZGlzID0gbmV3IFJlZGlzKC4uLnJlZGlzT3B0aW9ucylcbiAgICB9XG4gICAgdGhpcy5Sb29tU3RhdGUgPSBSb29tU3RhdGVSZWRpc1xuICAgIHRoaXMuVXNlclN0YXRlID0gVXNlclN0YXRlUmVkaXNcbiAgICB0aGlzLkRpcmVjdE1lc3NhZ2luZ1N0YXRlID0gRGlyZWN0TWVzc2FnaW5nU3RhdGVSZWRpc1xuICAgIHRoaXMubG9ja1RUTCA9IHRoaXMub3B0aW9ucy5sb2NrVFRMIHx8IDEwMDAwXG4gICAgdGhpcy5pbnN0YW5jZVVJRCA9IHRoaXMuc2VydmVyLmluc3RhbmNlVUlEXG4gICAgdGhpcy5zZXJ2ZXIucmVkaXMgPSB0aGlzLnJlZGlzXG4gICAgZm9yIChsZXQgW2NtZCwgZGVmXSBvZiBfLnRvUGFpcnMobHVhQ29tbWFuZHMpKSB7XG4gICAgICB0aGlzLnJlZGlzLmRlZmluZUNvbW1hbmQoY21kLCB7XG4gICAgICAgIG51bWJlck9mS2V5czogZGVmLm51bWJlck9mS2V5cyxcbiAgICAgICAgbHVhOiBkZWYubHVhXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIG1ha2VLZXlOYW1lIChwcmVmaXgsIG5hbWUsIGtleU5hbWUpIHtcbiAgICByZXR1cm4gYCR7bmFtZXNwYWNlfToke3ByZWZpeH06eyR7bmFtZX19OiR7a2V5TmFtZX1gXG4gIH1cblxuICBoYXNSb29tIChuYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ3Jvb21zJywgbmFtZSwgJ2lzSW5pdCcpKVxuICB9XG5cbiAgaGFzVXNlciAobmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCd1c2VycycsIG5hbWUsICdpc0luaXQnKSlcbiAgfVxuXG4gIGNsb3NlICgpIHtcbiAgICB0aGlzLmNsb3NlZCA9IHRydWVcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5xdWl0KCkucmV0dXJuKClcbiAgfVxuXG4gIGdldFJvb20gKG5hbWUsIGlzUHJlZGljYXRlID0gZmFsc2UpIHtcbiAgICBsZXQgcm9vbSA9IG5ldyBSb29tKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiB0aGlzLmhhc1Jvb20obmFtZSkudGhlbihleGlzdHMgPT4ge1xuICAgICAgaWYgKCFleGlzdHMpIHtcbiAgICAgICAgaWYgKGlzUHJlZGljYXRlKSB7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShudWxsKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxldCBlcnJvciA9IG5ldyBDaGF0U2VydmljZUVycm9yKCdub1Jvb20nLCBuYW1lKVxuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChlcnJvcilcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyb29tKVxuICAgIH0pXG4gIH1cblxuICBhZGRSb29tIChuYW1lLCBzdGF0ZSkge1xuICAgIGxldCByb29tID0gbmV3IFJvb20odGhpcy5zZXJ2ZXIsIG5hbWUpXG4gICAgcmV0dXJuIHJvb20uaW5pdFN0YXRlKHN0YXRlKS5yZXR1cm4ocm9vbSlcbiAgfVxuXG4gIHJlbW92ZVJvb20gKG5hbWUpIHtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgfVxuXG4gIGFkZFNvY2tldCAoaWQsIHVzZXJOYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaHNldChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHRoaXMuaW5zdGFuY2VVSUQsICdzb2NrZXRzJyksIGlkLCB1c2VyTmFtZSlcbiAgfVxuXG4gIHJlbW92ZVNvY2tldCAoaWQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5oZGVsKFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnaW5zdGFuY2VzJywgdGhpcy5pbnN0YW5jZVVJRCwgJ3NvY2tldHMnKSwgaWQpXG4gIH1cblxuICBnZXRJbnN0YW5jZVNvY2tldHMgKHVpZCA9IHRoaXMuaW5zdGFuY2VVSUQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5oZ2V0YWxsKHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHVpZCwgJ3NvY2tldHMnKSlcbiAgfVxuXG4gIHVwZGF0ZUhlYXJ0YmVhdCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnaW5zdGFuY2VzJywgdGhpcy5pbnN0YW5jZVVJRCwgJ2hlYXJ0YmVhdCcpLCBfLm5vdygpKVxuICAgICAgLmNhdGNoUmV0dXJuKClcbiAgfVxuXG4gIGdldEluc3RhbmNlSGVhcnRiZWF0ICh1aWQgPSB0aGlzLmluc3RhbmNlVUlEKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHVpZCwgJ2hlYXJ0YmVhdCcpKVxuICAgICAgLnRoZW4odHMgPT4gdHMgPyBwYXJzZUludCh0cykgOiBudWxsKVxuICB9XG5cbiAgZ2V0T3JBZGRVc2VyIChuYW1lLCBzdGF0ZSkge1xuICAgIGxldCB1c2VyID0gbmV3IFVzZXIodGhpcy5zZXJ2ZXIsIG5hbWUpXG4gICAgcmV0dXJuIHRoaXMuaGFzVXNlcihuYW1lKS50aGVuKGV4aXN0cyA9PiB7XG4gICAgICBpZiAoIWV4aXN0cykge1xuICAgICAgICByZXR1cm4gdXNlci5pbml0U3RhdGUoc3RhdGUpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICAgIH1cbiAgICB9KS5jYXRjaChDaGF0U2VydmljZUVycm9yLCBlID0+IHVzZXIpXG4gICAgICAucmV0dXJuKHVzZXIpXG4gIH1cblxuICBnZXRVc2VyIChuYW1lLCBpc1ByZWRpY2F0ZSA9IGZhbHNlKSB7XG4gICAgbGV0IHVzZXIgPSBuZXcgVXNlcih0aGlzLnNlcnZlciwgbmFtZSlcbiAgICByZXR1cm4gdGhpcy5oYXNVc2VyKG5hbWUpLnRoZW4oZXhpc3RzID0+IHtcbiAgICAgIGlmICghZXhpc3RzKSB7XG4gICAgICAgIGlmIChpc1ByZWRpY2F0ZSkge1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcignbm9Vc2VyJywgbmFtZSlcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodXNlcilcbiAgICB9KVxuICB9XG5cbiAgYWRkVXNlciAobmFtZSwgc3RhdGUpIHtcbiAgICBsZXQgdXNlciA9IG5ldyBVc2VyKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiB1c2VyLmluaXRTdGF0ZShzdGF0ZSkucmV0dXJuKHVzZXIpXG4gIH1cblxuICByZW1vdmVVc2VyIChuYW1lKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gIH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFJlZGlzU3RhdGVcbiJdfQ==
\No newline at end of file