UNPKG

mongoose-transaction-plugin

Version:

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

188 lines (186 loc) 22.7 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 _debug = require("debug"); const _ = require("lodash"); const transaction_1 = require("./transaction"); mongoose.Promise = Promise; const debug = _debug('transaction'); class PreFindOne { constructor(schema) { this.schema = schema; } get options() { return this.schema.options; } get model() { return this.schema.model; } get _conditions() { return this.schema._conditions; } get _fields() { return this.schema._fields; } checkInSameTransaction(newTid, oldTid) { if (!oldTid) return false; if (newTid && newTid.equals(oldTid)) { debug('already locked: ', oldTid); return true; } return false; } isExpired(tid) { const docTime = tid.getTimestamp(); const current = new Date().getTime(); debug('timestamp! ', docTime, current, current - docTime); return (current - docTime) >= 30000; } recommitTransaction(tModel, t) { return __awaiter(this, void 0, void 0, function* () { debug('recommit transaction'); yield transaction_1.Transaction.recommit(t); yield tModel.update({ _id: t._id }, { state: 'committed' }, { w: 1 }).exec(); }); } cancelTransaction(tModel, tid) { debug('cancel transaction'); tModel.update({ _id: tid }, { state: 'canceled' }, { w: 1 }).exec(); } resolvePreviousTransaction(tid) { return __awaiter(this, void 0, void 0, function* () { const tModel = this.options.tModel; debug('tModel is ', tModel.collection.name); if (!this.isExpired(tid)) return; // let it be conflicted const transaction = yield tModel.findOne({ _id: tid }).exec(); if (!transaction) throw new Error('There is no transaction history.'); debug('find Transaction ', transaction, transaction.state); switch (transaction.state) { case 'init': this.cancelTransaction(tModel, tid); return tid; case 'pending': yield this.recommitTransaction(tModel, transaction); return { '$exists': false }; case 'committed': debug('already committed. ignore __t'); return tid; case 'canceled': // TODO : 비정상 상태로 인하여 canceled가 남는 경우 transcation document는 제거 되지 않고 현재 남겨져 있다. debug('already canceled. ignore __t'); return tid; } }); } isUpdateSuccessfully(rawResponse) { return rawResponse.n > 0 && rawResponse.nModified > 0 && rawResponse.n === rawResponse.nModified; } update(query) { return __awaiter(this, void 0, void 0, function* () { const rawResponse = yield this.model.update(this._conditions, query, { force: true, w: 1 }).exec(); debug('rawResponse: %o', rawResponse); if (!this.isUpdateSuccessfully(rawResponse)) { throw new Error('write lock'); } }); } getMinimalDoc(conditions) { return __awaiter(this, void 0, void 0, function* () { return this.model.findOne(conditions, { _id: 1, __t: 1 }).exec(); }); } run() { return __awaiter(this, void 0, void 0, function* () { if (!this.options.transaction) return; if (this.options.transaction && !this.options.__t) throw new Error('Called `findOne` outside of a transaction'); debug('pre-findOne'); debug('options: %o', _.omit(this.options, 'tModel')); debug('conditions: %o', this._conditions); const doc = yield this.getMinimalDoc(this._conditions); if (!doc) return; debug('document found! t : ', doc.__t, ', id : ', doc.id); if (this.checkInSameTransaction(this.options.__t, doc.__t)) return; this._conditions['_id'] = doc._id; this._conditions['__t'] = { $exists: false }; if (doc.__t) { this._conditions['__t'] = yield this.resolvePreviousTransaction(doc.__t); } debug('conditions are modified', this._conditions); const updateQuery = { __t: this.options.__t }; debug('update query is modified %o', updateQuery); yield this.update(updateQuery); debug('locking success'); delete this._conditions['__t']; debug('rollback conditions', this._conditions); if (this._fields) this._fields['__t'] = 1; }); } } function ensureNoUnique(schema) { const indexes = schema.indexes(); const shardKey = schema.options.shardKey && Object.keys(schema.options.shardKey)[0]; indexes.forEach(([name, options]) => { if (options && options.unique) { if (typeof name === 'object') { name = Object.keys(name)[0]; } if (name !== shardKey) throw new Error(`Transaction doesn't support an unique index (${name}) shardKey (${shardKey})`); } }); } function plugin(schema, options) { ensureNoUnique(schema); schema.add({ __t: { type: mongoose.Schema.Types.ObjectId }, }); const oldIndexMethod = schema.index; schema.index = function (...args) { const res = oldIndexMethod.apply(this, args); ensureNoUnique(schema); return res; }; schema.pre('save', function (next) { debug('pre-save'); debug('checking __t field: ', this.__t); if (!this.__t && !this.isNew) return next(new Error('You can\' not save without lock')); // TODO: // if (this.__t.toString().substring(8, 18) !== '0000000000') return next(new Error('')); this.__t = undefined; next(); }); schema.pre('findOne', function (next) { return __awaiter(this, void 0, void 0, function* () { const o = new PreFindOne(this); try { yield o.run(); next(); } catch (e) { next(e); } }); }); } exports.plugin = plugin; //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy8uLi9zcmMvcGx1Z2luLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSxxQ0FBcUM7QUFDckMsZ0NBQWdDO0FBQ2hDLDRCQUE0QjtBQUU1QiwrQ0FBNEM7QUFDM0MsUUFBZ0IsQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0FBRXBDLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxhQUFhLENBQUMsQ0FBQztBQU9wQyxNQUFNLFVBQVU7SUFHZCxZQUFhLE1BQU07UUFDakIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVELElBQUksT0FBTztRQUNULE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDN0IsQ0FBQztJQUVELElBQUksS0FBSztRQUNQLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFDM0IsQ0FBQztJQUVELElBQUksV0FBVztRQUNiLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUM7SUFDakMsQ0FBQztJQUVELElBQUksT0FBTztRQUNULE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDN0IsQ0FBQztJQUVELHNCQUFzQixDQUFDLE1BQU0sRUFBRSxNQUFNO1FBQ25DLElBQUksQ0FBQyxNQUFNO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFDMUIsSUFBSSxNQUFNLElBQUksTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNuQyxLQUFLLENBQUMsa0JBQWtCLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDbEMsT0FBTyxJQUFJLENBQUM7U0FDYjtRQUNELE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUVELFNBQVMsQ0FBQyxHQUFHO1FBQ1gsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ25DLE1BQU0sT0FBTyxHQUFHLElBQUksSUFBSSxFQUFFLENBQUMs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Q0FBQyxDQUFDO1FBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUs7WUFBRSxPQUFPLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDLENBQUM7UUFFeEYsUUFBUTtRQUNSLHlGQUF5RjtRQUN6RixJQUFJLENBQUMsR0FBRyxHQUFHLFNBQVMsQ0FBQztRQUNyQixJQUFJLEVBQUUsQ0FBQztJQUNULENBQUMsQ0FBQyxDQUFDO0lBRUgsTUFBTSxDQUFDLEdBQUcsQ0FBQyxTQUFTLEVBQUUsVUFBZ0IsSUFBSTs7WUFDeEMsTUFBTSxDQUFDLEdBQUcsSUFBSSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDL0IsSUFBSTtnQkFDRixNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDZCxJQUFJLEVBQUUsQ0FBQzthQUNSO1lBQUMsT0FBTyxDQUFDLEVBQUU7Z0JBQ1YsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2FBQ1Q7UUFDSCxDQUFDO0tBQUEsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQW5DRCx3QkFtQ0MiLCJmaWxlIjoicGx1Z2luLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgbW9uZ29vc2UgZnJvbSAnbW9uZ29vc2UnO1xuaW1wb3J0ICogYXMgX2RlYnVnIGZyb20gJ2RlYnVnJztcbmltcG9ydCAqIGFzIF8gZnJvbSAnbG9kYXNoJztcblxuaW1wb3J0IHsgVHJhbnNhY3Rpb24gfSBmcm9tICcuL3RyYW5zYWN0aW9uJztcbihtb25nb29zZSBhcyBhbnkpLlByb21pc2UgPSBQcm9taXNlO1xuXG5jb25zdCBkZWJ1ZyA9IF9kZWJ1ZygndHJhbnNhY3Rpb24nKTtcblxuZXhwb3J0IGludGVyZmFjZSBUeERvY3VtZW50IGV4dGVuZHMgbW9uZ29vc2UuRG9jdW1lbnQge1xuICBfX3Q/OiBtb25nb29zZS5UeXBlcy5PYmplY3RJZDtcbiAgLy8gX19uZXc/OiBib29sZWFuO1xufVxuXG5jbGFzcyBQcmVGaW5kT25lIHtcbiAgc2NoZW1hOiBhbnk7XG5cbiAgY29uc3RydWN0b3IgKHNjaGVtYSkge1xuICAgIHRoaXMuc2NoZW1hID0gc2NoZW1hO1xuICB9XG5cbiAgZ2V0IG9wdGlvbnMoKSB7XG4gICAgcmV0dXJuIHRoaXMuc2NoZW1hLm9wdGlvbnM7XG4gIH1cblxuICBnZXQgbW9kZWwoKSB7XG4gICAgcmV0dXJuIHRoaXMuc2NoZW1hLm1vZGVsO1xuICB9XG5cbiAgZ2V0IF9jb25kaXRpb25zKCkge1xuICAgIHJldHVybiB0aGlzLnNjaGVtYS5fY29uZGl0aW9ucztcbiAgfVxuXG4gIGdldCBfZmllbGRzKCkge1xuICAgIHJldHVybiB0aGlzLnNjaGVtYS5fZmllbGRzO1xuICB9XG5cbiAgY2hlY2tJblNhbWVUcmFuc2FjdGlvbihuZXdUaWQsIG9sZFRpZCkge1xuICAgIGlmICghb2xkVGlkKSByZXR1cm4gZmFsc2U7XG4gICAgaWYgKG5ld1RpZCAmJiBuZXdUaWQuZXF1YWxzKG9sZFRpZCkpIHtcbiAgICAgIGRlYnVnKCdhbHJlYWR5IGxvY2tlZDogJywgb2xkVGlkKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBpc0V4cGlyZWQodGlkKSB7XG4gICAgY29uc3QgZG9jVGltZSA9IHRpZC5nZXRUaW1lc3RhbXAoKTtcbiAgICBjb25zdCBjdXJyZW50ID0gbmV3IERhdGUoKS5nZXRUaW1lKCk7XG4gICAgZGVidWcoJ3RpbWVzdGFtcCEgJywgZG9jVGltZSwgY3VycmVudCwgY3VycmVudCAtIGRvY1RpbWUpO1xuXG4gICAgcmV0dXJuIChjdXJyZW50IC0gZG9jVGltZSkgPj0gMzAwMDA7XG4gIH1cblxuICBhc3luYyByZWNvbW1pdFRyYW5zYWN0aW9uKHRNb2RlbCwgdCkge1xuICAgIGRlYnVnKCdyZWNvbW1pdCB0cmFuc2FjdGlvbicpO1xuICAgIGF3YWl0IFRyYW5zYWN0aW9uLnJlY29tbWl0KHQpO1xuICAgIGF3YWl0IHRNb2RlbC51cGRhdGUoe19pZDogdC5faWR9LCB7c3RhdGU6ICdjb21taXR0ZWQnfSwge3c6IDF9KS5leGVjKCk7XG4gIH1cblxuICBjYW5jZWxUcmFuc2FjdGlvbih0TW9kZWwsIHRpZCkge1xuICAgIGRlYnVnKCdjYW5jZWwgdHJhbnNhY3Rpb24nKTtcbiAgICB0TW9kZWwudXBkYXRlKHtfaWQ6IHRpZH0sIHtzdGF0ZTogJ2NhbmNlbGVkJ30sIHt3OiAxfSkuZXhlYygpO1xuICB9XG5cbiAgYXN5bmMgcmVzb2x2ZVByZXZpb3VzVHJhbnNhY3Rpb24odGlkKSB7XG4gICAgY29uc3QgdE1vZGVsID0gdGhpcy5vcHRpb25zLnRNb2RlbDtcbiAgICBkZWJ1ZygndE1vZGVsIGlzICcsIHRNb2RlbC5jb2xsZWN0aW9uLm5hbWUpO1xuXG4gICAgaWYgKCF0aGlzLmlzRXhwaXJlZCh0aWQpKSByZXR1cm47IC8vIGxldCBpdCBiZSBjb25mbGljdGVkXG5cbiAgICBjb25zdCB0cmFuc2FjdGlvbiA9IGF3YWl0IHRNb2RlbC5maW5kT25lKHtfaWQ6IHRpZH0pLmV4ZWMoKTtcbiAgICBpZiAoIXRyYW5zYWN0aW9uKSB0aHJvdyBuZXcgRXJyb3IoJ1RoZXJlIGlzIG5vIHRyYW5zYWN0aW9uIGhpc3RvcnkuJyk7XG5cbiAgICBkZWJ1ZygnZmluZCBUcmFuc2FjdGlvbiAnLCB0cmFuc2FjdGlvbiwgdHJhbnNhY3Rpb24uc3RhdGUpO1xuICAgIHN3aXRjaCAodHJhbnNhY3Rpb24uc3RhdGUpIHtcbiAgICAgIGNhc2UgJ2luaXQnOlxuICAgICAgICB0aGlzLmNhbmNlbFRyYW5zYWN0aW9uKHRNb2RlbCwgdGlkKTtcbiAgICAgICAgcmV0dXJuIHRpZDtcblxuICAgICAgY2FzZSAncGVuZGluZyc6XG4gICAgICAgIGF3YWl0IHRoaXMucmVjb21taXRUcmFuc2FjdGlvbih0TW9kZWwsIHRyYW5zYWN0aW9uKTtcbiAgICAgICAgcmV0dXJuIHsnJGV4aXN0cyc6IGZhbHNlfTtcblxuICAgICAgY2FzZSAnY29tbWl0dGVkJzpcbiAgICAgICAgZGVidWcoJ2FscmVhZHkgY29tbWl0dGVkLiBpZ25vcmUgX190Jyk7XG4gICAgICAgIHJldHVybiB0aWQ7XG5cbiAgICAgIGNhc2UgJ2NhbmNlbGVkJzpcbiAgICAgICAgLy8gVE9ETyA6ICDruYTsoJXsg4Eg7IOB7YOc66GcIOyduO2VmOyXrCBjYW5jZWxlZOqwgCDrgqjripQg6rK97JqwIHRyYW5zY2F0aW9uIGRvY3VtZW5064qUIOygnOqxsCDrkJjsp4Ag7JWK6rOgIO2YhOyerCDrgqjqsqjsoLgg7J6I64ukLlxuICAgICAgICBkZWJ1ZygnYWxyZWFkeSBjYW5jZWxlZC4gaWdub3JlIF9fdCcpO1xuICAgICAgICByZXR1cm4gdGlkO1xuICAgIH1cbiAgfVxuXG4gIGlzVXBkYXRlU3VjY2Vzc2Z1bGx5KHJhd1Jlc3BvbnNlKSB7XG4gICAgcmV0dXJuIHJhd1Jlc3BvbnNlLm4gPiAwICYmIHJhd1Jlc3BvbnNlLm5Nb2RpZmllZCA+IDAgJiYgcmF3UmVzcG9uc2UubiA9PT0gcmF3UmVzcG9uc2Uubk1vZGlmaWVkO1xuICB9XG5cbiAgYXN5bmMgdXBkYXRlKHF1ZXJ5KSB7XG4gICAgY29uc3QgcmF3UmVzcG9uc2UgPSBhd2FpdCB0aGlzLm1vZGVsLnVwZGF0ZSh0aGlzLl9jb25kaXRpb25zLCBxdWVyeSwge1xuICAgICAgZm9yY2U6IHRydWUsXG4gICAgICB3OiAxXG4gICAgfSkuZXhlYygpO1xuICAgIGRlYnVnKCdyYXdSZXNwb25zZTogJW8nLCByYXdSZXNwb25zZSk7XG4gICAgaWYgKCF0aGlzLmlzVXBkYXRlU3VjY2Vzc2Z1bGx5KHJhd1Jlc3BvbnNlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCd3cml0ZSBsb2NrJyk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgZ2V0TWluaW1hbERvYyhjb25kaXRpb25zKSB7XG4gICAgcmV0dXJuIHRoaXMubW9kZWwuZmluZE9uZShjb25kaXRpb25zLCB7X2lkOiAxLCBfX3Q6IDF9KS5leGVjKCk7XG4gIH1cblxuICBhc3luYyBydW4oKSB7XG4gICAgaWYgKCF0aGlzLm9wdGlvbnMudHJhbnNhY3Rpb24pIHJldHVybjtcbiAgICBpZiAodGhpcy5vcHRpb25zLnRyYW5zYWN0aW9uICYmICF0aGlzLm9wdGlvbnMuX190KSB0aHJvdyBuZXcgRXJyb3IoJ0NhbGxlZCBgZmluZE9uZWAgb3V0c2lkZSBvZiBhIHRyYW5zYWN0aW9uJyk7XG5cbiAgICBkZWJ1ZygncHJlLWZpbmRPbmUnKTtcbiAgICBkZWJ1Zygnb3B0aW9uczogJW8nLCBfLm9taXQodGhpcy5vcHRpb25zLCAndE1vZGVsJykpO1xuICAgIGRlYnVnKCdjb25kaXRpb25zOiAlbycsIHRoaXMuX2NvbmRpdGlvbnMpO1xuXG4gICAgY29uc3QgZG9jID0gYXdhaXQgdGhpcy5nZXRNaW5pbWFsRG9jKHRoaXMuX2NvbmRpdGlvbnMpO1xuICAgIGlmICghZG9jKSByZXR1cm47XG4gICAgZGVidWcoJ2RvY3VtZW50IGZvdW5kISB0IDogJywgZG9jLl9fdCwgJywgaWQgOiAnLCBkb2MuaWQpO1xuXG4gICAgaWYgKHRoaXMuY2hlY2tJblNhbWVUcmFuc2FjdGlvbih0aGlzLm9wdGlvbnMuX190LCBkb2MuX190KSkgcmV0dXJuO1xuXG4gICAgdGhpcy5fY29uZGl0aW9uc1snX2lkJ10gPSBkb2MuX2lkO1xuICAgIHRoaXMuX2NvbmRpdGlvbnNbJ19fdCddID0geyRleGlzdHM6IGZhbHNlfTtcbiAgICBpZiAoZG9jLl9fdCkge1xuICAgICAgdGhpcy5fY29uZGl0aW9uc1snX190J10gPSBhd2FpdCB0aGlzLnJlc29sdmVQcmV2aW91c1RyYW5zYWN0aW9uKGRvYy5fX3QpO1xuICAgIH1cbiAgICBkZWJ1ZygnY29uZGl0aW9ucyBhcmUgbW9kaWZpZWQnLCB0aGlzLl9jb25kaXRpb25zKTtcblxuICAgIGNvbnN0IHVwZGF0ZVF1ZXJ5ID0ge19fdDogdGhpcy5vcHRpb25zLl9fdH07XG4gICAgZGVidWcoJ3VwZGF0ZSBxdWVyeSBpcyBtb2RpZmllZCAlbycsIHVwZGF0ZVF1ZXJ5KTtcbiAgICBhd2FpdCB0aGlzLnVwZGF0ZSh1cGRhdGVRdWVyeSk7XG5cbiAgICBkZWJ1ZygnbG9ja2luZyBzdWNjZXNzJyk7XG4gICAgZGVsZXRlIHRoaXMuX2NvbmRpdGlvbnNbJ19fdCddO1xuICAgIGRlYnVnKCdyb2xsYmFjayBjb25kaXRpb25zJywgdGhpcy5fY29uZGl0aW9ucyk7XG4gICAgaWYgKHRoaXMuX2ZpZWxkcykgdGhpcy5fZmllbGRzWydfX3QnXSA9IDE7XG4gIH1cbn1cblxuZnVuY3Rpb24gZW5zdXJlTm9VbmlxdWUoc2NoZW1hKSB7XG4gIGNvbnN0IGluZGV4ZXMgPSBzY2hlbWEuaW5kZXhlcygpO1xuICBjb25zdCBzaGFyZEtleSA9IHNjaGVtYS5vcHRpb25zLnNoYXJkS2V5ICYmIE9iamVjdC5rZXlzKHNjaGVtYS5vcHRpb25zLnNoYXJkS2V5KVswXTtcbiAgaW5kZXhlcy5mb3JFYWNoKChbbmFtZSwgb3B0aW9uc10pID0+IHtcbiAgICBpZiAob3B0aW9ucyAmJiBvcHRpb25zLnVuaXF1ZSkge1xuICAgICAgaWYgKHR5cGVvZiBuYW1lID09PSAnb2JqZWN0Jykge1xuICAgICAgICBuYW1lID0gT2JqZWN0LmtleXMobmFtZSlbMF07XG4gICAgICB9XG4gICAgICBpZiAobmFtZSAhPT0gc2hhcmRLZXkpXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgVHJhbnNhY3Rpb24gZG9lc24ndCBzdXBwb3J0IGFuIHVuaXF1ZSBpbmRleCAoJHtuYW1lfSkgc2hhcmRLZXkgKCR7c2hhcmRLZXl9KWApO1xuICAgIH1cbiAgfSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwbHVnaW4oc2NoZW1hOiBtb25nb29zZS5TY2hlbWEsIG9wdGlvbnM/OiBPYmplY3QpIHtcbiAgZW5zdXJlTm9VbmlxdWUoc2NoZW1hKTtcblxuICBzY2hlbWEuYWRkKHtcbiAgICBfX3Q6IHsgdHlwZTogbW9uZ29vc2UuU2NoZW1hLlR5cGVzLk9iamVjdElkIH0sXG4gICAgLy8gX19uZXc6IHsgdHlwZTogQm9vbGVhbiwgZGVmYXVsdDogdHJ1ZSB9XG4gIH0pO1xuXG4gIGNvbnN0IG9sZEluZGV4TWV0aG9kOiBhbnkgPSBzY2hlbWEuaW5kZXg7XG4gIHNjaGVtYS5pbmRleCA9IGZ1bmN0aW9uICguLi5hcmdzKSB7XG4gICAgY29uc3QgcmVzID0gb2xkSW5kZXhNZXRob2QuYXBwbHkodGhpcywgYXJncyk7XG4gICAgZW5zdXJlTm9VbmlxdWUoc2NoZW1hKTtcbiAgICByZXR1cm4gcmVzO1xuICB9O1xuXG4gIHNjaGVtYS5wcmUoJ3NhdmUnLCBmdW5jdGlvbihuZXh0KSB7XG4gICAgZGVidWcoJ3ByZS1zYXZlJyk7XG4gICAgZGVidWcoJ2NoZWNraW5nIF9fdCBmaWVsZDogJywgdGhpcy5fX3QpO1xuICAgIGlmICghdGhpcy5fX3QgJiYgIXRoaXMuaXNOZXcpIHJldHVybiBuZXh0KG5ldyBFcnJvcignWW91IGNhblxcJyBub3Qgc2F2ZSB3aXRob3V0IGxvY2snKSk7XG5cbiAgICAvLyBUT0RPOlxuICAgIC8vIGlmICh0aGlzLl9fdC50b1N0cmluZygpLnN1YnN0cmluZyg4LCAxOCkgIT09ICcwMDAwMDAwMDAwJykgcmV0dXJuIG5leHQobmV3IEVycm9yKCcnKSk7XG4gICAgdGhpcy5fX3QgPSB1bmRlZmluZWQ7XG4gICAgbmV4dCgpO1xuICB9KTtcblxuICBzY2hlbWEucHJlKCdmaW5kT25lJywgYXN5bmMgZnVuY3Rpb24gKG5leHQpIHtcbiAgICBjb25zdCBvID0gbmV3IFByZUZpbmRPbmUodGhpcyk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IG8ucnVuKCk7XG4gICAgICBuZXh0KCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgbmV4dChlKTtcbiAgICB9XG4gIH0pO1xufVxuIl19