@slate-sheikah/backend
Version:
slate-sheikah backend: Backend components for slate-sheikah. Slate + Automerge + Sockets
536 lines (435 loc) • 48.2 kB
JavaScript
;
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _socket = _interopRequireDefault(require("socket.io"));
var Automerge = _interopRequireWildcard(require("automerge"));
var _debounce = _interopRequireDefault(require("lodash/debounce"));
var _bridge = require("@slate-sheikah/bridge");
var _AutomergeBackend = _interopRequireDefault(require("./AutomergeBackend"));
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var SocketIOCollaboration = /*#__PURE__*/function () {
/**
* Constructor
*/
function SocketIOCollaboration(options) {
var _this = this;
(0, _classCallCheck2["default"])(this, SocketIOCollaboration);
(0, _defineProperty2["default"])(this, "io", void 0);
(0, _defineProperty2["default"])(this, "options", void 0);
(0, _defineProperty2["default"])(this, "backends", []);
(0, _defineProperty2["default"])(this, "backendCounts", []);
(0, _defineProperty2["default"])(this, "configure", function () {
return _this.io.of(_this.nspMiddleware).use(_this.authMiddleware).on('connect', _this.onConnect);
});
(0, _defineProperty2["default"])(this, "nspMiddleware", /*#__PURE__*/function () {
var _ref = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(path, query, next) {
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
return _context.abrupt("return", next(null, true));
case 1:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return function (_x, _x2, _x3) {
return _ref.apply(this, arguments);
};
}());
(0, _defineProperty2["default"])(this, "init", /*#__PURE__*/function () {
var _ref2 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(socket) {
var path, _query, onDocumentLoad;
return _regenerator["default"].wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
path = socket.nsp.name;
try {
_query = socket.handshake.query;
onDocumentLoad = _this.options.onDocumentLoad; //make some backends if this is the first time this meeting is loaded.
if (!_this.backends[path]) {
_this.backends[path] = {
id: Date.now(),
automerge: new _AutomergeBackend["default"](),
cleanupTimer: Math.floor(Date.now() / 1000) + (_this.options.cleanThreshold || 30) * 60,
loadDocument: (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2() {
var automerge, doc;
return _regenerator["default"].wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
automerge = new _AutomergeBackend["default"]();
if (!onDocumentLoad) {
_context2.next = 7;
break;
}
_context2.next = 4;
return onDocumentLoad(path, _query);
case 4:
_context2.t0 = _context2.sent;
_context2.next = 8;
break;
case 7:
_context2.t0 = _this.options.defaultValue;
case 8:
doc = _context2.t0;
if (doc) {
automerge.appendDocument(path, doc);
_this.backends[path].automerge = automerge;
}
case 10:
case "end":
return _context2.stop();
}
}
}, _callee2);
}))()
};
_this.backendCounts[path] = 0;
_this.backends[path].presenceData = {};
}
} catch (e) {
console.log('Error in slate-collab init', e);
} //return a promise for creating the automergebackend so we can await on that being done
return _context3.abrupt("return", _this.backends[path].loadDocument);
case 3:
case "end":
return _context3.stop();
}
}
}, _callee3);
}));
return function (_x4) {
return _ref2.apply(this, arguments);
};
}());
(0, _defineProperty2["default"])(this, "authMiddleware", /*#__PURE__*/function () {
var _ref4 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(socket, next) {
var query, onAuthRequest, permit;
return _regenerator["default"].wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
query = socket.handshake.query;
onAuthRequest = _this.options.onAuthRequest;
if (!onAuthRequest) {
_context4.next = 8;
break;
}
_context4.next = 5;
return onAuthRequest(query, socket);
case 5:
permit = _context4.sent;
if (permit) {
_context4.next = 8;
break;
}
return _context4.abrupt("return", next(new Error("Authentication error: ".concat(socket.id))));
case 8:
return _context4.abrupt("return", next());
case 9:
case "end":
return _context4.stop();
}
}
}, _callee4);
}));
return function (_x5, _x6) {
return _ref4.apply(this, arguments);
};
}());
(0, _defineProperty2["default"])(this, "onConnect", /*#__PURE__*/function () {
var _ref5 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5(socket) {
var name, onSocketConnection, id, conn, presenceData, _doc;
return _regenerator["default"].wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
_context5.prev = 0;
name = socket.nsp.name;
onSocketConnection = _this.options.onSocketConnection;
id = socket.id, conn = socket.conn; //try to pull presence data out of the payload.
presenceData = {};
try {
presenceData = JSON.parse(socket.handshake.query.presenceData);
} catch (e) {}
_context5.next = 8;
return _this.init(socket);
case 8:
_this.backendCounts[name] = _this.backendCounts[name] + 1;
_this.backends[name].presenceData[id] = presenceData;
_this.backends[name].automerge.createConnection(id, function (_ref6) {
var type = _ref6.type,
payload = _ref6.payload;
socket.compress(false).emit('msg', {
type: type,
payload: _objectSpread({
id: conn.id
}, payload)
});
});
socket.on('msg', _this.onMessage(id, name));
socket.on('flush', _this.onFlush(name));
socket.on('disconnect', _this.onDisconnect(id, socket));
_doc = _this.backends[name].automerge.getDocument(name); //send document
socket.compress(true).emit('msg', {
type: 'document',
id: _this.backends[name].id,
payload: Automerge.save(_doc)
}); //send presence information to namespace
_this.io.of(name).compress(false).emit('msg', {
type: 'participant',
payload: Object.values(_this.backends[name].presenceData)
});
_this.backends[name].automerge.openConnection(id);
_this.garbageCursors(name);
_context5.t0 = onSocketConnection;
if (!_context5.t0) {
_context5.next = 23;
break;
}
_context5.next = 23;
return onSocketConnection({
docId: name,
socket: socket,
_this: _this
});
case 23:
_context5.next = 28;
break;
case 25:
_context5.prev = 25;
_context5.t1 = _context5["catch"](0);
console.log('Error in slate-collab onConnect', _context5.t1);
case 28:
case "end":
return _context5.stop();
}
}
}, _callee5, null, [[0, 25]]);
}));
return function (_x7) {
return _ref5.apply(this, arguments);
};
}());
(0, _defineProperty2["default"])(this, "onMessage", function (id, name) {
return function (data) {
switch (data.type) {
case 'operation':
try {
_this.backends[name].automerge.receiveOperation(id, data);
_this.autoSaveDoc(name);
_this.garbageCursors(name);
} catch (e) {
console.log('Error in OnMessage/operation', e);
}
}
};
});
(0, _defineProperty2["default"])(this, "onFlush", function (name) {
return function (data) {
try {
console.log("Flushing document ".concat(name, " to the database."));
_this.autoSaveDoc(name);
} catch (e) {
console.log("Error flushing document ".concat(name, " to database"), e);
}
};
});
(0, _defineProperty2["default"])(this, "autoSaveDoc", function (name) {//noop to be overwritten by the constructor.
});
(0, _defineProperty2["default"])(this, "saveDocument", /*#__PURE__*/function () {
var _ref7 = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(docId) {
var onDocumentSave, _doc2;
return _regenerator["default"].wrap(function _callee6$(_context6) {
while (1) {
switch (_context6.prev = _context6.next) {
case 0:
_context6.prev = 0;
onDocumentSave = _this.options.onDocumentSave; //if the backend has already been cleaned up, stop trying to do this.
if (_this.backends[docId]) {
_context6.next = 4;
break;
}
return _context6.abrupt("return");
case 4:
_doc2 = _this.backends[docId].automerge.getDocument(docId);
if (_doc2) {
_context6.next = 7;
break;
}
throw new Error("Can't receive document by id: ".concat(docId));
case 7:
_context6.t0 = onDocumentSave;
if (!_context6.t0) {
_context6.next = 11;
break;
}
_context6.next = 11;
return onDocumentSave(docId, (0, _bridge.toJS)(_doc2.children));
case 11:
_context6.next = 16;
break;
case 13:
_context6.prev = 13;
_context6.t1 = _context6["catch"](0);
console.error('Error in saveDocument.', _context6.t1, docId);
case 16:
case "end":
return _context6.stop();
}
}
}, _callee6, null, [[0, 13]]);
}));
return function (_x8) {
return _ref7.apply(this, arguments);
};
}());
(0, _defineProperty2["default"])(this, "onDisconnect", function (id, socket) {
return /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7() {
var onSocketDisconnection, name;
return _regenerator["default"].wrap(function _callee7$(_context7) {
while (1) {
switch (_context7.prev = _context7.next) {
case 0:
_context7.prev = 0;
onSocketDisconnection = _this.options.onSocketDisconnection;
name = socket.nsp.name; //increment the cleanup timer
_this.backends[name].cleanupTimer = Math.floor(Date.now() / 1000) + (_this.options.cleanThreshold || 30) * 60;
/**
* wrap the automerge closeConnection call in a timeout to give any outstanding messages
* time to flush to the database
*/
setTimeout(function () {
_this.backends[name].automerge.closeConnection(id);
}, 5000);
_this.backendCounts[name] = _this.backendCounts[name] - 1;
delete _this.backends[name].presenceData[socket.id]; //send presence information to namespace
_this.io.of(name).compress(false).emit('msg', {
type: 'participant',
payload: Object.values(_this.backends[name].presenceData)
});
_context7.next = 10;
return _this.saveDocument(name);
case 10:
_this.garbageCursors(name);
_context7.t0 = onSocketDisconnection;
if (!_context7.t0) {
_context7.next = 15;
break;
}
_context7.next = 15;
return onSocketDisconnection({
docId: name,
socket: socket,
_this: _this
});
case 15:
_context7.next = 20;
break;
case 17:
_context7.prev = 17;
_context7.t1 = _context7["catch"](0);
console.log('Error in slate-collab onDisconnect', _context7.t1);
case 20:
case "end":
return _context7.stop();
}
}
}, _callee7, null, [[0, 17]]);
}));
});
(0, _defineProperty2["default"])(this, "garbageCursors", function (nsp) {
try {
var _Object$keys;
var _doc3 = _this.backends[nsp].automerge.getDocument(nsp);
if (!_doc3.cursors) return;
var namespace = _this.io.of(nsp);
(_Object$keys = Object.keys(_doc3 === null || _doc3 === void 0 ? void 0 : _doc3.cursors)) === null || _Object$keys === void 0 ? void 0 : _Object$keys.forEach(function (key) {
if (!namespace.sockets[key]) {
_this.backends[nsp].automerge.garbageCursor(nsp, key);
}
});
} catch (e) {//don't necessarily care if this fails.
}
});
(0, _defineProperty2["default"])(this, "destroy", /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8() {
return _regenerator["default"].wrap(function _callee8$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
_this.io.close();
case 1:
case "end":
return _context8.stop();
}
}
}, _callee8);
})));
this.io = (0, _socket["default"])(options.entry, _objectSpread(_objectSpread({}, options.connectOpts), {}, {
perMessageDeflate: true
}));
this.options = options;
this.configure();
this.autoSaveDoc = (0, _debounce["default"])(this.saveDocument, options.saveFrequency || 2000, {
maxWait: options.saveFrequency || 2000
});
this.backends = [];
this.backendCounts = []; //spawn cleaner
setInterval(function () {
_this.cleaner();
}, options.cleanFrequency || 60000);
return this;
}
/**
* Initial IO configuration
*/
(0, _createClass2["default"])(SocketIOCollaboration, [{
key: "cleaner",
/**
* memory cleaner process that checks the backeds to see if there aren't connections and if the timer has expired.
*/
value: function cleaner() {
var _this2 = this;
console.log('Cleaner running');
var targets = [];
try {
Object.keys(this.backends).forEach(function (key) {
if (_this2.backendCounts[key] === 0 && _this2.backends[key].cleanupTimer < Math.floor(Date.now() / 1000)) {
targets.push(key);
}
});
console.log("Found ".concat(targets.length, " documents to clean."));
if (targets.length) {
//free up that precious, precious memory.
targets.forEach(function (key) {
delete _this2.backends[key];
delete _this2.io.nsps[key];
delete _this2.backendCounts[key];
});
}
} catch (e) {
console.log('Error freeing memory', e);
}
}
/**
* SocketIO auth middleware. Used for user authentification.
*/
}]);
return SocketIOCollaboration;
}();
exports["default"] = SocketIOCollaboration;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9Tb2NrZXRJT0Nvbm5lY3Rpb24udHMiXSwibmFtZXMiOlsiU29ja2V0SU9Db2xsYWJvcmF0aW9uIiwib3B0aW9ucyIsImlvIiwib2YiLCJuc3BNaWRkbGV3YXJlIiwidXNlIiwiYXV0aE1pZGRsZXdhcmUiLCJvbiIsIm9uQ29ubmVjdCIsInBhdGgiLCJxdWVyeSIsIm5leHQiLCJzb2NrZXQiLCJuc3AiLCJuYW1lIiwiaGFuZHNoYWtlIiwib25Eb2N1bWVudExvYWQiLCJiYWNrZW5kcyIsImlkIiwiRGF0ZSIsIm5vdyIsImF1dG9tZXJnZSIsIkF1dG9tZXJnZUJhY2tlbmQiLCJjbGVhbnVwVGltZXIiLCJNYXRoIiwiZmxvb3IiLCJjbGVhblRocmVzaG9sZCIsImxvYWREb2N1bWVudCIsImRlZmF1bHRWYWx1ZSIsImRvYyIsImFwcGVuZERvY3VtZW50IiwiYmFja2VuZENvdW50cyIsInByZXNlbmNlRGF0YSIsImUiLCJjb25zb2xlIiwibG9nIiwib25BdXRoUmVxdWVzdCIsInBlcm1pdCIsIkVycm9yIiwib25Tb2NrZXRDb25uZWN0aW9uIiwiY29ubiIsIkpTT04iLCJwYXJzZSIsImluaXQiLCJjcmVhdGVDb25uZWN0aW9uIiwidHlwZSIsInBheWxvYWQiLCJjb21wcmVzcyIsImVtaXQiLCJvbk1lc3NhZ2UiLCJvbkZsdXNoIiwib25EaXNjb25uZWN0IiwiZ2V0RG9jdW1lbnQiLCJBdXRvbWVyZ2UiLCJzYXZlIiwiT2JqZWN0IiwidmFsdWVzIiwib3BlbkNvbm5lY3Rpb24iLCJnYXJiYWdlQ3Vyc29ycyIsImRvY0lkIiwiX3RoaXMiLCJkYXRhIiwicmVjZWl2ZU9wZXJhdGlvbiIsImF1dG9TYXZlRG9jIiwib25Eb2N1bWVudFNhdmUiLCJjaGlsZHJlbiIsImVycm9yIiwib25Tb2NrZXREaXNjb25uZWN0aW9uIiwic2V0VGltZW91dCIsImNsb3NlQ29ubmVjdGlvbiIsInNhdmVEb2N1bWVudCIsImN1cnNvcnMiLCJuYW1lc3BhY2UiLCJrZXlzIiwiZm9yRWFjaCIsImtleSIsInNvY2tldHMiLCJnYXJiYWdlQ3Vyc29yIiwiY2xvc2UiLCJlbnRyeSIsImNvbm5lY3RPcHRzIiwicGVyTWVzc2FnZURlZmxhdGUiLCJjb25maWd1cmUiLCJzYXZlRnJlcXVlbmN5IiwibWF4V2FpdCIsInNldEludGVydmFsIiwiY2xlYW5lciIsImNsZWFuRnJlcXVlbmN5IiwidGFyZ2V0cyIsInB1c2giLCJsZW5ndGgiLCJuc3BzIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFJQTs7QUFFQTs7QUFJQTs7Ozs7O0lBMkNxQkEscUI7QUFNbkI7OztBQUlBLGlDQUFZQyxPQUFaLEVBQW1EO0FBQUE7O0FBQUE7QUFBQTtBQUFBO0FBQUEsdURBUHBCLEVBT29CO0FBQUEsNERBTlYsRUFNVTtBQUFBLHdEQWlDL0I7QUFBQSxhQUNsQixLQUFJLENBQUNDLEVBQUwsQ0FDR0MsRUFESCxDQUNNLEtBQUksQ0FBQ0MsYUFEWCxFQUVHQyxHQUZILENBRU8sS0FBSSxDQUFDQyxjQUZaLEVBR0dDLEVBSEgsQ0FHTSxTQUhOLEVBR2lCLEtBQUksQ0FBQ0MsU0FIdEIsQ0FEa0I7QUFBQSxLQWpDK0I7QUFBQTtBQUFBLCtGQTJDM0IsaUJBQU9DLElBQVAsRUFBcUJDLEtBQXJCLEVBQWlDQyxJQUFqQztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsaURBQ2ZBLElBQUksQ0FBQyxJQUFELEVBQU8sSUFBUCxDQURXOztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLE9BM0MyQjs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsZ0dBc0RwQyxrQkFBT0MsTUFBUDtBQUFBOztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ1BILGdCQUFBQSxJQURPLEdBQ0FHLE1BQU0sQ0FBQ0MsR0FBUCxDQUFXQyxJQURYOztBQUViLG9CQUFJO0FBQ0lKLGtCQUFBQSxNQURKLEdBQ1lFLE1BQU0sQ0FBQ0csU0FBUCxDQUFpQkwsS0FEN0I7QUFFTU0sa0JBQUFB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RyxFQUFJO0FBQ3hDLGNBQ0UsTUFBSSxDQUFDNUMsYUFBTCxDQUFtQjRDLEdBQW5CLE1BQTRCLENBQTVCLElBQ0EsTUFBSSxDQUFDMUQsUUFBTCxDQUFjMEQsR0FBZCxFQUFtQnBELFlBQW5CLEdBQWtDQyxJQUFJLENBQUNDLEtBQUwsQ0FBV04sSUFBSSxDQUFDQyxHQUFMLEtBQWEsSUFBeEIsQ0FGcEMsRUFHRTtBQUNBb0UsWUFBQUEsT0FBTyxDQUFDQyxJQUFSLENBQWFkLEdBQWI7QUFDRDtBQUNGLFNBUEQ7QUFTQXpDLFFBQUFBLE9BQU8sQ0FBQ0MsR0FBUixpQkFBcUJxRCxPQUFPLENBQUNFLE1BQTdCOztBQUNBLFlBQUlGLE9BQU8sQ0FBQ0UsTUFBWixFQUFvQjtBQUNsQjtBQUNBRixVQUFBQSxPQUFPLENBQUNkLE9BQVIsQ0FBZ0IsVUFBQUMsR0FBRyxFQUFJO0FBQ3JCLG1CQUFPLE1BQUksQ0FBQzFELFFBQUwsQ0FBYzBELEdBQWQsQ0FBUDtBQUNBLG1CQUFPLE1BQUksQ0FBQ3pFLEVBQUwsQ0FBUXlGLElBQVIsQ0FBYWhCLEdBQWIsQ0FBUDtBQUNBLG1CQUFPLE1BQUksQ0FBQzVDLGFBQUwsQ0FBbUI0QyxHQUFuQixDQUFQO0FBQ0QsV0FKRDtBQUtEO0FBQ0YsT0FuQkQsQ0FtQkUsT0FBTzFDLENBQVAsRUFBVTtBQUNWQyxRQUFBQSxPQUFPLENBQUNDLEdBQVIsQ0FBWSxzQkFBWixFQUFvQ0YsQ0FBcEM7QUFDRDtBQUNGO0FBRUQiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgaW8gZnJvbSAnc29ja2V0LmlvJ1xuaW1wb3J0ICogYXMgQXV0b21lcmdlIGZyb20gJ2F1dG9tZXJnZSdcbmltcG9ydCB7IE5vZGUgfSBmcm9tICdzbGF0ZSdcbmltcG9ydCB7IFNlcnZlciB9IGZyb20gJ2h0dHAnXG5cbmltcG9ydCBkZWJvdW5jZSBmcm9tICdsb2Rhc2gvZGVib3VuY2UnXG5cbmltcG9ydCB7IFN5bmNEb2MsIENvbGxhYkFjdGlvbiwgdG9KUyB9IGZyb20gJ0BzbGF0ZS1zaGVpa2FoL2JyaWRnZSdcblxuaW1wb3J0IHsgZ2V0Q2xpZW50cyB9IGZyb20gJy4vdXRpbHMnXG5cbmltcG9ydCBBdXRvbWVyZ2VCYWNrZW5kIGZyb20gJy4vQXV0b21lcmdlQmFja2VuZCdcbmltcG9ydCB7IFNvY2tldElPQ29ubmVjdGlvbiB9IGZyb20gJ2luZGV4J1xuXG5leHBvcnQgaW50ZXJmYWNlIFNvY2tldElPQ29sbGFib3JhdGlvbk9wdGlvbnMge1xuICBlbnRyeTogU2VydmVyXG4gIGNvbm5lY3RPcHRzPzogU29ja2V0SU8uU2VydmVyT3B0aW9uc1xuICBkZWZhdWx0VmFsdWU/OiBOb2RlW11cbiAgc2F2ZUZyZXF1ZW5jeT86IG51bWJlclxuICBjbGVhbkZyZXF1ZW5jeT86IG51bWJlclxuICBjbGVhblRocmVzaG9sZD86IG51bWJlclxuICBvbkF1dGhSZXF1ZXN0PzogKFxuICAgIHF1ZXJ5OiBPYmplY3QsXG4gICAgc29ja2V0PzogU29ja2V0SU8uU29ja2V0XG4gICkgPT4gUHJvbWlzZTxib29sZWFuPiB8IGJvb2xlYW5cbiAgb25Eb2N1bWVudExvYWQ/OiAoXG4gICAgcGF0aG5hbWU6IHN0cmluZyxcbiAgICBxdWVyeT86IE9iamVjdFxuICApID0+IFByb21pc2U8Tm9kZVtdPiB8IE5vZGVbXVxuICBvbkRvY3VtZW50U2F2ZT86IChwYXRobmFtZTogc3RyaW5nLCBkb2M6IE5vZGVbXSkgPT4gUHJvbWlzZTx2b2lkPiB8IHZvaWRcbiAgb25Tb2NrZXRDb25uZWN0aW9uPzogKFxuICAgIG1ldGFkYXRhOiBDb25uZWN0aW9uQ2FsbGJhY2tNZXRhXG4gICkgPT4gUHJvbWlzZTx2b2lkPiB8IHZvaWRcbiAgb25Tb2NrZXREaXNjb25uZWN0aW9uPzogKFxuICAgIG1ldGFkYXRhOiBDb25uZWN0aW9uQ2FsbGJhY2tNZXRhXG4gICkgPT4gUHJvbWlzZTx2b2lkPiB8IHZvaWRcbn1cbmV4cG9ydCBpbnRlcmZhY2UgQmFja2VuZENvdW50cyB7XG4gIFtrZXk6IHN0cmluZ106IG51bWJlclxufVxuXG5leHBvcnQgaW50ZXJmYWNlIENvbm5lY3Rpb25DYWxsYmFja01ldGEge1xuICBkb2NJZDogc3RyaW5nXG4gIHNvY2tldDogU29ja2V0SU8uU29ja2V0XG4gIF90aGlzOiBTb2NrZXRJT0Nvbm5lY3Rpb25cbn1cblxuZXhwb3J0IGludGVyZmFjZSBCYWNrZW5kcyB7XG4gIGF1dG9tZXJnZTogQXV0b21lcmdlQmFja2VuZFxuICByZWFkeTogYm9vbGVhblxuICBmYWlsZWQ6IGJvb2xlYW5cbiAgY2xlYW51cFRpbWVyOiBudW1iZXJcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU29ja2V0SU9Db2xsYWJvcmF0aW9uIHtcbiAgcHJpdmF0ZSBpbzogU29ja2V0SU8uU2VydmVyXG4gIHByaXZhdGUgb3B0aW9uczogU29ja2V0SU9Db2xsYWJvcmF0aW9uT3B0aW9uc1xuICBwcml2YXRlIGJhY2tlbmRzOiBCYWNrZW5kc1tdID0gW11cbiAgcHJpdmF0ZSBiYWNrZW5kQ291bnRzOiBCYWNrZW5kQ291bnRzW10gPSBbXVxuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RvclxuICAgKi9cblxuICBjb25zdHJ1Y3RvcihvcHRpb25zOiBTb2NrZXRJT0NvbGxhYm9yYXRpb25PcHRpb25zKSB7XG4gICAgdGhpcy5pbyA9IGlvKG9wdGlvbnMuZW50cnksIHtcbiAgICAgIC4uLm9wdGlvbnMuY29ubmVjdE9wdHMsXG4gICAgICBwZXJNZXNzYWdlRGVmbGF0ZTogdHJ1ZVxuICAgIH0pXG5cbiAgICB0aGlzLm9wdGlvbnMgPSBvcHRpb25zXG5cbiAgICB0aGlzLmNvbmZpZ3VyZSgpXG5cbiAgICB0aGlzLmF1dG9TYXZlRG9jID0gZGVib3VuY2UoXG4gICAgICB0aGlzLnNhdmVEb2N1bWVudCxcbiAgICAgIG9wdGlvbnMuc2F2ZUZyZXF1ZW5jeSB8fCAyMDAwLFxuICAgICAge1xuICAgICAgICBtYXhXYWl0OiBvcHRpb25zLnNhdmVGcmVxdWVuY3kgfHwgMjAwMFxuICAgICAgfVxuICAgIClcblxuICAgIHRoaXMuYmFja2VuZHMgPSBbXVxuICAgIHRoaXMuYmFja2VuZENvdW50cyA9IFtdXG5cbiAgICAvL3NwYXduIGNsZWFuZXJcbiAgICBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICB0aGlzLmNsZWFuZXIoKVxuICAgIH0sIG9wdGlvbnMuY2xlYW5GcmVxdWVuY3kgfHwgNjAwMDApXG5cbiAgICByZXR1cm4gdGhpc1xuICB9XG5cbiAgLyoqXG4gICAqIEluaXRpYWwgSU8gY29uZmlndXJhdGlvblxuICAgKi9cblxuICBwcml2YXRlIGNvbmZpZ3VyZSA9ICgpID0+XG4gICAgdGhpcy5pb1xuICAgICAgLm9mKHRoaXMubnNwTWlkZGxld2FyZSlcbiAgICAgIC51c2UodGhpcy5hdXRoTWlkZGxld2FyZSlcbiAgICAgIC5vbignY29ubmVjdCcsIHRoaXMub25Db25uZWN0KVxuXG4gIC8qKlxuICAgKiBOYW1lc3BhY2UgU29ja2V0SU8gbWlkZGxld2FyZS4gTG9hZCBkb2N1bWVudCB2YWx1ZSBhbmQgYXBwZW5kIGl0IHRvIENvbGxhYm9yYXRpb25CYWNrZW5kLlxuICAgKi9cblxuICBwcml2YXRlIG5zcE1pZGRsZXdhcmUgPSBhc3luYyAocGF0aDogc3RyaW5nLCBxdWVyeTogYW55LCBuZXh0OiBhbnkpID0+IHtcbiAgICByZXR1cm4gbmV4dChudWxsLCB0cnVlKVxuICAgIC8vdGhpcyBpcyBuZWVkZWQgdG8gc2V0IHVwIHRoZSBuYW1lc3BhY2UsIGJ1dCBpdCBvbmx5IHJ1bnMgb25jZS5cbiAgICAvL3RoZSBsb2dpYyB0aGF0IFdBUyBpbiBoZXJlIG5lZWRzIHRvIGJlIGFibGUgdG8gYmUgcmFuIG11bHRpcGxlIHRpbWVzLlxuICB9XG5cbiAgLyoqXG4gICAqIGluaXQgZnVuY3Rpb24gdG8gc2V0IHVwIG5ldyBkb2N1bWVudHMgaXMgdGhleSBkb24ndCBleGlzdC4gIFRoZXNlIGdldCBjbGVhbmVkIHVwIG9uY2VcbiAgICogYWxsIHRoZSBzb2NrZXRzIGRpc2Nvbm5lY3QuXG4gICAqIEBwYXJhbSBzb2NrZXRcbiAgICovXG4gIHByaXZhdGUgaW5pdCA9IGFzeW5jIChzb2NrZXQ6IFNvY2tldElPLlNvY2tldCkgPT4ge1xuICAgIGNvbnN0IHBhdGggPSBzb2NrZXQubnNwLm5hbWVcbiAgICB0cnkge1xuICAgICAgY29uc3QgcXVlcnkgPSBzb2NrZXQuaGFuZHNoYWtlLnF1ZXJ5XG4gICAgICBjb25zdCB7IG9uRG9jdW1lbnRMb2FkIH0gPSB0aGlzLm9wdGlvbnNcblxuICAgICAgLy9tYWtlIHNvbWUgYmFja2VuZHMgaWYgdGhpcyBpcyB0aGUgZmlyc3QgdGltZSB0aGlzIG1lZXRpbmcgaXMgbG9hZGVkLlxuICAgICAgaWYgKCF0aGlzLmJhY2tlbmRzW3BhdGhdKSB7XG4gICAgICAgIHRoaXMuYmFja2VuZHNbcGF0aF0gPSB7XG4gICAgICAgICAgaWQ6IERhdGUubm93KCksXG4gICAgICAgICAgYXV0b21lcmdlOiBuZXcgQXV0b21lcmdlQmFja2VuZCgpLFxuICAgICAgICAgIGNsZWFudXBUaW1lcjpcbiAgICAgICAgICAgIE1hdGguZmxvb3IoRGF0ZS5ub3coKSAvIDEwMDApICtcbiAgICAgICAgICAgICh0aGlzLm9wdGlvbnMuY2xlYW5UaHJlc2hvbGQgfHwgMzApICogNjAsXG4gICAgICAgICAgbG9hZERvY3VtZW50OiAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgYXV0b21lcmdlID0gbmV3IEF1dG9tZXJnZUJhY2tlbmQoKVxuXG4gICAgICAgICAgICBjb25zdCBkb2MgPSBvbkRvY3VtZW50TG9hZFxuICAgICAgICAgICAgICA/IGF3YWl0IG9uRG9jdW1lbnRMb2FkKHBhdGgsIHF1ZXJ5KVxuICAgICAgICAgICAgICA6IHRoaXMub3B0aW9ucy5kZWZhdWx0VmFsdWVcblxuICAgICAgICAgICAgaWYgKGRvYykge1xuICAgICAgICAgICAgICBhdXRvbWVyZ2UuYXBwZW5kRG9jdW1lbnQocGF0aCwgZG9jKVxuICAgICAgICAgICAgICB0aGlzLmJhY2tlbmRzW3BhdGhdLmF1dG9tZXJnZSA9IGF1dG9tZXJnZVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pKClcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmJhY2tlbmRDb3VudHNbcGF0aF0gPSAwXG4gICAgICAgIHRoaXMuYmFja2VuZHNbcGF0aF0ucHJlc2VuY2VEYXRhID0ge31cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLmxvZygnRXJyb3IgaW4gc2xhdGUtY29sbGFiIGluaXQnLCBlKVxuICAgIH1cblxuICAgIC8vcmV0dXJuIGEgcHJvbWlzZSBmb3IgY3JlYXRpbmcgdGhlIGF1dG9tZXJnZWJhY2tlbmQgc28gd2UgY2FuIGF3YWl0IG9uIHRoYXQgYmVpbmcgZG9uZVxuICAgIHJldHVybiB0aGlzLmJhY2tlbmRzW3BhdGhdLmxvYWREb2N1bWVudFxuICB9XG5cbiAgLyoqXG4gICAqIG1lbW9yeSBjbGVhbmVyIHByb2Nlc3MgdGhhdCBjaGVja3MgdGhlIGJhY2tlZHMgdG8gc2VlIGlmIHRoZXJlIGFyZW4ndCBjb25uZWN0aW9ucyBhbmQgaWYgdGhlIHRpbWVyIGhhcyBleHBpcmVkLlxuICAgKi9cbiAgcHJpdmF0ZSBjbGVhbmVyKCkge1xuICAgIGNvbnNvbGUubG9nKCdDbGVhbmVyIHJ1bm5pbmcnKVxuICAgIGNvbnN0IHRhcmdldHM6IHN0cmluZ1tdID0gW11cblxuICAgIHRyeSB7XG4gICAgICBPYmplY3Qua2V5cyh0aGlzLmJhY2tlbmRzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICB0aGlzLmJhY2tlbmRDb3VudHNba2V5XSA9PT0gMCAmJlxuICAgICAgICAgIHRoaXMuYmFja2VuZHNba2V5XS5jbGVhbnVwVGltZXIgPCBNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKVxuICAgICAgICApIHtcbiAgICAgICAgICB0YXJnZXRzLnB1c2goa2V5KVxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBjb25zb2xlLmxvZyhgRm91bmQgJHt0YXJnZXRzLmxlbmd0aH0gZG9jdW1lbnRzIHRvIGNsZWFuLmApXG4gICAgICBpZiAodGFyZ2V0cy5sZW5ndGgpIHtcbiAgICAgICAgLy9mcmVlIHVwIHRoYXQgcHJlY2lvdXMsIHByZWNpb3VzIG1lbW9yeS5cbiAgICAgICAgdGFyZ2V0cy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgICAgZGVsZXRlIHRoaXMuYmFja2VuZHNba2V5XVxuICAgICAgICAgIGRlbGV0ZSB0aGlzLmlvLm5zcHNba2V5XVxuICAgICAgICAgIGRlbGV0ZSB0aGlzLmJhY2tlbmRDb3VudHNba2V5XVxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdFcnJvciBmcmVlaW5nIG1lbW9yeScsIGUpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNvY2tldElPIGF1dGggbWlkZGxld2FyZS4gVXNlZCBmb3IgdXNlciBhdXRoZW50aWZpY2F0aW9uLlxuICAgKi9cblxuICBwcml2YXRlIGF1dGhNaWRkbGV3YXJlID0gYXN5bmMgKFxuICAgIHNvY2tldDogU29ja2V0SU8uU29ja2V0LFxuICAgIG5leHQ6IChlPzogYW55KSA9PiB2b2lkXG4gICkgPT4ge1xuICAgIGNvbnN0IHsgcXVlcnkgfSA9IHNvY2tldC5oYW5kc2hha2VcbiAgICBjb25zdCB7IG9uQXV0aFJlcXVlc3QgfSA9IHRoaXMub3B0aW9uc1xuXG4gICAgaWYgKG9uQXV0aFJlcXVlc3QpIHtcbiAgICAgIGNvbnN0IHBlcm1pdCA9IGF3YWl0IG9uQXV0aFJlcXVlc3QocXVlcnksIHNvY2tldClcblxuICAgICAgaWYgKCFwZXJtaXQpIHJldHVybiBuZXh0KG5ldyBFcnJvcihgQXV0aGVudGljYXRpb24gZXJyb3I6ICR7c29ja2V0LmlkfWApKVxuICAgIH1cblxuICAgIHJldHVybiBuZXh0KClcbiAgfVxuXG4gIC8qKlxuICAgKiBPbiAnY29ubmVjdCcgaGFuZGxlci5cbiAgICovXG5cbiAgcHJpdmF0ZSBvbkNvbm5lY3QgPSBhc3luYyAoc29ja2V0OiBTb2NrZXRJTy5Tb2NrZXQpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBuYW1lIH0gPSBzb2NrZXQubnNwXG4gICAgICBjb25zdCB7IG9uU29ja2V0Q29ubmVjdGlvbiB9ID0gdGhpcy5vcHRpb25zXG4gICAgICBjb25zdCB7IGlkLCBjb25uIH0gPSBzb2NrZXRcblxuICAgICAgLy90cnkgdG8gcHVsbCBwcmVzZW5jZSBkYXRhIG91dCBvZiB0aGUgcGF5bG9hZC5cbiAgICAgIGxldCBwcmVzZW5jZURhdGEgPSB7fVxuICAgICAgdHJ5IHtcbiAgICAgICAgcHJlc2VuY2VEYXRhID0gSlNPTi5wYXJzZShzb2NrZXQuaGFuZHNoYWtlLnF1ZXJ5LnByZXNlbmNlRGF0YSlcbiAgICAgIH0gY2F0Y2ggKGUpIHt9XG5cbiAgICAgIGF3YWl0IHRoaXMuaW5pdChzb2NrZXQpXG4gICAgICB0aGlzLmJhY2tlbmRDb3VudHNbbmFtZV0gPSB0aGlzLmJhY2tlbmRDb3VudHNbbmFtZV0gKyAxXG4gICAgICB0aGlzLmJhY2tlbmRzW25hbWVdLnByZXNlbmNlRGF0YVtpZF0gPSBwcmVzZW5jZURhdGFcblxuICAgICAgdGhpcy5iYWNrZW5kc1tuYW1lXS5hdXRvbWVyZ2UuY3JlYXRlQ29ubmVjdGlvbihcbiAgICAgICAgaWQsXG4gICAgICAgICh7IHR5cGUsIHBheWxvYWQgfTogQ29sbGFiQWN0aW9uKSA9PiB7XG4gICAgICAgICAgc29ja2V0XG4gICAgICAgICAgICAuY29tcHJlc3MoZmFsc2UpXG4gICAgICAgICAgICAuZW1pdCgnbXNnJywgeyB0eXBlLCBwYXlsb2FkOiB7IGlkOiBjb25uLmlkLCAuLi5wYXlsb2FkIH0gfSlcbiAgICAgICAgfVxuICAgICAgKVxuXG4gICAgICBzb2NrZXQub24oJ21zZycsIHRoaXMub25NZXNzYWdlKGlkLCBuYW1lKSlcbiAgICAgIHNvY2tldC5vbignZmx1c2gnLCB0aGlzLm9uRmx1c2gobmFtZSkpXG5cbiAgICAgIHNvY2tldC5vbignZGlzY29ubmVjdCcsIHRoaXMub25EaXNjb25uZWN0KGlkLCBzb2NrZXQpKVxuXG4gICAgICBjb25zdCBkb2MgPSB0aGlzLmJhY2tlbmRzW25hbWVdLmF1dG9tZXJnZS5nZXREb2N1bWVudChuYW1lKVxuXG4gICAgICAvL3NlbmQgZG9jdW1lbnRcbiAgICAgIHNvY2tldC5jb21wcmVzcyh0cnVlKS5lbWl0KCdtc2cnLCB7XG4gICAgICAgIHR5cGU6ICdkb2N1bWVudCcsXG4gICAgICAgIGlkOiB0aGlzLmJhY2tlbmRzW25hbWVdLmlkLFxuICAgICAgICBwYXlsb2FkOiBBdXRvbWVyZ2Uuc2F2ZTxTeW5jRG9jPihkb2MpXG4gICAgICB9KVxuXG4gICAgICAvL3NlbmQgcHJlc2VuY2UgaW5mb3JtYXRpb24gdG8gbmFtZXNwYWNlXG4gICAgICB0aGlzLmlvXG4gICAgICAgIC5vZihuYW1lKVxuICAgICAgICAuY29tcHJlc3MoZmFsc2UpXG4gICAgICAgIC5lbWl0KCdtc2cnLCB7XG4gICAgICAgICAgdHlwZTogJ3BhcnRpY2lwYW50JyxcbiAgICAgICAgICBwYXlsb2FkOiBPYmplY3QudmFsdWVzKHRoaXMuYmFja2VuZHNbbmFtZV0ucHJlc2VuY2VEYXRhKVxuICAgICAgICB9KVxuXG4gICAgICB0aGlzLmJhY2tlbmRzW25hbWVdLmF1dG9tZXJnZS5vcGVuQ29ubmVjdGlvbihpZClcblxuICAgICAgdGhpcy5nYXJiYWdlQ3Vyc29ycyhuYW1lKVxuXG4gICAgICBvblNvY2tldENvbm5lY3Rpb24gJiZcbiAgICAgICAgKGF3YWl0IG9uU29ja2V0Q29ubmVjdGlvbih7XG4gICAgICAgICAgZG9jSWQ6IG5hbWUsXG4gICAgICAgICAgc29ja2V0LFxuICAgICAgICAgIF90aGlzOiB0aGlzXG4gICAgICAgIH0pKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdFcnJvciBpbiBzbGF0ZS1jb2xsYWIgb25Db25uZWN0JywgZSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogT24gJ21lc3NhZ2UnIGhhbmRsZXJcbiAgICovXG5cbiAgcHJpdmF0ZSBvbk1lc3NhZ2UgPSAoaWQ6IHN0cmluZywgbmFtZTogc3RyaW5nKSA9PiAoZGF0YTogYW55KSA9PiB7XG4gICAgc3dpdGNoIChkYXRhLnR5cGUpIHtcbiAgICAgIGNhc2UgJ29wZXJhdGlvbic6XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdGhpcy5iYWNrZW5kc1tuYW1lXS5hdXRvbWVyZ2UucmVjZWl2ZU9wZXJhdGlvbihpZCwgZGF0YSlcblxuICAgICAgICAgIHRoaXMuYXV0b1NhdmVEb2MobmFtZSlcblxuICAgICAgICAgIHRoaXMuZ2FyYmFnZUN1cnNvcnMobmFtZSlcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIGNvbnNvbGUubG9nKCdFcnJvciBpbiBPbk1lc3NhZ2Uvb3BlcmF0aW9uJywgZSlcbiAgICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBmb3JjZXMgdGhlIGJhY2tlbmQgdG8gc2F2ZSB0aGUgZG9jdW1lbnRcbiAgICogQHBhcmFtIG5hbWVcbiAgICovXG4gIHByaXZhdGUgb25GbHVzaCA9IChuYW1lOiBzdHJpbmcpID0+IChkYXRhOiBhbnkpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc29sZS5sb2coYEZsdXNoaW5nIGRvY3VtZW50ICR7bmFtZX0gdG8gdGhlIGRhdGFiYXNlLmApXG4gICAgICB0aGlzLmF1dG9TYXZlRG9jKG5hbWUpXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS5sb2coYEVycm9yIGZsdXNoaW5nIGRvY3VtZW50ICR7bmFtZX0gdG8gZGF0YWJhc2VgLCBlKVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgYXV0b1NhdmVEb2MgPSAobmFtZTogc3RyaW5nKSA9PiB7XG4gICAgLy9ub29wIHRvIGJlIG92ZXJ3cml0dGVuIGJ5IHRoZSBjb25zdHJ1Y3Rvci5cbiAgfVxuXG4gIC8qKlxuICAgKiBTYXZlIGRvY3VtZW50XG4gICAqL1xuXG4gIHByaXZhdGUgc2F2ZURvY3VtZW50ID0gYXN5bmMgKGRvY0lkOiBzdHJpbmcpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBvbkRvY3VtZW50U2F2ZSB9ID0gdGhpcy5vcHRpb25zXG5cbiAgICAgIC8vaWYgdGhlIGJhY2tlbmQgaGFzIGFscmVhZHkgYmVlbiBjbGVhbmVkIHVwLCBzdG9wIHRyeWluZyB0byBkbyB0aGlzLlxuICAgICAgaWYgKCF0aGlzLmJhY2tlbmRzW2RvY0lkXSkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgY29uc3QgZG9jID0gdGhpcy5iYWNrZW5kc1tkb2NJZF0uYXV0b21lcmdlLmdldERvY3VtZW50KGRvY0lkKVxuXG4gICAgICBpZiAoIWRvYykge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbid0IHJlY2VpdmUgZG9jdW1lbnQgYnkgaWQ6ICR7ZG9jSWR9YClcbiAgICAgIH1cblxuICAgICAgb25Eb2N1bWVudFNhdmUgJiYgKGF3YWl0IG9uRG9jdW1lbnRTYXZlKGRvY0lkLCB0b0pTKGRvYy5jaGlsZHJlbikpKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUuZXJyb3IoJ0Vycm9yIGluIHNhdmVEb2N1bWVudC4nLCBlLCBkb2NJZClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogT24gJ2Rpc2Nvbm5lY3QnIGhhbmRsZXJcbiAgICovXG5cbiAgcHJpdmF0ZSBvbkRpc2Nvbm5lY3QgPSAoaWQ6IHN0cmluZywgc29ja2V0OiBTb2NrZXRJTy5Tb2NrZXQpID0+IGFzeW5jICgpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBvblNvY2tldERpc2Nvbm5lY3Rpb24gfSA9IHRoaXMub3B0aW9uc1xuICAgICAgY29uc3QgbmFtZSA9IHNvY2tldC5uc3AubmFtZVxuXG4gICAgICAvL2luY3JlbWVudCB0aGUgY2xlYW51cCB0aW1lclxuICAgICAgdGhpcy5iYWNrZW5kc1tuYW1lXS5jbGVhbnVwVGltZXIgPVxuICAgICAgICBNYXRoLmZsb29yKERhdGUubm93KCkgLyAxMDAwKSArICh0aGlzLm9wdGlvbnMuY2xlYW5UaHJlc2hvbGQgfHwgMzApICogNjBcblxuICAgICAgLyoqXG4gICAgICAgKiB3cmFwIHRoZSBhdXRvbWVyZ2UgY2xvc2VDb25uZWN0aW9uIGNhbGwgaW4gYSB0aW1lb3V0IHRvIGdpdmUgYW55IG91dHN0YW5kaW5nIG1lc3NhZ2VzXG4gICAgICAgKiB0aW1lIHRvIGZsdXNoIHRvIHRoZSBkYXRhYmFzZVxuICAgICAgICovXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgdGhpcy5iYWNrZW5kc1tuYW1lXS5hdXRvbWVyZ2UuY2xvc2VDb25uZWN0aW9uKGlkKVxuICAgICAgfSwgNTAwMClcblxuICAgICAgdGhpcy5iYWNrZW5kQ291bnRzW25hbWVdID0gdGhpcy5iYWNrZW5kQ291bnRzW25hbWVdIC0gMVxuICAgICAgZGVsZXRlIHRoaXMuYmFja2VuZHNbbmFtZV0ucHJlc2VuY2VEYXRhW3NvY2tldC5pZF1cblxuICAgICAgLy9zZW5kIHByZXNlbmNlIGluZm9ybWF0aW9uIHRvIG5hbWVzcGFjZVxuICAgICAgdGhpcy5pb1xuICAgICAgICAub2YobmFtZSlcbiAgICAgICAgLmNvbXByZXNzKGZhbHNlKVxuICAgICAgICAuZW1pdCgnbXNnJywge1xuICAgICAgICAgIHR5cGU6ICdwYXJ0aWNpcGFudCcsXG4gICAgICAgICAgcGF5bG9hZDogT2JqZWN0LnZhbHVlcyh0aGlzLmJhY2tlbmRzW25hbWVdLnByZXNlbmNlRGF0YSlcbiAgICAgICAgfSlcblxuICAgICAgYXdhaXQgdGhpcy5zYXZlRG9jdW1lbnQobmFtZSlcblxuICAgICAgdGhpcy5nYXJiYWdlQ3Vyc29ycyhuYW1lKVxuXG4gICAgICBvblNvY2tldERpc2Nvbm5lY3Rpb24gJiZcbiAgICAgICAgKGF3YWl0IG9uU29ja2V0RGlzY29ubmVjdGlvbih7XG4gICAgICAgICAgZG9jSWQ6IG5hbWUsXG4gICAgICAgICAgc29ja2V0LFxuICAgICAgICAgIF90aGlzOiB0aGlzXG4gICAgICAgIH0pKVxuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGNvbnNvbGUubG9nKCdFcnJvciBpbiBzbGF0ZS1jb2xsYWIgb25EaXNjb25uZWN0JywgZSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2xlYW4gdXAgdW51c2VkIGN1cnNvciBkYXRhLlxuICAgKi9cblxuICBnYXJiYWdlQ3Vyc29ycyA9IChuc3A6IHN0cmluZykgPT4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBkb2MgPSB0aGlzLmJhY2tlbmRzW25zcF0uYXV0b21lcmdlLmdldERvY3VtZW50KG5zcClcblxuICAgICAgaWYgKCFkb2MuY3Vyc29ycykgcmV0dXJuXG5cbiAgICAgIGNvbnN0IG5hbWVzcGFjZSA9IHRoaXMuaW8ub2YobnNwKVxuXG4gICAgICBPYmplY3Qua2V5cyhkb2M/LmN1cnNvcnMpPy5mb3JFYWNoKGtleSA9PiB7XG4gICAgICAgIGlmICghbmFtZXNwYWNlLnNvY2tldHNba2V5XSkge1xuICAgICAgICAgIHRoaXMuYmFja2VuZHNbbnNwXS5hdXRvbWVyZ2UuZ2FyYmFnZUN1cnNvcihuc3AsIGtleSlcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvL2Rvbid0IG5lY2Vzc2FyaWx5IGNhcmUgaWYgdGhpcyBmYWlscy5cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRGVzdHJveSBTb2NrZXRJTyBjb25uZWN0aW9uXG4gICAqL1xuXG4gIGRlc3Ryb3kgPSBhc3luYyAoKSA9PiB7XG4gICAgdGhpcy5pby5jbG9zZSgpXG4gIH1cbn1cbiJdfQ==