UNPKG

mongoose-transaction-plugin

Version:

A mongoose plugin for transaction-like semantics between multiple documents.

377 lines (376 loc) 56.2 kB
"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