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+IHtcbiAgICAgIHJldHVybiB0aGlzLnJlZGlzLmRlbChcbiAgICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnZXhpc3RzJyksIHRoaXMubWFrZUtleU5hbWUoJ2lzSW5pdCcpKVxuICAgIH0pXG4gIH1cblxuICBzdGFydFJlbW92aW5nICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnaXNJbml0JykpXG4gIH1cblxufVxuXG4vLyBSZWRpcyBsb2NrIG9wZXJhdGlvbnMuXG5jbGFzcyBMb2NrT3BlcmF0aW9ucyB7XG5cbiAgY29uc3RydWN0b3IgKHJlZGlzKSB7XG4gICAgdGhpcy5yZWRpcyA9IHJlZGlzXG4gIH1cblxuICBsb2NrIChrZXksIHZhbCwgdHRsKSB7XG4gICAgcmV0dXJuIHByb21pc2VSZXRyeShcbiAgICAgIHttaW5UaW1lb3V0OiAxMDAsIHJldHJpZXM6IDEwLCBmYWN0b3I6IDEuNSwgcmFuZG9taXplOiB0cnVlfVxuICAgICAgLCAocmV0cnksIG4pID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KGtleSwgdmFsLCAnTlgnLCAnUFgnLCB0dGwpLnRoZW4ocmVzID0+IHtcbiAgICAgICAgICBpZiAoIXJlcykge1xuICAgICAgICAgICAgbGV0IGVycm9yID0gbmV3IENoYXRTZXJ2aWNlRXJyb3IoJ3RpbWVvdXQnKVxuICAgICAgICAgICAgcmV0dXJuIHJldHJ5KGVycm9yKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICAgIH1cbiAgICAgICAgfSkuY2F0Y2gocmV0cnkpXG4gICAgICB9KVxuICB9XG5cbiAgdW5sb2NrIChrZXksIHZhbCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnVubG9jayhrZXksIHZhbClcbiAgfVxuXG59XG5cbi8vIFJlZGlzIHNjcmlwdHMuXG5sZXQgbHVhQ29tbWFuZHMgPSB7XG4gIHVubG9jazoge1xuICAgIG51bWJlck9mS2V5czogMSxcbiAgICBsdWE6IGBcbmlmIHJlZGlzLmNhbGwoXCJnZXRcIixLRVlTWzFdKSA9PSBBUkdWWzFdIHRoZW5cbiAgcmV0dXJuIHJlZGlzLmNhbGwoXCJkZWxcIixLRVlTWzFdKVxuZWxzZVxuICByZXR1cm4gMFxuZW5kYFxuICB9LFxuXG4gIG1lc3NhZ2VBZGQ6IHtcbiAgICBudW1iZXJPZktleXM6IDUsXG4gICAgbHVhOiBgXG5sb2NhbCBtc2cgPSBBUkdWWzFdXG5sb2NhbCB0cyA9IEFSR1ZbMl1cblxubG9jYWwgbGFzdE1lc3NhZ2VJZCA9IEtFWVNbMV1cbmxvY2FsIGhpc3RvcnlNYXhTaXplID0gS0VZU1syXVxubG9jYWwgbWVzc2FnZXNJZHMgPSBLRVlTWzNdXG5sb2NhbCBtZXNzYWdlc1RpbWVzdGFtcHMgPSBLRVlTWzRdXG5sb2NhbCBtZXNzYWdlc0hpc3RvcnkgPSBLRVlTWzVdXG5cbmxvY2FsIGlkID0gdG9udW1iZXIocmVkaXMuY2FsbCgnSU5DUicsIGxhc3RNZXNzYWdlSWQpKVxubG9jYWwgbWF4c3ogPSB0b251bWJlcihyZWRpcy5jYWxsKCdHRVQnLCBoaXN0b3J5TWF4U2l6ZSkpXG5cbnJlZGlzLmNhbGwoJ0xQVVNIJywgbWVzc2FnZXNJZHMsIGlkKVxucmVkaXMuY2FsbCgnTFBVU0gnLCBtZXNzYWdlc1RpbWVzdGFtcHMsIHRzKVxucmVkaXMuY2FsbCgnTFBVU0gnLCBtZXNzYWdlc0hpc3RvcnksIG1zZylcblxubG9jYWwgc3ogPSB0b251bWJlcihyZWRpcy5jYWxsKCdMTEVOJywgbWVzc2FnZXNIaXN0b3J5KSlcblxuaWYgc3ogPiBtYXhzeiB0aGVuXG4gIHJlZGlzLmNhbGwoJ1JQT1AnLCBtZXNzYWdlc0lkcylcbiAgcmVkaXMuY2FsbCgnUlBPUCcsIG1lc3NhZ2VzVGltZXN0YW1wcylcbiAgcmVkaXMuY2FsbCgnUlBPUCcsIG1lc3NhZ2VzSGlzdG9yeSlcbmVuZFxuXG5yZXR1cm4ge2lkfWBcbiAgfSxcblxuICBtZXNzYWdlc0dldDoge1xuICAgIG51bWJlck9mS2V5czogNSxcbiAgICBsdWE6IGBcbmxvY2FsIGlkID0gQVJHVlsxXVxubG9jYWwgbWF4bGVuID0gQVJHVlsyXVxuXG5sb2NhbCBsYXN0TWVzc2FnZUlkID0gS0VZU1sxXVxubG9jYWwgaGlzdG9yeU1heFNpemUgPSBLRVlTWzJdXG5sb2NhbCBtZXNzYWdlc0lkcyA9IEtFWVNbM11cbmxvY2FsIG1lc3NhZ2VzVGltZXN0YW1wcyA9IEtFWVNbNF1cbmxvY2FsIG1lc3NhZ2VzSGlzdG9yeSA9IEtFWVNbNV1cblxubG9jYWwgbGFzdGlkID0gdG9udW1iZXIocmVkaXMuY2FsbCgnR0VUJywgbGFzdE1lc3NhZ2VJZCkpXG5sb2NhbCBtYXhzeiA9IHRvbnVtYmVyKHJlZGlzLmNhbGwoJ0dFVCcsIGhpc3RvcnlNYXhTaXplKSlcbmxvY2FsIGlkID0gbWF0aC5taW4oaWQsIGxhc3RpZClcbmxvY2FsIGVuZHAgPSBsYXN0aWQgLSBpZFxubG9jYWwgbGVuID0gbWF0aC5taW4obWF4bGVuLCBlbmRwKVxubG9jYWwgc3RhcnQgPSBtYXRoLm1heCgwLCBlbmRwIC0gbGVuKVxuXG5pZiBzdGFydCA+PSBlbmRwIHRoZW5cbiAgcmV0dXJuIHt9XG5lbmRcblxuZW5kcCA9IGVuZHAgLSAxXG5sb2NhbCBtc2dzID0gcmVkaXMuY2FsbCgnTFJBTkdFJywgbWVzc2FnZXNIaXN0b3J5LCBzdGFydCwgZW5kcClcbmxvY2FsIHRzcyA9IHJlZGlzLmNhbGwoJ0xSQU5HRScsIG1lc3NhZ2VzVGltZXN0YW1wcywgc3RhcnQsIGVuZHApXG5sb2NhbCBpZHMgPSByZWRpcy5jYWxsKCdMUkFOR0UnLCBtZXNzYWdlc0lkcywgc3RhcnQsIGVuZHApXG5cbnJldHVybiB7bXNncywgdHNzLCBpZHN9YFxuICB9LFxuXG4gIGdldFNvY2tldHNUb1Jvb21zOiB7XG4gICAgbnVtYmVyT2ZLZXlzOiAxLFxuICAgIGx1YTogYFxubG9jYWwgcmVzdWx0ID0ge31cbmxvY2FsIHNvY2tldHMgPSBLRVlTWzFdXG5sb2NhbCBwcmVmaXggPSBBUkdWWzFdXG5sb2NhbCBpZHMgPSByZWRpcy5jYWxsKCdIS0VZUycsIHNvY2tldHMpXG5cbmlmIHRhYmxlLmdldG4oaWRzKSA9PSAwIHRoZW5cbiAgbG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZShjanNvbi5udWxsKVxuICByZXR1cm4ge2pzb25SZXN1bHR9XG5lbmRcblxuZm9yIGksIGlkIGluIHBhaXJzKGlkcykgZG9cbiAgbG9jYWwgam9pbmVkID0gcmVkaXMuY2FsbCgnU01FTUJFUlMnLCBwcmVmaXggLi4gaWQpXG4gIHJlc3VsdFtpZF0gPSBqb2luZWRcbmVuZFxuXG5sb2NhbCBqc29uUmVzdWx0ID0gY2pzb24uZW5jb2RlKHJlc3VsdClcbnJldHVybiB7anNvblJlc3VsdH1gXG4gIH0sXG5cbiAgcmVtb3ZlQWxsU29ja2V0c0Zyb21Sb29tOiB7XG4gICAgbnVtYmVyT2ZLZXlzOiAxLFxuICAgIGx1YTogYFxubG9jYWwgcm9vbSA9IEtFWVNbMV1cbmxvY2FsIHByZWZpeCA9IEFSR1ZbMV1cbmxvY2FsIHJvb21OYW1lID0gQVJHVlsyXVxubG9jYWwgaWRzID0gcmVkaXMuY2FsbCgnU01FTUJFUlMnLCByb29tKVxuXG5pZiB0YWJsZS5nZXRuKGlkcykgPT0gMCB0aGVuXG4gIGxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoY2pzb24ubnVsbClcbiAgcmV0dXJuIHtqc29uUmVzdWx0fVxuZW5kXG5cbnJlZGlzLmNhbGwoJ0RFTCcsIHJvb20pXG5cbmZvciBpLCBpZCBpbiBwYWlycyhpZHMpIGRvXG4gIHJlZGlzLmNhbGwoJ1NSRU0nLCBwcmVmaXggLi4gaWQsIHJvb21OYW1lKVxuZW5kXG5cbmxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoaWRzKVxucmV0dXJuIHtqc29uUmVzdWx0fWBcbiAgfSxcblxuICByZW1vdmVTb2NrZXQ6IHtcbiAgICBudW1iZXJPZktleXM6IDIsXG4gICAgbHVhOiBgXG5sb2NhbCBpZCA9IEtFWVNbMV1cbmxvY2FsIHNvY2tldHMgPSBLRVlTWzJdXG5sb2NhbCBwcmVmaXggPSBBUkdWWzFdXG5sb2NhbCBzb2NrZXRpZCA9IEFSR1ZbMl1cblxubG9jYWwgcm9vbXMgPSByZWRpcy5jYWxsKCdTTUVNQkVSUycsIGlkKVxucmVkaXMuY2FsbCgnREVMJywgaWQpXG5cbnJlZGlzLmNhbGwoJ0hERUwnLCBzb2NrZXRzLCBzb2NrZXRpZClcbmxvY2FsIG5jb25uZWN0ZWQgPSByZWRpcy5jYWxsKCdITEVOJywgc29ja2V0cylcblxubG9jYWwgcmVtb3ZlZFJvb21zID0ge31cbmxvY2FsIGpvaW5lZFNvY2tldHMgPSB7fVxuXG5mb3IgaSwgcm9vbSBpbiBwYWlycyhyb29tcykgZG9cbiAgbG9jYWwgaXNtZW1iZXIgPSByZWRpcy5jYWxsKCdTSVNNRU1CRVInLCBwcmVmaXggLi4gcm9vbSwgc29ja2V0aWQpXG4gIGlmIGlzbWVtYmVyID09IDEgdGhlblxuICAgIHJlZGlzLmNhbGwoJ1NSRU0nLCBwcmVmaXggLi4gcm9vbSwgc29ja2V0aWQpXG4gICAgbG9jYWwgbmpvaW5lZCA9IHJlZGlzLmNhbGwoJ1NDQVJEJywgcHJlZml4IC4uIHJvb20pXG4gICAgdGFibGUuaW5zZXJ0KHJlbW92ZWRSb29tcywgcm9vbSlcbiAgICB0YWJsZS5pbnNlcnQoam9pbmVkU29ja2V0cywgbmpvaW5lZClcbiAgZW5kXG5lbmRcblxuaWYgdGFibGUuZ2V0bihyZW1vdmVkUm9vbXMpID09IDAgb3IgdGFibGUuZ2V0bihyb29tcykgPT0gMCB0aGVuXG4gIGxvY2FsIGpzb25SZXN1bHQgPSBjanNvbi5lbmNvZGUoe2Nqc29uLm51bGwsIGNqc29uLm51bGwsIG5jb25uZWN0ZWR9KVxuICByZXR1cm4ge2pzb25SZXN1bHR9XG5lbmRcblxubG9jYWwganNvblJlc3VsdCA9IGNqc29uLmVuY29kZSh7cmVtb3ZlZFJvb21zLCBqb2luZWRTb2NrZXRzLCBuY29ubmVjdGVkfSlcbnJldHVybiB7anNvblJlc3VsdH1gXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIHN0YXRlIEFQSSBsaXN0cyBtYW5hZ2VtZW50LlxuY2xhc3MgTGlzdHNTdGF0ZVJlZGlzIHtcblxuICBtYWtlS2V5TmFtZSAoa2V5TmFtZSkge1xuICAgIHJldHVybiBgJHtuYW1lc3BhY2V9OiR7dGhpcy5wcmVmaXh9Onske3RoaXMubmFtZX19OiR7a2V5TmFtZX1gXG4gIH1cblxuICBjaGVja0xpc3QgKGxpc3ROYW1lLCBudW0sIGxpbWl0KSB7XG4gICAgaWYgKCF0aGlzLmhhc0xpc3QobGlzdE5hbWUpKSB7XG4gICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcignbm9MaXN0JywgbGlzdE5hbWUpXG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgfVxuICAgIGlmIChsaXN0TmFtZSA9PT0gJ3VzZXJsaXN0Jykge1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgfVxuICAgIHJldHVybiB0aGlzLnJlZGlzLnNjYXJkKGxpc3ROYW1lKS50aGVuKHN6ID0+IHtcbiAgICAgIGlmIChzeiArIG51bSA+IGxpbWl0KSB7XG4gICAgICAgIGxldCBlcnJvciA9IG5ldyBDaGF0U2VydmljZUVycm9yKCdsaXN0TGltaXRFeGNlZWRlZCcsIGxpc3ROYW1lKVxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKClcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgYWRkVG9MaXN0IChsaXN0TmFtZSwgZWxlbXMsIGxpbWl0KSB7XG4gICAgbGV0IG51bSA9IGVsZW1zLmxlbmd0aFxuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSwgbnVtLCBsaW1pdClcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc2FkZCh0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbXMpKVxuICB9XG5cbiAgcmVtb3ZlRnJvbUxpc3QgKGxpc3ROYW1lLCBlbGVtcykge1xuICAgIHJldHVybiB0aGlzLmNoZWNrTGlzdChsaXN0TmFtZSlcbiAgICAgIC50aGVuKCgpID0+IHRoaXMucmVkaXMuc3JlbSh0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbXMpKVxuICB9XG5cbiAgZ2V0TGlzdCAobGlzdE5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5jaGVja0xpc3QobGlzdE5hbWUpXG4gICAgICAudGhlbigoKSA9PiB0aGlzLnJlZGlzLnNtZW1iZXJzKHRoaXMubWFrZUtleU5hbWUobGlzdE5hbWUpKSlcbiAgfVxuXG4gIGhhc0luTGlzdCAobGlzdE5hbWUsIGVsZW0pIHtcbiAgICByZXR1cm4gdGhpcy5jaGVja0xpc3QobGlzdE5hbWUpXG4gICAgICAudGhlbigoKSA9PiB0aGlzLnJlZGlzLnNpc21lbWJlcih0aGlzLm1ha2VLZXlOYW1lKGxpc3ROYW1lKSwgZWxlbSkpXG4gICAgICAudGhlbihkYXRhID0+IFByb21pc2UucmVzb2x2ZShCb29sZWFuKGRhdGEpKSlcbiAgfVxuXG4gIHdoaXRlbGlzdE9ubHlTZXQgKG1vZGUpIHtcbiAgICBsZXQgd2hpdGVsaXN0T25seSA9IG1vZGUgPyB0cnVlIDogJydcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnd2hpdGVsaXN0TW9kZScpLCB3aGl0ZWxpc3RPbmx5KVxuICB9XG5cbiAgd2hpdGVsaXN0T25seUdldCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdE1vZGUnKSlcbiAgICAgIC50aGVuKGRhdGEgPT4gUHJvbWlzZS5yZXNvbHZlKEJvb2xlYW4oZGF0YSkpKVxuICB9XG5cbn1cblxuLy8gSW1wbGVtZW50cyByb29tIHN0YXRlIEFQSS5cbmNsYXNzIFJvb21TdGF0ZVJlZGlzIGV4dGVuZHMgTGlzdHNTdGF0ZVJlZGlzIHtcblxuICBjb25zdHJ1Y3RvciAoc2VydmVyLCByb29tTmFtZSkge1xuICAgIHN1cGVyKClcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlclxuICAgIHRoaXMucm9vbU5hbWUgPSByb29tTmFtZVxuICAgIHRoaXMubmFtZSA9IHRoaXMucm9vbU5hbWVcbiAgICB0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcyA9IHRoaXMuc2VydmVyLmhpc3RvcnlNYXhHZXRNZXNzYWdlc1xuICAgIHRoaXMucmVkaXMgPSB0aGlzLnNlcnZlci5yZWRpc1xuICAgIHRoaXMuZXhpdHNFcnJvck5hbWUgPSAncm9vbUV4aXN0cydcbiAgICB0aGlzLnByZWZpeCA9ICdyb29tcydcbiAgICBtaXhpbih0aGlzLCBTdGF0ZU9wZXJhdGlvbnMsIHRoaXMubmFtZSwgdGhpcy5leGl0c0Vycm9yTmFtZSwgdGhpcy5yZWRpcyxcbiAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lLmJpbmQodGhpcyksIHRoaXMuc3RhdGVSZXNldC5iaW5kKHRoaXMpKVxuICB9XG5cbiAgc3RhdGVSZXNldCAoc3RhdGUpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IHt9XG4gICAgbGV0IHsgd2hpdGVsaXN0LCBibGFja2xpc3QsIGFkbWlubGlzdCxcbiAgICAgICAgICB3aGl0ZWxpc3RPbmx5LCBvd25lciwgaGlzdG9yeU1heFNpemUsXG4gICAgICAgICAgZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID0gdGhpcy5zZXJ2ZXIuZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzLFxuICAgICAgICAgIGVuYWJsZVVzZXJsaXN0VXBkYXRlcyA9IHRoaXMuc2VydmVyLmVuYWJsZVVzZXJsaXN0VXBkYXRlc1xuICAgICAgICB9ID0gc3RhdGVcbiAgICBpZiAoIW93bmVyKSB7IG93bmVyID0gJycgfVxuICAgIHJldHVybiBQcm9taXNlLmFsbChbXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ3doaXRlbGlzdCcpLCB3aGl0ZWxpc3QpLFxuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCdibGFja2xpc3QnKSwgYmxhY2tsaXN0KSxcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgnYWRtaW5saXN0JyksIGFkbWlubGlzdCksXG4gICAgICBpbml0U2V0KHRoaXMucmVkaXMsIHRoaXMubWFrZUtleU5hbWUoJ3VzZXJsaXN0JyksIG51bGwpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JykpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJykpLFxuICAgICAgdGhpcy5yZWRpcy5kZWwodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNJZHMnKSksXG4gICAgICB0aGlzLnJlZGlzLmRlbCh0aGlzLm1ha2VLZXlOYW1lKCd1c2Vyc3NlZW4nKSksXG4gICAgICB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJyksIDApLFxuICAgICAgdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnb3duZXInKSwgb3duZXIpLFxuICAgICAgdGhpcy53aGl0ZWxpc3RPbmx5U2V0KHdoaXRlbGlzdE9ubHkpLFxuICAgICAgdGhpcy5hY2Nlc3NMaXN0c1VwZGF0ZXNTZXQoZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzKSxcbiAgICAgIHRoaXMudXNlcmxpc3RVcGRhdGVzU2V0KGVuYWJsZVVzZXJsaXN0VXBkYXRlcyksXG4gICAgICB0aGlzLmhpc3RvcnlNYXhTaXplU2V0KGhpc3RvcnlNYXhTaXplKVxuICAgIF0pLnJldHVybigpXG4gIH1cblxuICBoYXNMaXN0IChsaXN0TmFtZSkge1xuICAgIHJldHVybiBsaXN0TmFtZSA9PT0gJ2FkbWlubGlzdCcgfHwgbGlzdE5hbWUgPT09ICd3aGl0ZWxpc3QnIHx8XG4gICAgICBsaXN0TmFtZSA9PT0gJ2JsYWNrbGlzdCcgfHwgbGlzdE5hbWUgPT09ICd1c2VybGlzdCdcbiAgfVxuXG4gIG93bmVyR2V0ICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgnb3duZXInKSlcbiAgfVxuXG4gIG93bmVyU2V0IChvd25lcikge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdvd25lcicpLCBvd25lcilcbiAgfVxuXG4gIGFjY2Vzc0xpc3RzVXBkYXRlc1NldCAoZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzKSB7XG4gICAgZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID0gZW5hYmxlQWNjZXNzTGlzdHNVcGRhdGVzID8gdHJ1ZSA6ICcnXG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2VuYWJsZUFjY2Vzc0xpc3RzVXBkYXRlcycpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMpXG4gIH1cblxuICBhY2Nlc3NMaXN0c1VwZGF0ZXNHZXQgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdlbmFibGVBY2Nlc3NMaXN0c1VwZGF0ZXMnKSlcbiAgICAgIC50aGVuKGRhdGEgPT4gUHJvbWlzZS5yZXNvbHZlKEJvb2xlYW4oZGF0YSkpKVxuICB9XG5cbiAgdXNlcmxpc3RVcGRhdGVzU2V0IChlbmFibGVVc2VybGlzdFVwZGF0ZXMpIHtcbiAgICBlbmFibGVVc2VybGlzdFVwZGF0ZXMgPSBlbmFibGVVc2VybGlzdFVwZGF0ZXMgPyB0cnVlIDogJydcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zZXQodGhpcy5tYWtlS2V5TmFtZSgnZW5hYmxlVXNlcmxpc3RVcGRhdGVzJyksXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGVuYWJsZVVzZXJsaXN0VXBkYXRlcylcbiAgfVxuXG4gIHVzZXJsaXN0VXBkYXRlc0dldCAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuZ2V0KHRoaXMubWFrZUtleU5hbWUoJ2VuYWJsZVVzZXJsaXN0VXBkYXRlcycpKVxuICAgICAgLnRoZW4oZGF0YSA9PiBQcm9taXNlLnJlc29sdmUoQm9vbGVhbihkYXRhKSkpXG4gIH1cblxuICBoaXN0b3J5TWF4U2l6ZVNldCAoaGlzdG9yeU1heFNpemUpIHtcbiAgICBsZXQgbGltaXQgPSBoaXN0b3J5TWF4U2l6ZVxuICAgIGlmICghKF8uaXNOdW1iZXIoaGlzdG9yeU1heFNpemUpICYmIGhpc3RvcnlNYXhTaXplID49IDApKSB7XG4gICAgICBsaW1pdCA9IHRoaXMuc2VydmVyLmhpc3RvcnlNYXhTaXplXG4gICAgfVxuICAgIGlmIChsaW1pdCA9PT0gMCkge1xuICAgICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgICAuc2V0KHRoaXMubWFrZUtleU5hbWUoJ2hpc3RvcnlNYXhTaXplJyksIGxpbWl0KVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpKVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpKVxuICAgICAgICAuZGVsKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJykpXG4gICAgICAgIC5leGVjKClcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IGxhc3QgPSBsaW1pdCAtIDFcbiAgICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgICAgLnNldCh0aGlzLm1ha2VLZXlOYW1lKCdoaXN0b3J5TWF4U2l6ZScpLCBsaW1pdClcbiAgICAgICAgLmx0cmltKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpLCAwLCBsYXN0KVxuICAgICAgICAubHRyaW0odGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNUaW1lc3RhbXBzJyksIDAsIGxhc3QpXG4gICAgICAgIC5sdHJpbSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0lkcycpLCAwLCBsYXN0KVxuICAgICAgICAuZXhlYygpXG4gICAgfVxuICB9XG5cbiAgaGlzdG9yeUluZm8gKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5nZXQodGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSlcbiAgICAgIC5sbGVuKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpKVxuICAgICAgLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJykpXG4gICAgICAuZXhlYygpXG4gICAgICAuc3ByZWFkKChbLCBoaXN0b3J5TWF4U2l6ZV0sIFssIGhpc3RvcnlTaXplXSwgWywgbGFzdE1lc3NhZ2VJZF0pID0+IHtcbiAgICAgICAgaGlzdG9yeVNpemUgPSBwYXJzZUludChoaXN0b3J5U2l6ZSlcbiAgICAgICAgaGlzdG9yeU1heFNpemUgPSBwYXJzZUZsb2F0KGhpc3RvcnlNYXhTaXplKVxuICAgICAgICBsYXN0TWVzc2FnZUlkID0gcGFyc2VJbnQobGFzdE1lc3NhZ2VJZClcbiAgICAgICAgbGV0IGluZm8gPSB7IGhpc3RvcnlTaXplLFxuICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeU1heFNpemUsXG4gICAgICAgICAgICAgICAgICAgICBoaXN0b3J5TWF4R2V0TWVzc2FnZXM6IHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzLFxuICAgICAgICAgICAgICAgICAgICAgbGFzdE1lc3NhZ2VJZCB9XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoaW5mbylcbiAgICAgIH0pXG4gIH1cblxuICBnZXRDb21tb25Vc2VycyAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuc2RpZmYodGhpcy5tYWtlS2V5TmFtZSgndXNlcmxpc3QnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3QnKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lKCdhZG1pbmxpc3QnKSlcbiAgfVxuXG4gIG1lc3NhZ2VBZGQgKG1zZykge1xuICAgIGxldCB0aW1lc3RhbXAgPSBfLm5vdygpXG4gICAgbGV0IHNtc2cgPSBKU09OLnN0cmluZ2lmeShtc2cpXG4gICAgcmV0dXJuIHRoaXMucmVkaXMubWVzc2FnZUFkZChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2xhc3RNZXNzYWdlSWQnKSwgdGhpcy5tYWtlS2V5TmFtZSgnaGlzdG9yeU1heFNpemUnKSxcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzVGltZXN0YW1wcycpLFxuICAgICAgdGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIHNtc2csIHRpbWVzdGFtcClcbiAgICAgIC5zcHJlYWQoaWQgPT4ge1xuICAgICAgICBtc2cuaWQgPSBpZFxuICAgICAgICBtc2cudGltZXN0YW1wID0gdGltZXN0YW1wXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobXNnKVxuICAgICAgfSlcbiAgfVxuXG4gIGNvbnZlcnRNZXNzYWdlcyAobXNncywgdHNzLCBpZHMpIHtcbiAgICBsZXQgZGF0YSA9IFtdXG4gICAgaWYgKCFtc2dzKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpXG4gICAgfVxuICAgIGZvciAobGV0IGlkeCA9IDA7IGlkeCA8IG1zZ3MubGVuZ3RoOyBpZHgrKykge1xuICAgICAgbGV0IG1zZyA9IG1zZ3NbaWR4XVxuICAgICAgbGV0IG9iaiA9IEpTT04ucGFyc2UobXNnLCAoa2V5LCB2YWwpID0+IHtcbiAgICAgICAgaWYgKHZhbCAmJiB2YWwudHlwZSA9PT0gJ0J1ZmZlcicpIHtcbiAgICAgICAgICByZXR1cm4gbmV3IEJ1ZmZlcih2YWwuZGF0YSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gdmFsXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICBvYmoudGltZXN0YW1wID0gcGFyc2VJbnQodHNzW2lkeF0pXG4gICAgICBvYmouaWQgPSBwYXJzZUludChpZHNbaWR4XSlcbiAgICAgIGRhdGFbaWR4XSA9IG9ialxuICAgIH1cbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGRhdGEpXG4gIH1cblxuICBtZXNzYWdlc0dldFJlY2VudCAoKSB7XG4gICAgaWYgKHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzIDw9IDApIHsgcmV0dXJuIFByb21pc2UucmVzb2x2ZShbXSkgfVxuICAgIGxldCBsaW1pdCA9IHRoaXMuaGlzdG9yeU1heEdldE1lc3NhZ2VzIC0gMVxuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5scmFuZ2UodGhpcy5tYWtlS2V5TmFtZSgnbWVzc2FnZXNIaXN0b3J5JyksIDAsIGxpbWl0KVxuICAgICAgLmxyYW5nZSh0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSwgMCwgbGltaXQpXG4gICAgICAubHJhbmdlKHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSWRzJyksIDAsIGxpbWl0KVxuICAgICAgLmV4ZWMoKVxuICAgICAgLnNwcmVhZCgoWywgbXNnc10sIFssIHRzc10sIFssIGlkc10pID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udmVydE1lc3NhZ2VzKG1zZ3MsIHRzcywgaWRzKVxuICAgICAgfSlcbiAgfVxuXG4gIG1lc3NhZ2VzR2V0IChpZCwgbWF4TWVzc2FnZXMgPSB0aGlzLmhpc3RvcnlNYXhHZXRNZXNzYWdlcykge1xuICAgIGlmIChtYXhNZXNzYWdlcyA8PSAwKSB7IHJldHVybiBQcm9taXNlLnJlc29sdmUoW10pIH1cbiAgICBpZCA9IF8ubWF4KFswLCBpZF0pXG4gICAgcmV0dXJuIHRoaXMucmVkaXMubWVzc2FnZXNHZXQoXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdsYXN0TWVzc2FnZUlkJyksIHRoaXMubWFrZUtleU5hbWUoJ2hpc3RvcnlNYXhTaXplJyksXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc0lkcycpLCB0aGlzLm1ha2VLZXlOYW1lKCdtZXNzYWdlc1RpbWVzdGFtcHMnKSxcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ21lc3NhZ2VzSGlzdG9yeScpLCBpZCwgbWF4TWVzc2FnZXMpXG4gICAgICAuc3ByZWFkKChtc2dzLCB0c3MsIGlkcykgPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jb252ZXJ0TWVzc2FnZXMobXNncywgdHNzLCBpZHMpXG4gICAgICB9KVxuICB9XG5cbiAgdXNlclNlZW5HZXQgKHVzZXJOYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMubXVsdGkoKVxuICAgICAgLmhnZXQodGhpcy5tYWtlS2V5TmFtZSgndXNlcnNzZWVuJyksIHVzZXJOYW1lKVxuICAgICAgLnNpc21lbWJlcih0aGlzLm1ha2VLZXlOYW1lKCd1c2VybGlzdCcpLCB1c2VyTmFtZSlcbiAgICAgIC5leGVjKClcbiAgICAgIC5zcHJlYWQoKFssIHRzXSwgWywgaXNqb2luZWRdKSA9PiB7XG4gICAgICAgIGxldCBqb2luZWQgPSBCb29sZWFuKGlzam9pbmVkKVxuICAgICAgICBsZXQgdGltZXN0YW1wID0gdHMgPyBwYXJzZUludCh0cykgOiBudWxsXG4gICAgICAgIHJldHVybiB7am9pbmVkLCB0aW1lc3RhbXB9XG4gICAgICB9KVxuICB9XG5cbiAgdXNlclNlZW5VcGRhdGUgKHVzZXJOYW1lKSB7XG4gICAgbGV0IHRpbWVzdGFtcCA9IF8ubm93KClcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5oc2V0KHRoaXMubWFrZUtleU5hbWUoJ3VzZXJzc2VlbicpLCB1c2VyTmFtZSwgdGltZXN0YW1wKVxuICB9XG5cbn1cblxuLy8gSW1wbGVtZW50cyBkaXJlY3QgbWVzc2FnaW5nIHN0YXRlIEFQSS5cbmNsYXNzIERpcmVjdE1lc3NhZ2luZ1N0YXRlUmVkaXMgZXh0ZW5kcyBMaXN0c1N0YXRlUmVkaXMge1xuXG4gIGNvbnN0cnVjdG9yIChzZXJ2ZXIsIHVzZXJOYW1lKSB7XG4gICAgc3VwZXIoKVxuICAgIHRoaXMuc2VydmVyID0gc2VydmVyXG4gICAgdGhpcy51c2VyTmFtZSA9IHVzZXJOYW1lXG4gICAgdGhpcy5uYW1lID0gdGhpcy51c2VyTmFtZVxuICAgIHRoaXMucHJlZml4ID0gJ3VzZXJzJ1xuICAgIHRoaXMuZXhpdHNFcnJvck5hbWUgPSAndXNlckV4aXN0cydcbiAgICB0aGlzLnJlZGlzID0gdGhpcy5zZXJ2ZXIucmVkaXNcbiAgICBtaXhpbih0aGlzLCBTdGF0ZU9wZXJhdGlvbnMsIHRoaXMubmFtZSwgdGhpcy5leGl0c0Vycm9yTmFtZSwgdGhpcy5yZWRpcyxcbiAgICAgICAgICB0aGlzLm1ha2VLZXlOYW1lLmJpbmQodGhpcyksIHRoaXMuc3RhdGVSZXNldC5iaW5kKHRoaXMpKVxuICB9XG5cbiAgaGFzTGlzdCAobGlzdE5hbWUpIHtcbiAgICByZXR1cm4gbGlzdE5hbWUgPT09ICd3aGl0ZWxpc3QnIHx8IGxpc3ROYW1lID09PSAnYmxhY2tsaXN0J1xuICB9XG5cbiAgc3RhdGVSZXNldCAoc3RhdGUpIHtcbiAgICBzdGF0ZSA9IHN0YXRlIHx8IHt9XG4gICAgbGV0IHsgd2hpdGVsaXN0LCBibGFja2xpc3QsIHdoaXRlbGlzdE9ubHkgfSA9IHN0YXRlXG4gICAgd2hpdGVsaXN0T25seSA9IHdoaXRlbGlzdE9ubHkgPyB0cnVlIDogJydcbiAgICByZXR1cm4gUHJvbWlzZS5hbGwoW1xuICAgICAgaW5pdFNldCh0aGlzLnJlZGlzLCB0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3QnKSwgd2hpdGVsaXN0KSxcbiAgICAgIGluaXRTZXQodGhpcy5yZWRpcywgdGhpcy5tYWtlS2V5TmFtZSgnYmxhY2tsaXN0JyksIGJsYWNrbGlzdCksXG4gICAgICB0aGlzLnJlZGlzLnNldCh0aGlzLm1ha2VLZXlOYW1lKCd3aGl0ZWxpc3RNb2RlJyksIHdoaXRlbGlzdE9ubHkpXG4gICAgXSkucmV0dXJuKClcbiAgfVxuXG59XG5cbi8vIEltcGxlbWVudHMgdXNlciBzdGF0ZSBBUEkuXG5jbGFzcyBVc2VyU3RhdGVSZWRpcyB7XG5cbiAgY29uc3RydWN0b3IgKHNlcnZlciwgdXNlck5hbWUpIHtcbiAgICB0aGlzLnNlcnZlciA9IHNlcnZlclxuICAgIHRoaXMudXNlck5hbWUgPSB1c2VyTmFtZVxuICAgIHRoaXMubmFtZSA9IHRoaXMudXNlck5hbWVcbiAgICB0aGlzLnByZWZpeCA9ICd1c2VycydcbiAgICB0aGlzLnJlZGlzID0gdGhpcy5zZXJ2ZXIucmVkaXNcbiAgICBtaXhpbih0aGlzLCBMb2NrT3BlcmF0aW9ucywgdGhpcy5yZWRpcylcbiAgfVxuXG4gIG1ha2VLZXlOYW1lIChrZXlOYW1lKSB7XG4gICAgcmV0dXJuIGAke25hbWVzcGFjZX06JHt0aGlzLnByZWZpeH06eyR7dGhpcy5uYW1lfX06JHtrZXlOYW1lfWBcbiAgfVxuXG4gIG1ha2VTb2NrZXRUb1Jvb21zIChpZCA9ICcnKSB7XG4gICAgcmV0dXJuIHRoaXMubWFrZUtleU5hbWUoYHNvY2tldHNUb1Jvb21zOiR7aWR9YClcbiAgfVxuXG4gIG1ha2VSb29tVG9Tb2NrZXRzIChyb29tID0gJycpIHtcbiAgICByZXR1cm4gdGhpcy5tYWtlS2V5TmFtZShgcm9vbXNUb1NvY2tldHM6JHtyb29tfWApXG4gIH1cblxuICBtYWtlUm9vbUxvY2sgKHJvb20pIHtcbiAgICByZXR1cm4gdGhpcy5tYWtlS2V5TmFtZShgcm9vbUxvY2s6JHtyb29tfWApXG4gIH1cblxuICBhZGRTb2NrZXQgKGlkLCB1aWQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5tdWx0aSgpXG4gICAgICAuaHNldCh0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJyksIGlkLCB1aWQpXG4gICAgICAuaGxlbih0aGlzLm1ha2VLZXlOYW1lKCdzb2NrZXRzJykpXG4gICAgICAuZXhlYygpXG4gICAgICAuc3ByZWFkKChfLCBbLCBuY29ubmVjdGVkXSkgPT4gUHJvbWlzZS5yZXNvbHZlKG5jb25uZWN0ZWQpKVxuICB9XG5cbiAgZ2V0QWxsU29ja2V0cyAoKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGtleXModGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpKVxuICB9XG5cbiAgZ2V0U29ja2V0c1RvSW5zdGFuY2UgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmhnZXRhbGwodGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpKVxuICB9XG5cbiAgZ2V0Um9vbVRvU29ja2V0cyAocm9vbU5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5zbWVtYmVycyh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgfVxuXG4gIGdldFNvY2tldHNUb1Jvb21zICgpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXRTb2NrZXRzVG9Sb29tcyhcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ3NvY2tldHMnKSwgdGhpcy5tYWtlU29ja2V0VG9Sb29tcygpKVxuICAgICAgLnNwcmVhZChyZXN1bHQgPT4ge1xuICAgICAgICBsZXQgZGF0YSA9IEpTT04ucGFyc2UocmVzdWx0KSB8fCB7fVxuICAgICAgICBmb3IgKGxldCBbaywgdl0gb2YgXy50b1BhaXJzKGRhdGEpKSB7XG4gICAgICAgICAgaWYgKF8uaXNFbXB0eSh2KSkgeyBkYXRhW2tdID0gW10gfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZGF0YSlcbiAgICAgIH0pXG4gIH1cblxuICBhZGRTb2NrZXRUb1Jvb20gKGlkLCByb29tTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5zYWRkKHRoaXMubWFrZVNvY2tldFRvUm9vbXMoaWQpLCByb29tTmFtZSlcbiAgICAgIC5zYWRkKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpLCBpZClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC50aGVuKChbLCAsIFssIG5qb2luZWRdXSkgPT4gUHJvbWlzZS5yZXNvbHZlKG5qb2luZWQpKVxuICB9XG5cbiAgcmVtb3ZlU29ja2V0RnJvbVJvb20gKGlkLCByb29tTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLm11bHRpKClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5zcmVtKHRoaXMubWFrZVNvY2tldFRvUm9vbXMoaWQpLCByb29tTmFtZSlcbiAgICAgIC5zcmVtKHRoaXMubWFrZVJvb21Ub1NvY2tldHMocm9vbU5hbWUpLCBpZClcbiAgICAgIC5zY2FyZCh0aGlzLm1ha2VSb29tVG9Tb2NrZXRzKHJvb21OYW1lKSlcbiAgICAgIC5leGVjKClcbiAgICAgIC50aGVuKChbWywgd2Fzam9pbmVkXSwgLCAsIFssIG5qb2luZWRdXSkgPT4ge1xuICAgICAgICBsZXQgaGFzQ2hhbmdlZCA9IG5qb2luZWQgIT09IHdhc2pvaW5lZFxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKFtuam9pbmVkLCBoYXNDaGFuZ2VkXSlcbiAgICAgIH0pXG4gIH1cblxuICByZW1vdmVBbGxTb2NrZXRzRnJvbVJvb20gKHJvb21OYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMucmVtb3ZlQWxsU29ja2V0c0Zyb21Sb29tKFxuICAgICAgdGhpcy5tYWtlUm9vbVRvU29ja2V0cyhyb29tTmFtZSksIHRoaXMubWFrZVNvY2tldFRvUm9vbXMoKSwgcm9vbU5hbWUpXG4gICAgICAuc3ByZWFkKHJlc3VsdCA9PiBQcm9taXNlLnJlc29sdmUoSlNPTi5wYXJzZShyZXN1bHQpKSlcbiAgfVxuXG4gIHJlbW92ZVNvY2tldCAoaWQpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5yZW1vdmVTb2NrZXQoXG4gICAgICB0aGlzLm1ha2VTb2NrZXRUb1Jvb21zKGlkKSwgdGhpcy5tYWtlS2V5TmFtZSgnc29ja2V0cycpLFxuICAgICAgdGhpcy5tYWtlUm9vbVRvU29ja2V0cygpLCBpZClcbiAgICAgIC5zcHJlYWQocmVzdWx0ID0+IFByb21pc2UucmVzb2x2ZShKU09OLnBhcnNlKHJlc3VsdCkpKVxuICB9XG5cbiAgbG9ja1RvUm9vbSAocm9vbU5hbWUsIHR0bCkge1xuICAgIHJldHVybiB1aWQoMTgpLnRoZW4odmFsID0+IHtcbiAgICAgIGxldCBzdGFydCA9IF8ubm93KClcbiAgICAgIHJldHVybiB0aGlzLmxvY2sodGhpcy5tYWtlUm9vbUxvY2socm9vbU5hbWUpLCB2YWwsIHR0bCkudGhlbigoKSA9PiB7XG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKS5kaXNwb3NlcigoKSA9PiB7XG4gICAgICAgICAgaWYgKHN0YXJ0ICsgdHRsIDwgXy5ub3coKSkge1xuICAgICAgICAgICAgdGhpcy5zZXJ2ZXIuZW1pdChcbiAgICAgICAgICAgICAgJ2xvY2tUaW1lRXhjZWVkZWQnLCB2YWwsIHt1c2VyTmFtZTogdGhpcy51c2VyTmFtZSwgcm9vbU5hbWV9KVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gdGhpcy51bmxvY2sodGhpcy5tYWtlUm9vbUxvY2socm9vbU5hbWUpLCB2YWwpXG4gICAgICAgIH0pXG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxufVxuXG4vLyBJbXBsZW1lbnRzIGdsb2JhbCBzdGF0ZSBBUEkuXG5jbGFzcyBSZWRpc1N0YXRlIHtcblxuICBjb25zdHJ1Y3RvciAoc2VydmVyLCBvcHRpb25zKSB7XG4gICAgdGhpcy5zZXJ2ZXIgPSBzZXJ2ZXJcbiAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zXG4gICAgdGhpcy5jbG9zZWQgPSBmYWxzZVxuICAgIGlmICh0aGlzLm9wdGlvbnMudXNlQ2x1c3Rlcikge1xuICAgICAgdGhpcy5yZWRpcyA9IG5ldyBSZWRpcy5DbHVzdGVyKC4uLnRoaXMub3B0aW9ucy5yZWRpc09wdGlvbnMpXG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCByZWRpc09wdGlvbnMgPSBfLmNhc3RBcnJheSh0aGlzLm9wdGlvbnMucmVkaXNPcHRpb25zKVxuICAgICAgdGhpcy5yZWRpcyA9IG5ldyBSZWRpcyguLi5yZWRpc09wdGlvbnMpXG4gICAgfVxuICAgIHRoaXMuUm9vbVN0YXRlID0gUm9vbVN0YXRlUmVkaXNcbiAgICB0aGlzLlVzZXJTdGF0ZSA9IFVzZXJTdGF0ZVJlZGlzXG4gICAgdGhpcy5EaXJlY3RNZXNzYWdpbmdTdGF0ZSA9IERpcmVjdE1lc3NhZ2luZ1N0YXRlUmVkaXNcbiAgICB0aGlzLmxvY2tUVEwgPSB0aGlzLm9wdGlvbnMubG9ja1RUTCB8fCAxMDAwMFxuICAgIHRoaXMuaW5zdGFuY2VVSUQgPSB0aGlzLnNlcnZlci5pbnN0YW5jZVVJRFxuICAgIHRoaXMuc2VydmVyLnJlZGlzID0gdGhpcy5yZWRpc1xuICAgIGZvciAobGV0IFtjbWQsIGRlZl0gb2YgXy50b1BhaXJzKGx1YUNvbW1hbmRzKSkge1xuICAgICAgdGhpcy5yZWRpcy5kZWZpbmVDb21tYW5kKGNtZCwge1xuICAgICAgICBudW1iZXJPZktleXM6IGRlZi5udW1iZXJPZktleXMsXG4gICAgICAgIGx1YTogZGVmLmx1YVxuICAgICAgfSlcbiAgICB9XG4gIH1cblxuICBtYWtlS2V5TmFtZSAocHJlZml4LCBuYW1lLCBrZXlOYW1lKSB7XG4gICAgcmV0dXJuIGAke25hbWVzcGFjZX06JHtwcmVmaXh9Onske25hbWV9fToke2tleU5hbWV9YFxuICB9XG5cbiAgaGFzUm9vbSAobmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdyb29tcycsIG5hbWUsICdpc0luaXQnKSlcbiAgfVxuXG4gIGhhc1VzZXIgKG5hbWUpIHtcbiAgICByZXR1cm4gdGhpcy5yZWRpcy5nZXQodGhpcy5tYWtlS2V5TmFtZSgndXNlcnMnLCBuYW1lLCAnaXNJbml0JykpXG4gIH1cblxuICBjbG9zZSAoKSB7XG4gICAgdGhpcy5jbG9zZWQgPSB0cnVlXG4gICAgcmV0dXJuIHRoaXMucmVkaXMucXVpdCgpLnJldHVybigpXG4gIH1cblxuICBnZXRSb29tIChuYW1lLCBpc1ByZWRpY2F0ZSA9IGZhbHNlKSB7XG4gICAgbGV0IHJvb20gPSBuZXcgUm9vbSh0aGlzLnNlcnZlciwgbmFtZSlcbiAgICByZXR1cm4gdGhpcy5oYXNSb29tKG5hbWUpLnRoZW4oZXhpc3RzID0+IHtcbiAgICAgIGlmICghZXhpc3RzKSB7XG4gICAgICAgIGlmIChpc1ByZWRpY2F0ZSkge1xuICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUobnVsbClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsZXQgZXJyb3IgPSBuZXcgQ2hhdFNlcnZpY2VFcnJvcignbm9Sb29tJywgbmFtZSlcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyb3IpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUocm9vbSlcbiAgICB9KVxuICB9XG5cbiAgYWRkUm9vbSAobmFtZSwgc3RhdGUpIHtcbiAgICBsZXQgcm9vbSA9IG5ldyBSb29tKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiByb29tLmluaXRTdGF0ZShzdGF0ZSkucmV0dXJuKHJvb20pXG4gIH1cblxuICByZW1vdmVSb29tIChuYW1lKSB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gIH1cblxuICBhZGRTb2NrZXQgKGlkLCB1c2VyTmFtZSkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmhzZXQoXG4gICAgICB0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB0aGlzLmluc3RhbmNlVUlELCAnc29ja2V0cycpLCBpZCwgdXNlck5hbWUpXG4gIH1cblxuICByZW1vdmVTb2NrZXQgKGlkKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGRlbChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHRoaXMuaW5zdGFuY2VVSUQsICdzb2NrZXRzJyksIGlkKVxuICB9XG5cbiAgZ2V0SW5zdGFuY2VTb2NrZXRzICh1aWQgPSB0aGlzLmluc3RhbmNlVUlEKSB7XG4gICAgcmV0dXJuIHRoaXMucmVkaXMuaGdldGFsbCh0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB1aWQsICdzb2NrZXRzJykpXG4gIH1cblxuICB1cGRhdGVIZWFydGJlYXQgKCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLnNldChcbiAgICAgIHRoaXMubWFrZUtleU5hbWUoJ2luc3RhbmNlcycsIHRoaXMuaW5zdGFuY2VVSUQsICdoZWFydGJlYXQnKSwgXy5ub3coKSlcbiAgICAgIC5jYXRjaFJldHVybigpXG4gIH1cblxuICBnZXRJbnN0YW5jZUhlYXJ0YmVhdCAodWlkID0gdGhpcy5pbnN0YW5jZVVJRCkge1xuICAgIHJldHVybiB0aGlzLnJlZGlzLmdldCh0aGlzLm1ha2VLZXlOYW1lKCdpbnN0YW5jZXMnLCB1aWQsICdoZWFydGJlYXQnKSlcbiAgICAgIC50aGVuKHRzID0+IHRzID8gcGFyc2VJbnQodHMpIDogbnVsbClcbiAgfVxuXG4gIGdldE9yQWRkVXNlciAobmFtZSwgc3RhdGUpIHtcbiAgICBsZXQgdXNlciA9IG5ldyBVc2VyKHRoaXMuc2VydmVyLCBuYW1lKVxuICAgIHJldHVybiB0aGlzLmhhc1VzZXIobmFtZSkudGhlbihleGlzdHMgPT4ge1xuICAgICAgaWYgKCFleGlzdHMpIHtcbiAgICAgICAgcmV0dXJuIHVzZXIuaW5pdFN0YXRlKHN0YXRlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICB9XG4gICAgfSkuY2F0Y2goQ2hhdFNlcnZpY2VFcnJvciwgZSA9PiB1c2VyKVxuICAgICAgLnJldHVybih1c2VyKVxuICB9XG5cbiAgZ2V0VXNlciAobmFtZSwgaXNQcmVkaWNhdGUgPSBmYWxzZSkge1xuICAgIGxldCB1c2VyID0gbmV3IFVzZXIodGhpcy5zZXJ2ZXIsIG5hbWUpXG4gICAgcmV0dXJuIHRoaXMuaGFzVXNlcihuYW1lKS50aGVuKGV4aXN0cyA9PiB7XG4gICAgICBpZiAoIWV4aXN0cykge1xuICAgICAgICBpZiAoaXNQcmVkaWNhdGUpIHtcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKG51bGwpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbGV0IGVycm9yID0gbmV3IENoYXRTZXJ2aWNlRXJyb3IoJ25vVXNlcicsIG5hbWUpXG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGVycm9yKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHVzZXIpXG4gICAgfSlcbiAgfVxuXG4gIGFkZFVzZXIgKG5hbWUsIHN0YXRlKSB7XG4gICAgbGV0IHVzZXIgPSBuZXcgVXNlcih0aGlzLnNlcnZlciwgbmFtZSlcbiAgICByZXR1cm4gdXNlci5pbml0U3RhdGUoc3RhdGUpLnJldHVybih1c2VyKVxuICB9XG5cbiAgcmVtb3ZlVXNlciAobmFtZSkge1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKVxuICB9XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBSZWRpc1N0YXRlXG4iXX0=
\No newline at end of file