mongoose-transaction-plugin
Version:
A mongoose plugin for transaction-like semantics between multiple documents.
377 lines (376 loc) • 56.2 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const mongoose = require("mongoose");
const _ = require("lodash");
const Bluebird = require("bluebird");
const _debug = require("debug");
const events = require("events");
const debug = _debug('transaction');
const RETRYCOUNT = 5;
const RetryTimeTable = [197, 173, 181, 149, 202];
const TRANSACTION_KEEP_COMMITTED = (process.env.TRANSACTION_KEEP_COMMITTED === 'true' || false);
class Transaction extends events.EventEmitter {
constructor() {
super(...arguments);
this.participants = [];
}
static get getModel() { return Transaction.model; }
static initialize(connection) {
if (this.model)
return;
const historySchema = new mongoose.Schema({
col: { type: String, required: true },
oid: { type: mongoose.Schema.Types.Mixed, required: true },
shardKeyName: { type: String, required: true },
shardKey: { type: mongoose.Schema.Types.Mixed, required: true },
op: { type: String, required: true },
query: { type: String, required: true }
});
const rollbackSchmea = new mongoose.Schema({
col: { type: String, required: true },
oid: { type: mongoose.Schema.Types.Mixed, required: true },
shardKeyName: { type: String, required: true },
shardKey: { type: mongoose.Schema.Types.Mixed, required: true }
});
const transactionSchema = new mongoose.Schema({
history: [historySchema],
rollback: [rollbackSchmea],
state: { type: String, required: true, default: 'init', index: true }
});
this.connection = connection;
this.model = connection.model('Transaction', transactionSchema);
}
begin() {
return Bluebird.try(() => __awaiter(this, void 0, void 0, function* () {
if (!Transaction.model)
throw new Error('Not initialized exception');
if (this.transaction)
throw new Error('Transaction has already been started');
const transaction = new Transaction.model();
// TODO: should be fixed mongoose.d.ts
this.transaction = yield transaction.save();
debug('transaction created: %o', this.transaction);
return this;
})).catch((e) => {
// TODO we should handle every exception correctly in here,
// otherwise the uncaught exception will make the process down.
// Ref. http://bluebirdjs.com/docs/api/disposer.html#note-about-disposers-in-node
// ## Possible exceptions
// - Error('Not initialized exception')
// - Error('Transaction has already been started')
// - A potential error from transaction.save
if (e.message === 'Transaction has already been started')
return;
throw e;
}).disposer((tx, promise) => {
if (promise.isFulfilled()) {
return tx.commit()
.catch(e => {
console.log('tx.commit failed', e);
});
}
return tx.cancel()
.catch(e => {
console.log('tx.cancel failed', e);
});
});
}
static scope(doInTransactionScope) {
return Bluebird.using(new Transaction().begin(), doInTransactionScope);
}
get _id() {
return this.transaction._id;
}
cancel() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.transaction)
return;
if (this.transaction.state && this.transaction.state !== 'init')
return;
try {
yield Bluebird.each(this.participants, (participant) => __awaiter(this, void 0, void 0, function* () {
if (participant.op === 'insert')
return participant.doc.remove();
return participant.doc.update({ $unset: { __t: '' } }).exec();
}));
yield this.transaction.remove();
}
catch (e) {
debug('[warning] removing __t has been failed');
}
this.transaction = undefined;
this.participants = [];
});
}
static rollback(transaction) {
return __awaiter(this, void 0, void 0, function* () {
if (!transaction)
return;
if (transaction.state && transaction.state !== 'init')
return;
const rollbackHistories = transaction.rollback || [];
yield Bluebird.each(rollbackHistories, (history) => __awaiter(this, void 0, void 0, function* () {
debug('find Rollback history collection: ', history.col, ' oid: ', history.oid);
const collection = this.connection.db.collection(history.col);
yield collection.deleteOne({ _id: history.oid, [history.shardKeyName]: history.shardKey });
}));
yield (new Transaction.getModel()).collection.deleteOne({ _id: transaction._id });
});
}
static commitHistory(history, tid) {
return __awaiter(this, void 0, void 0, function* () {
return Promise.resolve(this.connection.db.collection(history.col))
.then((collection) => __awaiter(this, void 0, void 0, function* () {
if (history.op === 'remove')
return Transaction.commitHistoryRemove(history, collection);
return Transaction.commitHistoryUpdate(history, tid, collection);
}))
.catch(err => {
debug(`transaction ${history.op} failed ${err.message}`);
throw new Error(err);
});
});
}
static commitHistoryRemove(history, collection) {
return __awaiter(this, void 0, void 0, function* () {
return collection.deleteOne({ _id: history.oid, [history.shardKeyName]: history.shardKey });
});
}
static commitHistoryUpdate(history, tid, collection) {
return __awaiter(this, void 0, void 0, function* () {
let query = JSON.parse(history.query);
if (history.op === 'insert') {
query = _.omit(query, ['_id', '__t', history.shardKeyName]);
}
else {
query['$unset'] = query['$unset'] || {};
query['$set'] = query['$set'] || {};
query['$set'] = _.omit(query['$set'], ['_id', '__t', history.shardKeyName]);
query['$unset']['__t'] = '';
}
if (query['$set'] != null && Object.keys(query['$set']).length === 0) {
query = _.omit(query, ['$set']);
}
return collection.update({ _id: history.oid, [history.shardKeyName]: history.shardKey, __t: tid }, query, { w: 1 });
});
}
static recommit(transaction) {
return __awaiter(this, void 0, void 0, function* () {
const histories = transaction.history;
if (histories && histories.length === 0)
return Transaction.rollback(transaction);
try {
yield Bluebird.each(histories, (history) => __awaiter(this, void 0, void 0, function* () {
debug('find history collection: ', history.col, ' oid: ', history.oid);
yield Transaction.commitHistory(history, transaction._id);
}));
debug('transaction recommited!');
if (!TRANSACTION_KEEP_COMMITTED) {
yield transaction.remove();
}
else {
transaction.state = 'committed';
yield transaction.save();
}
}
catch (err) {
// 하나라도 실패하면 pending 상태로 recommit 처리된다.
debug('Fails to save whole transactions but they will be saved', err);
throw err;
}
});
}
static validate(doc) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
return doc.validate(err => err && reject(err) || resolve());
});
});
}
static makeHistory(participants, transaction) {
return __awaiter(this, void 0, void 0, function* () {
yield Bluebird.each(participants, (participant) => __awaiter(this, void 0, void 0, function* () {
// TODO: should be fixed mongoose.d.ts
yield Transaction.validate(participant.doc);
debug('delta: %o', participant.doc.$__delta());
const shardKeyName = Transaction.getShardKey(participant.doc);
let query;
if (participant.op === 'update') {
query = JSON.stringify((participant.doc.$__delta() || [null, {}])[1]);
}
else if (participant.op === 'remove') {
query = JSON.stringify({ _id: '', [shardKeyName]: '' });
}
else if (participant.op === 'insert') {
query = JSON.stringify(participant.doc);
}
debug(`[makeHistory] op : ${participant.op} shardKey : ${shardKeyName} history.oid : %o query : ${JSON.stringify(query)}`, participant.doc._id);
transaction.history.push({
col: participant.doc.collection.name,
oid: participant.doc._id,
shardKeyName,
shardKey: participant.doc[shardKeyName],
op: participant.op,
query: query
});
debug(`histroy : %o`, transaction.history[transaction.history.length - 1]);
}));
});
}
commit() {
return __awaiter(this, void 0, void 0, function* () {
if (!this.transaction)
return;
yield Transaction.makeHistory(this.participants, this.transaction);
debug('history generated: %o', this.transaction.history);
this.transaction.state = 'pending';
debug('transaction: %o', this.transaction);
try {
this.transaction = yield this.transaction.save();
}
catch (err) {
this.transaction.state = 'init';
throw err;
}
debug('apply participants\' changes');
try {
yield Bluebird.map(this.participants, (participant) => __awaiter(this, void 0, void 0, function* () {
debug('commit: [%s] %o', participant.op, participant.doc);
debug('delta: %o', participant.doc.$__delta());
if (participant.op === 'remove')
return participant.doc.remove();
if (participant.op === 'insert')
participant.doc.isNew = false;
return participant.doc.save();
}));
debug('transaction committed');
// TRANSACTION_KEEP_COMMITTED 값에 따라 Transaction Document를 지우거나 갱신한다.
if (!TRANSACTION_KEEP_COMMITTED) {
yield this.transaction.remove();
}
else {
this.transaction.state = 'committed';
yield this.transaction.save();
}
}
catch (err) {
// 하나라도 실패하면 pending 상태로 recommit 처리된다.
debug('Fails to save whole transactions but they will be saved', err);
}
this.transaction = undefined;
this.participants = [];
});
}
// 생성될 document를 transaction에 참가시킴
insertDoc(doc, retry = false) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.transaction)
throw new Error('Could not find any transaction');
doc['__t'] = this.transaction._id;
const shardKey = Transaction.getShardKey(doc);
if (_.isNil(doc[shardKey]))
throw new Error(`${shardKey} value is required`);
debug('insertDoc : %o', doc);
yield this.setRollbackHistory(doc);
try {
yield doc.collection.insert(doc);
}
catch (e) {
if (retry || e.code !== 11000)
throw e; // E11000 duplicate key error
const existsDoc = yield doc.collection.findOne({ _id: doc._id, [shardKey]: doc[shardKey] });
if (!existsDoc || _.isNil(existsDoc.__t) || existsDoc.__t.toString() === this.transaction._id.toString())
throw e;
const oldTransaction = yield this.transaction.collection.findOne({ _id: existsDoc.__t });
if (!oldTransaction)
throw e;
yield Transaction.recommit(oldTransaction);
return this.insertDoc(doc, true);
}
this.participants.push({ op: 'insert', doc: doc });
});
}
// 삭제할 document를 transaction에 참가시킴
removeDoc(doc) {
if (!this.transaction)
throw new Error('Could not find any transaction');
const id = doc['__t'];
if (!id || id.toHexString() !== this.transaction.id)
throw new Error('Already other locked');
this.participants.push({ op: 'remove', doc: doc });
}
findOne(model, cond, fields, options) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.transaction)
throw new Error('Could not find any transaction');
const withCond = _.find(this.participants, p => {
return p.model === model && JSON.stringify(cond) === JSON.stringify(p.cond);
});
if (withCond && withCond.doc)
return withCond.doc;
if (!options)
options = { retrycount: RETRYCOUNT };
if (options['retrycount'] === undefined) {
debug('set retrycount ', options, options['retrycount']);
options['retrycount'] = RETRYCOUNT;
}
const opt = _.cloneDeep(options || {});
opt['__t'] = this.transaction._id;
opt['tModel'] = Transaction.getModel;
opt['transaction'] = true;
debug('tModel before ', opt['tModel'].collection.name);
debug('attempt write lock', _.omit(opt, 'tModel'));
let doc;
try {
doc = yield model.findOne(cond, fields, opt).exec();
}
catch (err) {
debug('transaction err : retrycount is ', options['retrycount']);
if (err.message !== 'write lock' || options['retrycount'] === 0)
throw err;
options['retrycount'] -= 1;
yield Bluebird.delay(RetryTimeTable[Math.floor(Math.random() * RetryTimeTable.length)]);
return this.findOne(model, cond, fields, options);
}
if (!doc)
return;
const withSameId = _.find(this.participants, p => {
return p.model === model && p.doc._id.equals(doc._id);
});
if (withSameId && withSameId.doc)
return withSameId.doc;
this.participants.push({ op: 'update', doc: doc, model: model, cond: cond });
return doc;
});
}
setRollbackHistory(doc) {
return __awaiter(this, void 0, void 0, function* () {
const shardKey = Transaction.getShardKey(doc);
const rollbackHistory = {
col: doc.collection.name,
oid: doc._id,
shardKey: doc[shardKey],
shardKeyName: shardKey
};
yield this.transaction.collection.updateOne({ _id: this.transaction._id }, { $push: { rollback: rollbackHistory } });
this.transaction.rollback.push(rollbackHistory);
});
}
static getShardKey(doc) {
return doc.schema.options &&
doc.schema.options.shardKey &&
Object.keys(doc.schema.options.shardKey)[0] ||
'_id';
}
}
Transaction.TRANSACTION_EXPIRE_THRESHOLD = 60 * 1000;
exports.Transaction = Transaction;
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy8uLi9zcmMvdHJhbnNhY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7OztBQUFBLHFDQUFxQztBQUNyQyw0QkFBNEI7QUFDNUIscUNBQXFDO0FBQ3JDLGdDQUFnQztBQUNoQyxpQ0FBaUM7QUFFakMsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0FBQ3BDLE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQztBQUNyQixNQUFNLGNBQWMsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUNqRCxNQUFNLDBCQUEwQixHQUFHLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsS0FBSyxNQUFNLElBQUksS0FBSyxDQUFDLENBQUM7QUFzQ2hHLE1BQWEsV0FBWSxTQUFRLE1BQU0sQ0FBQyxZQUFZO0lBQXBEOztRQVFVLGlCQUFZLEdBQW1CLEVBQUUsQ0FBQztJQWtWNUMsQ0FBQztJQXJWUSxNQUFNLEtBQUssUUFBUSxLQUFLLE9BQU8sV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFLbkQsTUFBTSxDQUFDLFVBQVUsQ0FBQyxVQUErQjtRQUN0RCxJQUFJLElBQUksQ0FBQyxLQUFLO1lBQUUsT0FBTztRQUV2QixNQUFNLGFBQWEsR0FBRyxJQUFJLFFBQVEsQ0FBQyxNQUFNLENBQUM7WUFDeEMsR0FBRyxFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFO1lBQ3JDLEdBQUcsRUFBRSxFQUFFLElBQUksRUFBRSxRQUFRLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRTtZQUMxRCxZQUFZLEVBQUUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7WUFDOUMsUUFBUSxFQUFFLEVBQUUsSUFBSSxF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QztZQUM5QyxNQUFNLGVBQWUsR0FBYztnQkFDakMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSTtnQkFDeEIsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHO2dCQUNaLFFBQVEsRUFBRSxHQUFHLENBQUMsUUFBUSxDQUFDO2dCQUN2QixZQUFZLEVBQUUsUUFBUTthQUN2QixDQUFDO1lBQ0YsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsRUFDN0IsRUFBRSxLQUFLLEVBQUcsRUFBRSxRQUFRLEVBQUcsZUFBZSxFQUFFLEVBQUUsQ0FDMUMsQ0FBQztZQUM3QyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7UUFDbEQsQ0FBQztLQUFBO0lBRU8sTUFBTSxDQUFDLFdBQVcsQ0FBQyxHQUFzQjtRQUMvQyxPQUFjLEdBQUcsQ0FBQyxNQUFPLENBQUMsT0FBTztZQUNuQixHQUFHLENBQUMsTUFBTyxDQUFDLE9BQU8sQ0FBQyxRQUFRO1lBQ25DLE1BQU0sQ0FBQyxJQUFJLENBQVEsR0FBRyxDQUFDLE1BQU8sQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ25ELEtBQUssQ0FBQztJQUNmLENBQUM7O0FBeFZhLHdDQUE0QixHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFEekQsa0NBMFZDIiwiZmlsZSI6InRyYW5zYWN0aW9uLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgbW9uZ29vc2UgZnJvbSAnbW9uZ29vc2UnO1xuaW1wb3J0ICogYXMgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0ICogYXMgQmx1ZWJpcmQgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0ICogYXMgX2RlYnVnIGZyb20gJ2RlYnVnJztcbmltcG9ydCAqIGFzIGV2ZW50cyBmcm9tICdldmVudHMnO1xuXG5jb25zdCBkZWJ1ZyA9IF9kZWJ1ZygndHJhbnNhY3Rpb24nKTtcbmNvbnN0IFJFVFJZQ09VTlQgPSA1O1xuY29uc3QgUmV0cnlUaW1lVGFibGUgPSBbMTk3LCAxNzMsIDE4MSwgMTQ5LCAyMDJdO1xuY29uc3QgVFJBTlNBQ1RJT05fS0VFUF9DT01NSVRURUQgPSAocHJvY2Vzcy5lbnYuVFJBTlNBQ1RJT05fS0VFUF9DT01NSVRURUQgPT09ICd0cnVlJyB8fCBmYWxzZSk7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUhpc3Rvcnkge1xuICAvLyBjb2xsZWN0aW9uIG5hbWVcbiAgY29sOiBzdHJpbmc7XG4gIC8vIF9pZCBkb2VzIG5vdCBuZWNlc3NhcmlseSBoYXZlIHRvIGJlIE9iamVjdElkO1xuICBvaWQ6IGFueTtcbiAgc2hhcmRLZXlOYW1lOiBzdHJpbmc7XG4gIHNoYXJkS2V5OiBhbnk7XG4gIC8vIGluc2VydCwgdXBkYXRlLCByZW1vdmVcbiAgb3A6ICdpbnNlcnQnIHwgJ3JlbW92ZScgfCAndXBkYXRlJztcbiAgLy8gdXBkYXRlIHF1ZXJ5IHN0cmluZy5cbiAgcXVlcnk6IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJUm9sbGJhY2sge1xuICBjb2w6IHN0cmluZztcbiAgb2lkOiBhbnk7XG4gIHNoYXJkS2V5TmFtZTogc3RyaW5nO1xuICBzaGFyZEtleTogYW55O1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElUcmFuc2FjdGlvbiBleHRlbmRzIG1vbmdvb3NlLkRvY3VtZW50IHtcbiAgaGlzdG9yeTogSUhpc3RvcnlbXTtcbiAgcm9sbGJhY2s6IElSb2xsYmFja1tdO1xuICAvLyBpbml0IC0+ICgpIC0+IHBlbmRpbmcgLT4gY29tbWl0dGVkXG4gIHN0YXRlOiBzdHJpbmc7XG4gIGlkOiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBJUGFydGljaXBhbnQge1xuICBvcDogJ2luc2VydCcgfCAncmVtb3ZlJyB8ICd1cGRhdGUnO1xuICBkb2M6IG1vbmdvb3NlLkRvY3VtZW50O1xuICBtb2RlbD86IGFueTtcbiAgY29uZD86IE9iamVjdDtcbiAgX2lkPzogYW55O1xufVxuXG5leHBvcnQgY2xhc3MgVHJhbnNhY3Rpb24gZXh0ZW5kcyBldmVudHMuRXZlbnRFbWl0dGVyIHtcbiAgcHVibGljIHN0YXRpYyBUUkFOU0FDVElPTl9FWFBJUkVfVEhSRVNIT0xEID0gNjAgKiAxMDAwO1xuICBwcml2YXRlIHN0YXRpYyBtb2RlbDogbW9uZ29vc2UuTW9kZWw8SVRyYW5zYWN0aW9uPjtcbiAgcHJpdmF0ZSBzdGF0aWMgY29ubmVjdGlvbjogbW9uZ29vc2UuQ29ubmVjdGlvbjtcblxuICBwdWJsaWMgc3RhdGljIGdldCBnZXRNb2RlbCgpIHsgcmV0dXJuIFRyYW5zYWN0aW9uLm1vZGVsOyB9XG5cbiAgcHJpdmF0ZSB0cmFuc2FjdGlvbjogSVRyYW5zYWN0aW9uO1xuICBwcml2YXRlIHBhcnRpY2lwYW50czogSVBhcnRpY2lwYW50W10gPSBbXTtcblxuICBwdWJsaWMgc3RhdGljIGluaXRpYWxpemUoY29ubmVjdGlvbjogbW9uZ29vc2UuQ29ubmVjdGlvbikge1xuICAgIGlmICh0aGlzLm1vZGVsKSByZXR1cm47XG5cbiAgICBjb25zdCBoaXN0b3J5U2NoZW1hID0gbmV3IG1vbmdvb3NlLlNjaGVtYSh7XG4gICAgICBjb2w6IHsgdHlwZTogU3RyaW5nLCByZXF1aXJlZDogdHJ1ZSB9LFxuICAgICAgb2lkOiB7IHR5cGU6IG1vbmdvb3NlLlNjaGVtYS5UeXBlcy5NaXhlZCwgcmVxdWlyZWQ6IHRydWUgfSxcbiAgICAgIHNoYXJkS2V5TmFtZTogeyB0eXBlOiBTdHJpbmcsIHJlcXVpcmVkOiB0cnVlIH0sXG4gICAgICBzaGFyZEtleTogeyB0eXBlOiBtb25nb29zZS5TY2hlbWEuVHlwZXMuTWl4ZWQsIHJlcXVpcmVkOiB0cnVlIH0sXG4gICAgICBvcDogeyB0eXBlOiBTdHJpbmcsIHJlcXVpcmVkOiB0cnVlIH0sXG4gICAgICBxdWVyeTogeyB0eXBlOiBTdHJpbmcsIHJlcXVpcmVkOiB0cnVlIH1cbiAgICB9KTtcblxuICAgIGNvbnN0IHJvbGxiYWNrU2NobWVhID0gbmV3IG1vbmdvb3NlLlNjaGVtYSh7XG4gICAgICBjb2w6IHsgdHlwZTogU3RyaW5nLCByZXF1aXJlZDogdHJ1ZSB9LFxuICAgICAgb2lkOiB7IHR5cGU6IG1vbmdvb3NlLlNjaGVtYS5UeXBlcy5NaXhlZCwgcmVxdWlyZWQ6IHRydWUgfSxcbiAgICAgIHNoYXJkS2V5TmFtZTogeyB0eXBlOiBTdHJpbmcsIHJlcXVpcmVkOiB0cnVlIH0sXG4gICAgICBzaGFyZEtleTogeyB0eXBlOiBtb25nb29zZS5TY2hlbWEuVHlwZXMuTWl4ZWQsIHJlcXVpcmVkOiB0cnVlIH1cbiAgICB9KTtcblxuICAgIGNvbnN0IHRyYW5zYWN0aW9uU2NoZW1hID0gbmV3IG1vbmdvb3NlLlNjaGVtYSh7XG4gICAgICBoaXN0b3J5OiBbaGlzdG9yeVNjaGVtYV0sXG4gICAgICByb2xsYmFjazogW3JvbGxiYWNrU2NobWVhXSxcbiAgICAgIHN0YXRlOiB7IHR5cGU6IFN0cmluZywgcmVxdWlyZWQ6IHRydWUsIGRlZmF1bHQ6ICdpbml0JywgaW5kZXg6IHRydWUgfVxuICAgIH0pO1xuICAgIHRoaXMuY29ubmVjdGlvbiA9IGNvbm5lY3Rpb247XG4gICAgdGhpcy5tb2RlbCA9IGNvbm5lY3Rpb24ubW9kZWw8SVRyYW5zYWN0aW9uPignVHJhbnNhY3Rpb24nLCB0cmFuc2FjdGlvblNjaGVtYSk7XG4gIH1cblxuICBwdWJsaWMgYmVnaW4oKSB7XG4gICAgcmV0dXJuIEJsdWViaXJkLnRyeShhc3luYyAoKSA9PiB7XG4gICAgICBpZiAoIVRyYW5zYWN0aW9uLm1vZGVsKSB0aHJvdyBuZXcgRXJyb3IoJ05vdCBpbml0aWFsaXplZCBleGNlcHRpb24nKTtcbiAgICAgIGlmICh0aGlzLnRyYW5zYWN0aW9uKSB0aHJvdyBuZXcgRXJyb3IoJ1RyYW5zYWN0aW9uIGhhcyBhbHJlYWR5IGJlZW4gc3RhcnRlZCcpO1xuXG4gICAgICBjb25zdCB0cmFuc2FjdGlvbiA9IG5ldyBUcmFuc2FjdGlvbi5tb2RlbCgpO1xuICAgICAgLy8gVE9ETzogc2hvdWxkIGJlIGZpeGVkIG1vbmdvb3NlLmQudHNcbiAgICAgIHRoaXMudHJhbnNhY3Rpb24gPSBhd2FpdCB0cmFuc2FjdGlvbi5zYXZlKCk7XG4gICAgICBkZWJ1ZygndHJhbnNhY3Rpb24gY3JlYXRlZDogJW8nLCB0aGlzLnRyYW5zYWN0aW9uKTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH0pLmNhdGNoKChlOiBFcnJvcikgPT4ge1xuICAgICAgLy8gVE9ETyB3ZSBzaG91bGQgaGFuZGxlIGV2ZXJ5IGV4Y2VwdGlvbiBjb3JyZWN0bHkgaW4gaGVyZSxcbiAgICAgIC8vIG90aGVyd2lzZSB0aGUgdW5jYXVnaHQgZXhjZXB0aW9uIHdpbGwgbWFrZSB0aGUgcHJvY2VzcyBkb3duLlxuICAgICAgLy8gUmVmLiBodHRwOi8vYmx1ZWJpcmRqcy5jb20vZG9jcy9hcGkvZGlzcG9zZXIuaHRtbCNub3RlLWFib3V0LWRpc3Bvc2Vycy1pbi1ub2RlXG5cbiAgICAgIC8vICMjIFBvc3NpYmxlIGV4Y2VwdGlvbnNcbiAgICAgIC8vIC0gRXJyb3IoJ05vdCBpbml0aWFsaXplZCBleGNlcHRpb24nKVxuICAgICAgLy8gLSBFcnJvcignVHJhbnNhY3Rpb24gaGFzIGFscmVhZHkgYmVlbiBzdGFydGVkJylcbiAgICAgIC8vIC0gQSBwb3RlbnRpYWwgZXJyb3IgZnJvbSB0cmFuc2FjdGlvbi5zYXZlXG4gICAgICBpZiAoZS5tZXNzYWdlID09PSAnVHJhbnNhY3Rpb24gaGFzIGFscmVhZHkgYmVlbiBzdGFydGVkJykgcmV0dXJuO1xuXG4gICAgICB0aHJvdyBlO1xuICAgIH0pLmRpc3Bvc2VyKCh0eDogdGhpcywgcHJvbWlzZSkgPT4ge1xuICAgICAgaWYgKHByb21pc2UuaXNGdWxmaWxsZWQoKSkge1xuICAgICAgICByZXR1cm4gdHguY29tbWl0KClcbiAgICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgICBjb25zb2xlLmxvZygndHguY29tbWl0IGZhaWxlZCcsIGUpO1xuICAgICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHR4LmNhbmNlbCgpXG4gICAgICAgIC5jYXRjaChlID0+IHtcbiAgICAgICAgICBjb25zb2xlLmxvZygndHguY2FuY2VsIGZhaWxlZCcsIGUpO1xuICAgICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIHN0YXRpYyBzY29wZTxSPihkb0luVHJhbnNhY3Rpb25TY29wZTogKHQ6IFRyYW5zYWN0aW9uKSA9PiBCbHVlYmlyZDxSPiB8IFByb21pc2U8Uj4pOiBCbHVlYmlyZDxSPiB8IFByb21pc2U8Uj4ge1xuICAgIHJldHVybiBCbHVlYmlyZC51c2luZzxUcmFuc2FjdGlvbiwgUj4obmV3IFRyYW5zYWN0aW9uKCkuYmVnaW4oKSwgZG9JblRyYW5zYWN0aW9uU2NvcGUpO1xuICB9XG5cbiAgcHVibGljIGdldCBfaWQoKTogbW9uZ29vc2UuVHlwZXMuT2JqZWN0SWQge1xuICAgIHJldHVybiB0aGlzLnRyYW5zYWN0aW9uLl9pZDtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBjYW5jZWwoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLnRyYW5zYWN0aW9uKSByZXR1cm47XG4gICAgaWYgKHRoaXMudHJhbnNhY3Rpb24uc3RhdGUgJiYgdGhpcy50cmFuc2FjdGlvbi5zdGF0ZSAhPT0gJ2luaXQnKSByZXR1cm47XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IEJsdWViaXJkLmVhY2godGhpcy5wYXJ0aWNpcGFudHMsIGFzeW5jIChwYXJ0aWNpcGFudCkgPT4ge1xuICAgICAgICBpZiAocGFydGljaXBhbnQub3AgPT09ICdpbnNlcnQnKVxuICAgICAgICAgIHJldHVybiBwYXJ0aWNpcGFudC5kb2MucmVtb3ZlKCk7XG5cbiAgICAgICAgcmV0dXJuIHBhcnRpY2lwYW50LmRvYy51cGRhdGUoeyR1bnNldDoge19fdDogJyd9fSkuZXhlYygpO1xuICAgICAgfSk7XG4gICAgICBhd2FpdCB0aGlzLnRyYW5zYWN0aW9uLnJlbW92ZSgpO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGRlYnVnKCdbd2FybmluZ10gcmVtb3ZpbmcgX190IGhhcyBiZWVuIGZhaWxlZCcpO1xuICAgIH1cbiAgICB0aGlzLnRyYW5zYWN0aW9uID0gdW5kZWZpbmVkO1xuICAgIHRoaXMucGFydGljaXBhbnRzID0gW107XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBhc3luYyByb2xsYmFjayh0cmFuc2FjdGlvbjogSVRyYW5zYWN0aW9uKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0cmFuc2FjdGlvbikgcmV0dXJuO1xuICAgIGlmICh0cmFuc2FjdGlvbi5zdGF0ZSAmJiB0cmFuc2FjdGlvbi5zdGF0ZSAhPT0gJ2luaXQnKSByZXR1cm47XG4gICAgY29uc3Qgcm9sbGJhY2tIaXN0b3JpZXMgPSB0cmFuc2FjdGlvbi5yb2xsYmFjayB8fCBbXTtcbiAgICBhd2FpdCBCbHVlYmlyZC5lYWNoKHJvbGxiYWNrSGlzdG9yaWVzLCBhc3luYyAoaGlzdG9yeSkgPT4ge1xuICAgICAgZGVidWcoJ2ZpbmQgUm9sbGJhY2sgaGlzdG9yeSBjb2xsZWN0aW9uOiAnLCBoaXN0b3J5LmNvbCwgJyBvaWQ6ICcsIGhpc3Rvcnkub2lkKTtcbiAgICAgIGNvbnN0IGNvbGxlY3Rpb24gPSB0aGlzLmNvbm5lY3Rpb24uZGIuY29sbGVjdGlvbihoaXN0b3J5LmNvbCk7XG4gICAgICBhd2FpdCBjb2xsZWN0aW9uLmRlbGV0ZU9uZSh7X2lkOiBoaXN0b3J5Lm9pZCwgW2hpc3Rvcnkuc2hhcmRLZXlOYW1lXTogaGlzdG9yeS5zaGFyZEtleX0pO1xuICAgIH0pO1xuICAgIGF3YWl0IChuZXcgVHJhbnNhY3Rpb24uZ2V0TW9kZWwoKSkuY29sbGVjdGlvbi5kZWxldGVPbmUoe19pZDogdHJhbnNhY3Rpb24uX2lkfSk7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBhc3luYyBjb21taXRIaXN0b3J5KGhpc3Rvcnk6IElIaXN0b3J5LCB0aWQ6IG1vbmdvb3NlLlR5cGVzLk9iamVjdElkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0aGlzLmNvbm5lY3Rpb24uZGIuY29sbGVjdGlvbihoaXN0b3J5LmNvbCkpXG4gICAgLnRoZW4oYXN5bmMgY29sbGVjdGlvbiA9PiB7XG4gICAgICBpZiAoaGlzdG9yeS5vcCA9PT0gJ3JlbW92ZScpXG4gICAgICAgIHJldHVybiBUcmFuc2FjdGlvbi5jb21taXRIaXN0b3J5UmVtb3ZlKGhpc3RvcnksIGNvbGxlY3Rpb24pO1xuICAgICAgcmV0dXJuIFRyYW5zYWN0aW9uLmNvbW1pdEhpc3RvcnlVcGRhdGUoaGlzdG9yeSwgdGlkLCBjb2xsZWN0aW9uKTtcbiAgICB9KVxuICAgIC5jYXRjaChlcnIgPT4ge1xuICAgICAgZGVidWcoYHRyYW5zYWN0aW9uICR7aGlzdG9yeS5vcH0gZmFpbGVkICR7ZXJyLm1lc3NhZ2V9YCk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoZXJyKTtcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhdGljIGFzeW5jIGNvbW1pdEhpc3RvcnlSZW1vdmUoaGlzdG9yeTogSUhpc3RvcnksIGNvbGxlY3Rpb246IGFueSk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBjb2xsZWN0aW9uLmRlbGV0ZU9uZSh7X2lkOiBoaXN0b3J5Lm9pZCwgW2hpc3Rvcnkuc2hhcmRLZXlOYW1lXTogaGlzdG9yeS5zaGFyZEtleX0pO1xuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgY29tbWl0SGlzdG9yeVVwZGF0ZShoaXN0b3J5OiBJSGlzdG9yeSwgdGlkOiBtb25nb29zZS5UeXBlcy5PYmplY3RJZCwgY29sbGVjdGlvbjogYW55KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgbGV0IHF1ZXJ5ID0gSlNPTi5wYXJzZShoaXN0b3J5LnF1ZXJ5KTtcblxuICAgIGlmIChoaXN0b3J5Lm9wID09PSAnaW5zZXJ0Jykge1xuICAgICAgcXVlcnkgPSBfLm9taXQocXVlcnksIFsnX2lkJywgJ19fdCcsIGhpc3Rvcnkuc2hhcmRLZXlOYW1lXSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHF1ZXJ5WyckdW5zZXQnXSA9IHF1ZXJ5WyckdW5zZXQnXSB8fCB7fTtcbiAgICAgIHF1ZXJ5Wyckc2V0J10gPSBxdWVyeVsnJHNldCddIHx8IHt9O1xuXG4gICAgICBxdWVyeVsnJHNldCddID0gXy5vbWl0KHF1ZXJ5Wyckc2V0J10sIFsnX2lkJywgJ19fdCcsIGhpc3Rvcnkuc2hhcmRLZXlOYW1lXSk7XG4gICAgICBxdWVyeVsnJHVuc2V0J11bJ19fdCddID0gJyc7XG4gICAgfVxuXG4gICAgaWYgKHF1ZXJ5Wyckc2V0J10gIT0gbnVsbCAmJiBPYmplY3Qua2V5cyhxdWVyeVsnJHNldCddKS5sZW5ndGggPT09IDApIHtcbiAgICAgIHF1ZXJ5ID0gXy5vbWl0KHF1ZXJ5LCBbJyRzZXQnXSk7XG4gICAgfVxuICAgIHJldHVybiBjb2xsZWN0aW9uLnVwZGF0ZSh7X2lkOiBoaXN0b3J5Lm9pZCwgW2hpc3Rvcnkuc2hhcmRLZXlOYW1lXTogaGlzdG9yeS5zaGFyZEtleSwgX190IDogdGlkfSwgcXVlcnksIHsgdyA6IDEgfSk7XG4gIH1cblxuICBwdWJsaWMgc3RhdGljIGFzeW5jIHJlY29tbWl0KHRyYW5zYWN0aW9uOiBJVHJhbnNhY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBoaXN0b3JpZXMgPSB0cmFuc2FjdGlvbi5oaXN0b3J5O1xuICAgIGlmIChoaXN0b3JpZXMgJiYgaGlzdG9yaWVzLmxlbmd0aCA9PT0gMClcbiAgICAgIHJldHVybiBUcmFuc2FjdGlvbi5yb2xsYmFjayh0cmFuc2FjdGlvbik7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IEJsdWViaXJkLmVhY2goaGlzdG9yaWVzLCBhc3luYyAoaGlzdG9yeSkgPT4ge1xuICAgICAgICBkZWJ1ZygnZmluZCBoaXN0b3J5IGNvbGxlY3Rpb246ICcsIGhpc3RvcnkuY29sLCAnIG9pZDogJywgaGlzdG9yeS5vaWQpO1xuICAgICAgICBhd2FpdCBUcmFuc2FjdGlvbi5jb21taXRIaXN0b3J5KGhpc3RvcnksIHRyYW5zYWN0aW9uLl9pZCk7XG4gICAgICB9KTtcbiAgICAgIGRlYnVnKCd0cmFuc2FjdGlvbiByZWNvbW1pdGVkIScpO1xuXG4gICAgICBpZiAoIVRSQU5TQUNUSU9OX0tFRVBfQ09NTUlUVEVEKSB7XG4gICAgICAgIGF3YWl0IHRyYW5zYWN0aW9uLnJlbW92ZSgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdHJhbnNhY3Rpb24uc3RhdGUgPSAnY29tbWl0dGVkJztcbiAgICAgICAgYXdhaXQgdHJhbnNhY3Rpb24uc2F2ZSgpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8g7ZWY64KY652864+EIOyLpO2MqO2VmOuptCBwZW5kaW5nIOyDge2DnOuhnCByZWNvbW1pdCDsspjrpqzrkJzri6QuXG4gICAgICBkZWJ1ZygnRmFpbHMgdG8gc2F2ZSB3aG9sZSB0cmFuc2FjdGlvbnMgYnV0IHRoZXkgd2lsbCBiZSBzYXZlZCcsIGVycik7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzdGF0aWMgYXN5bmMgdmFsaWRhdGUoZG9jOiBhbnkpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgcmV0dXJuIGRvYy52YWxpZGF0ZShlcnIgPT4gZXJyICYmIHJlamVjdChlcnIpIHx8IHJlc29sdmUoKSk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIHN0YXRpYyBhc3luYyBtYWtlSGlzdG9yeShwYXJ0aWNpcGFudHM6IElQYXJ0aWNpcGFudFtdLCB0cmFuc2FjdGlvbjogSVRyYW5zYWN0aW9uKSB7XG4gICAgYXdhaXQgQmx1ZWJpcmQuZWFjaChwYXJ0aWNpcGFudHMsIGFzeW5jIChwYXJ0aWNpcGFudCkgPT4ge1xuICAgICAgLy8gVE9ETzogc2hvdWxkIGJlIGZpeGVkIG1vbmdvb3NlLmQudHNcbiAgICAgIGF3YWl0IFRyYW5zYWN0aW9uLnZhbGlkYXRlKHBhcnRpY2lwYW50LmRvYyk7XG5cbiAgICAgIGRlYnVnKCdkZWx0YTogJW8nLCAoPGFueT5wYXJ0aWNpcGFudC5kb2MpLiRfX2RlbHRhKCkpO1xuICAgICAgY29uc3Qgc2hhcmRLZXlOYW1lID0gVHJhbnNhY3Rpb24uZ2V0U2hhcmRLZXkocGFydGljaXBhbnQuZG9jKTtcbiAgICAgIGxldCBxdWVyeTogc3RyaW5nO1xuICAgICAgaWYgKHBhcnRpY2lwYW50Lm9wID09PSAndXBkYXRlJykge1xuICAgICAgICBxdWVyeSA9IEpTT04uc3RyaW5naWZ5KCgoPGFueT5wYXJ0aWNpcGFudC5kb2MpLiRfX2RlbHRhKCkgfHwgW251bGwsIHt9XSlbMV0pO1xuICAgICAgfSBlbHNlIGlmIChwYXJ0aWNpcGFudC5vcCA9PT0gJ3JlbW92ZScpIHtcbiAgICAgICAgcXVlcnkgPSBKU09OLnN0cmluZ2lmeSh7IF9pZDogJycsIFtzaGFyZEtleU5hbWVdOiAnJyB9KTtcbiAgICAgIH0gZWxzZSBpZiAocGFydGljaXBhbnQub3AgPT09ICdpbnNlcnQnKSB7XG4gICAgICAgIHF1ZXJ5ID0gSlNPTi5zdHJpbmdpZnkocGFydGljaXBhbnQuZG9jKTtcbiAgICAgIH1cbiAgICAgIGRlYnVnKGBbbWFrZUhpc3RvcnldIG9wIDogJHtwYXJ0aWNpcGFudC5vcH0gc2hhcmRLZXkgOiAke3NoYXJkS2V5TmFtZX0gaGlzdG9yeS5vaWQgOiAlbyBxdWVyeSA6ICR7SlNPTi5zdHJpbmdpZnkocXVlcnkpfWAsIHBhcnRpY2lwYW50LmRvYy5faWQpO1xuXG4gICAgICB0cmFuc2FjdGlvbi5oaXN0b3J5LnB1c2goe1xuICAgICAgICBjb2w6ICg8YW55PnBhcnRpY2lwYW50LmRvYykuY29sbGVjdGlvbi5uYW1lLFxuICAgICAgICBvaWQ6IHBhcnRpY2lwYW50LmRvYy5faWQsXG4gICAgICAgIHNoYXJkS2V5TmFtZSxcbiAgICAgICAgc2hhcmRLZXk6IHBhcnRpY2lwYW50LmRvY1tzaGFyZEtleU5hbWVdLFxuICAgICAgICBvcDogcGFydGljaXBhbnQub3AsXG4gICAgICAgIHF1ZXJ5OiBxdWVyeVxuICAgICAgfSk7XG4gICAgICBkZWJ1ZyhgaGlzdHJveSA6ICVvYCwgdHJhbnNhY3Rpb24uaGlzdG9yeVt0cmFuc2FjdGlvbi5oaXN0b3J5Lmxlbmd0aCAtIDFdKTtcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBjb21taXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLnRyYW5zYWN0aW9uKSByZXR1cm47XG5cbiAgICBhd2FpdCBUcmFuc2FjdGlvbi5tYWtlSGlzdG9yeSh0aGlzLnBhcnRpY2lwYW50cywgdGhpcy50cmFuc2FjdGlvbik7XG4gICAgZGVidWcoJ2hpc3RvcnkgZ2VuZXJhdGVkOiAlbycsIHRoaXMudHJhbnNhY3Rpb24uaGlzdG9yeSk7XG5cbiAgICB0aGlzLnRyYW5zYWN0aW9uLnN0YXRlID0gJ3BlbmRpbmcnO1xuICAgIGRlYnVnKCd0cmFuc2FjdGlvbjogJW8nLCB0aGlzLnRyYW5zYWN0aW9uKTtcbiAgICB0cnkge1xuICAgICAgdGhpcy50cmFuc2FjdGlvbiA9IGF3YWl0IHRoaXMudHJhbnNhY3Rpb24uc2F2ZSgpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgdGhpcy50cmFuc2FjdGlvbi5zdGF0ZSA9ICdpbml0JztcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG5cbiAgICBkZWJ1ZygnYXBwbHkgcGFydGljaXBhbnRzXFwnIGNoYW5nZXMnKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgQmx1ZWJpcmQubWFwKHRoaXMucGFydGljaXBhbnRzLCBhc3luYyAocGFydGljaXBhbnQpID0+IHtcbiAgICAgICAgZGVidWcoJ2NvbW1pdDogWyVzXSAlbycsIHBhcnRpY2lwYW50Lm9wLCBwYXJ0aWNpcGFudC5kb2MpO1xuICAgICAgICBkZWJ1ZygnZGVsdGE6ICVvJywgKHBhcnRpY2lwYW50LmRvYyBhcyBhbnkpLiRfX2RlbHRhKCkpO1xuICAgICAgICBpZiAocGFydGljaXBhbnQub3AgPT09ICdyZW1vdmUnKSByZXR1cm4gcGFydGljaXBhbnQuZG9jLnJlbW92ZSgpO1xuICAgICAgICBpZiAocGFydGljaXBhbnQub3AgPT09ICdpbnNlcnQnKSBwYXJ0aWNpcGFudC5kb2MuaXNOZXcgPSBmYWxzZTtcbiAgICAgICAgcmV0dXJuIHBhcnRpY2lwYW50LmRvYy5zYXZlKCk7XG4gICAgICB9KTtcblxuICAgICAgZGVidWcoJ3RyYW5zYWN0aW9uIGNvbW1pdHRlZCcpO1xuICAgICAgLy8gVFJBTlNBQ1RJT05fS0VFUF9DT01NSVRURUQg6rCS7JeQIOuUsOudvCBUcmFuc2FjdGlvbiBEb2N1bWVudOulvCDsp