UNPKG

mongoose_record_replay

Version:

readonly mongoose drop in for testing (replaying w.o. mongo db) and recording (running against mongodb)

367 lines (364 loc) 47 kB
"use strict"; /** * instrument mongoose to record/replay queries (!! only queries so far) * * allows to run (mongoose read only) unit tests w.o. a mongoose instance * * @file */ Object.defineProperty(exports, "__esModule", { value: true }); exports.instrumentMongoose = exports.instrumentModelReplay = exports.instrumentModelRecord = exports.retrieveOp = exports.recordOp = exports.digestArgs = exports.instrumentModel = exports.JSONStringify = exports.JSONParse = void 0; const debugf = require("debugf"); var debuglog = debugf('mongoose_record_replay'); const path = require('path'); const process = require("process"); const mongoose = require("mongoose"); const events = require("events"); const fs = require("fs"); const crypto = require('crypto'); /** * The recording path, set via argument * or */ function JSONParse(text) { function customDeSer(key, value) { if (value.toString().indexOf("__REGEXP ") == 0) { var m = value.split("__REGEXP ")[1].match(/\/(.*)\/(.*)?/); return new RegExp(m[1], m[2] || ""); } else return value; } return JSON.parse(text, customDeSer); } exports.JSONParse = JSONParse; function JSONStringify(obj) { function customSer(key, value) { if (value instanceof RegExp) { return ("__REGEXP " + value.toString()); } else return value; } return JSON.stringify(obj, customSer, 2); } exports.JSONStringify = JSONStringify; function readFileAsJSON(filename) { try { var data = fs.readFileSync(filename, 'utf-8'); return JSON.parse(data); } catch (e) { console.log("Content of file " + filename + " is no json" + e); throw new Error("Content of file " + filename + " is no json" + e); } return undefined; } function assurePath(path) { try { fs.mkdirSync(path); } catch (e) { } try { fs.mkdirSync(path + 'data'); } catch (e) { } } var dbEmitter = new events.EventEmitter(); // unit test invoke this multiple times, avoid node js warning dbEmitter.setMaxListeners(0); function instrumentModel(model, recordingPath, theMode) { if (theMode === "RECORD") { instrumentModelRecord(model, recordingPath, theMode); } else if (theMode === "REPLAY") { // todo instrumentModelReplay(model, recordingPath); } return model; } exports.instrumentModel = instrumentModel; function makeFileName(digest, recordingPath) { return (recordingPath + 'data/' + digest + '.json'); } function digestArgs(op, name, query) { var md5sum = crypto.createHash('md5'); debuglog('here the name ' + name); md5sum.update(op + name + JSONStringify(query)); var digest = '' + md5sum.digest('hex'); return digest; } exports.digestArgs = digestArgs; function recordOp(op, name, query, res, recordingPath) { var digest = digestArgs(op, name, query); var resStr = JSON.stringify(res, undefined, 2); var len = 0; if (res && Array.isArray(res)) { len = res.length; } else { len = resStr.length; } var filename = makeFileName(digest, recordingPath); console.log('recording to file: ' + filename + ' (' + path.normalize(filename) + ')...'); fs.writeFileSync(filename, resStr); var known = {}; try { known = readFileAsJSON(recordingPath + 'queries.json'); } catch (ex) { } known[digest] = { op: op, name: name, digest: digest, query: query, res: len }; fs.writeFileSync(recordingPath + 'queries.json', JSONStringify(known)); } exports.recordOp = recordOp; function retrieveOp(op, name, query, recordingPath) { var digest = digestArgs(op, name, query); var filename = makeFileName(digest, recordingPath); debuglog(' reading from filename ' + filename); try { var res = readFileAsJSON(filename); } catch (e) { console.log(e); console.log(e.stack); console.log(`did not find query result recording (${filename}) \n for collection ${name} operation ${op} \n query arguments: ` + JSONStringify(query)); throw e; } if (res === undefined) { debuglog('empty result for query ' + op + ' ' + JSON.stringify(query, undefined, 2) + '\n' + filename); } return res; } exports.retrieveOp = retrieveOp; function instrumentModelRecord(modelDoc, recordingPath, theMode) { debuglog('mongoose_record_replay is instrumenting model ' + modelDoc.modelName + ' for recording to ' + recordingPath); var oFind = modelDoc.find; modelDoc.find = function () { debuglog('someone is calling find with ' + modelDoc.modelName + JSON.stringify(arguments, undefined, 2)); var res = oFind.apply(modelDoc, arguments); if (arguments.length !== 1) { throw Error('expected one argument in find, was ' + arguments.length); } var query = arguments[0]; res.lean().exec().then((a) => { //console.log("here result1 + " + JSON.stringify(a, undefined,2) ); recordOp("find", modelDoc.modelName, query, a, recordingPath); }); return res; }; var oDistinct = modelDoc.distinct; modelDoc.distinct = function () { debuglog('someone is calling distinct with' + JSON.stringify(arguments, undefined, 2)); var res = oDistinct.apply(modelDoc, arguments); if (arguments.length !== 1) { throw Error('expected one argument ' + JSON.stringify(arguments)); } var query = arguments[0]; var res2 = res.then((a) => { debuglog(() => "here result1 + " + JSON.stringify(a, undefined, 2)); try { recordOp("distinct", modelDoc.modelName, query, a, recordingPath); } catch (ex) { console.log(' recording to file failed ' + ex); debuglog(() => " recording to file failed " + ex); throw ex; } return a; }); return res; //res2.then((b) => { console.log(' 2nd promise then ' + b && b.length); return b; }); }; var oAggregate = modelDoc.aggregate; modelDoc.aggregate = function () { debuglog(() => 'someone is calling aggregate with' + JSON.stringify(arguments, undefined, 2)); var query = Array.prototype.slice.call(arguments); var res = oAggregate.apply(modelDoc, arguments); res.then((a) => { debuglog(() => "here result1 + " + JSON.stringify(a, undefined, 2)); recordOp("aggregate", modelDoc.modelName, query, a, recordingPath); }); return res; }; } exports.instrumentModelRecord = instrumentModelRecord; function instrumentModelReplay(modelDoc, recordingPath) { debuglog('instrumenting model ' + modelDoc.modelName + ' for replay from path ' + recordingPath); var oFind = modelDoc.find; modelDoc.find = function () { debuglog(() => 'someone is replaying find with' + JSON.stringify(arguments, undefined, 2)); var query = arguments[0]; var res = retrieveOp("find", modelDoc.modelName, query, recordingPath); debuglog(() => 'returning res ' + JSON.stringify(res) + ' for query find' + query); return { lean: function () { return { exec: function () { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(res); }, 0); }); } }; } }; }; var oDistinct = modelDoc.distinct; modelDoc.distinct = function () { debuglog('someone is replaying distinct with' + JSON.stringify(arguments, undefined, 2)); var query = arguments[0]; var res = retrieveOp("distinct", modelDoc.modelName, query, recordingPath); debuglog('returning res ' + JSON.stringify(res) + ' for query find' + query); return new Promise(function (resolve, reject) { setTimeout(function () { resolve(res); }, 0); }); }; var oAggregate = modelDoc.aggregate; modelDoc.aggregate = function () { debuglog('someone is replaying aggregate with' + JSON.stringify(arguments, undefined, 2)); var query = Array.prototype.slice.call(arguments); var res = retrieveOp("aggregate", modelDoc.modelName, query, recordingPath); var p = new Promise(function (resolve, reject) { setTimeout(function () { resolve(res); }, 0); }); p.exec = function () { return p; }; return p; }; } exports.instrumentModelReplay = instrumentModelReplay; /** * funtion to instrument mongoose * * * * @param mongoose a real mongoose instance * @param [path] {string} optional, a path to write/read files from, defaults to "mgrecrep/" * @param mode {string} undefined (environment value) or "REPLAY" or "RECORD" */ function instrumentMongoose(mongoose, path, mode) { debuglog(' instrument mongoose with ' + path + " " + mode); var theMode = mode || process.env.MONGO_RECORD_REPLAY; if (theMode && ["REPLAY", "RECORD"].indexOf(mode) < 0) { console.log('passed mode value or env MONGO_RECORD_REPLAY may only be "RECORD" or "REPLAY" , MONGO_RECORD MONGO_REPLAY'); throw new Error('mongoose_record_replay mode should be one of "REPLAY", "RECORD" was ' + theMode); } if (theMode === "RECORD") { var recordingPath = path || process.env.MONGO_RECORD_REPLAY_PATH || "mongoose_record_replay"; console.log('!* mode RECORD to path ' + recordingPath + ' in ' + __dirname + " " + mode); assurePath(recordingPath); var omodel = mongoose.model; mongoose.model = function () { if (arguments.length > 1) { return instrumentModel(omodel.apply(mongoose, arguments), recordingPath, theMode); } return omodel.apply(mongoose, arguments); }; return mongoose; } else if (theMode === "REPLAY") { recordingPath = path || process.env.MONGO_RECORD_REPLAY_PATH || "mongoose_record_replay"; console.log('!* mode REPLAY from path ' + recordingPath + ' in ' + __dirname + " " + mode + " " + path); var r = makeMongooseMock(recordingPath, theMode); return r; } return mongoose; } exports.instrumentMongoose = instrumentMongoose; var mocksPerPath = {}; function makeMongooseMock(recordingPath, theMode) { if (mocksPerPath[recordingPath] == undefined) { var res = { models: {}, recordingPath: recordingPath, theMode: theMode, modelNames: function () { return Object.keys(this.models); }, Schema: mongoose.Schema, model: function (a, b) { if (b === undefined) { return this.models[a]; } debuglog('creating model ' + a + ' at mock'); this.models[a] = instrumentModel({ find: function () { }, aggregate: function () { }, distinct: function () { }, modelName: a, schema: b, }, this.recordingPath, this.theMode); return this.models[a]; }, disconnect: function () { debuglog('simulationg disconnect '); }, connect: function (connStr) { // this.db.on.emit('on'); debuglog('simulationg connecting to ' + connStr); if (!this._once) { var that = this; setTimeout(function () { that.connection.emit('open'); debuglog('fired emit'); }, 0); } }, connection: dbEmitter }; mocksPerPath[recordingPath] = res; } return mocksPerPath[recordingPath]; } /*export*/ /*var mongooseMock2 = { models: {}, recordingPath : "", theMode : "", modelNames: function () { return Object.keys(this.models); }, Schema: mongoose.Schema, model: function (a, b) { if (b === undefined) { return this.models[a]; } debuglog('creating model ' + a + ' at mock'); this.models[a] = instrumentModel({ find: function () { }, aggregate: function () { }, distinct: function () { }, modelName: a, schema: b, } as any, this.recordingPath, this.theMode); return this.models[a]; }, disconnect: function () { debuglog('simulationg disconnect '); }, connect: function (connStr: string) { // this.db.on.emit('on'); debuglog('simulationg connecting to ' + connStr); if (!this._once) { var that = this; setTimeout(function () { that.connection.emit('open'); debuglog('fired emit'); }, 0); } }, // e.g. set('useCreateIndex',true) set : function(a,b) {}, connection: dbEmitter }; */ //# sourceMappingURL=data:application/json;charset=utf8;base64,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ZmlsZSI6Im1ncmVjcmVwLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIGluc3RydW1lbnQgbW9uZ29vc2UgdG8gcmVjb3JkL3JlcGxheSBxdWVyaWVzICghISBvbmx5IHF1ZXJpZXMgc28gZmFyKVxyXG4gKlxyXG4gKiBhbGxvd3MgdG8gcnVuIChtb25nb29zZSByZWFkIG9ubHkpIHVuaXQgdGVzdHMgdy5vLiBhIG1vbmdvb3NlIGluc3RhbmNlXHJcbiAqXHJcbiAqIEBmaWxlXHJcbiAqL1xyXG5cclxuaW1wb3J0ICogYXMgZGVidWdmIGZyb20gJ2RlYnVnZic7XHJcblxyXG52YXIgZGVidWdsb2cgPSBkZWJ1Z2YoJ21vbmdvb3NlX3JlY29yZF9yZXBsYXknKTtcclxuXHJcbmNvbnN0IHBhdGggPSByZXF1aXJlKCdwYXRoJyk7XHJcbmltcG9ydCAqIGFzIHByb2Nlc3MgZnJvbSAncHJvY2Vzcyc7XHJcbmltcG9ydCAqIGFzIG1vbmdvb3NlIGZyb20gJ21vbmdvb3NlJztcclxuaW1wb3J0ICogYXMgZXZlbnRzIGZyb20gJ2V2ZW50cyc7XHJcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcclxuY29uc3QgY3J5cHRvID0gcmVxdWlyZSgnY3J5cHRvJyk7XHJcblxyXG4vKipcclxuICogVGhlIHJlY29yZGluZyBwYXRoLCBzZXQgdmlhIGFyZ3VtZW50XHJcbiAqIG9yXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gSlNPTlBhcnNlKHRleHQ6IHN0cmluZyk6IGFueSB7XHJcbiAgICBmdW5jdGlvbiBjdXN0b21EZVNlcihrZXksIHZhbHVlKSB7XHJcbiAgICAgICAgaWYgKHZhbHVlLnRvU3RyaW5nKCkuaW5kZXhPZihcIl9fUkVHRVhQIFwiKSA9PSAwKSB7XHJcbiAgICAgICAgICAgIHZhciBtID0gdmFsdWUuc3BsaXQoXCJfX1JFR0VYUCBcIilbMV0ubWF0Y2goL1xcLyguKilcXC8oLiopPy8pO1xyXG4gICAgICAgICAgICByZXR1cm4gbmV3IFJlZ0V4cChtWzFdLCBtWzJdIHx8IFwiXCIpO1xyXG4gICAgICAgIH0gZWxzZVxyXG4gICAgICAgICAgICByZXR1cm4gdmFsdWU7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gSlNPTi5wYXJzZSh0ZXh0LCBjdXN0b21EZVNlcik7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBKU09OU3RyaW5naWZ5KG9iajogYW55KTogc3RyaW5nIHtcclxuICAgIGZ1bmN0aW9uIGN1c3RvbVNlcihrZXksIHZhbHVlKSB7XHJcbiAgICAgICAgaWYgKHZhbHVlIGluc3RhbmNlb2YgUmVnRXhwKXtcclxuICAgICAgICAgICAgcmV0dXJuIChcIl9fUkVHRVhQIFwiICsgdmFsdWUudG9TdHJpbmcoKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2VcclxuICAgICAgICAgICAgcmV0dXJuIHZhbHVlO1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KG9iaiwgY3VzdG9tU2VyLCAyKTtcclxufVxyXG5cclxuZnVuY3Rpb24gcmVhZEZpbGVBc0pTT04oZmlsZW5hbWU6IHN0cmluZyk6IGFueSB7XHJcbiAgICB0cnkge1xyXG4gICAgICAgIHZhciBkYXRhID0gZnMucmVhZEZpbGVTeW5jKGZpbGVuYW1lLCAndXRmLTgnKTtcclxuICAgICAgICByZXR1cm4gSlNPTi5wYXJzZShkYXRhKTtcclxuICAgIH0gY2F0Y2ggKGUpIHtcclxuICAgICAgICBjb25zb2xlLmxvZyhcIkNvbnRlbnQgb2YgZmlsZSBcIiArIGZpbGVuYW1lICsgXCIgaXMgbm8ganNvblwiICsgZSk7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiQ29udGVudCBvZiBmaWxlIFwiICsgZmlsZW5hbWUgKyBcIiBpcyBubyBqc29uXCIgKyBlKTtcclxuICAgIH1cclxuICAgIHJldHVybiB1bmRlZmluZWQ7XHJcbn1cclxuXHJcblxyXG5mdW5jdGlvbiBhc3N1cmVQYXRoKHBhdGg6IHN0cmluZykge1xyXG4gICAgdHJ5IHtcclxuICAgICAgICBmcy5ta2RpclN5bmMocGF0aCk7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcblxyXG4gICAgfVxyXG4gICAgdHJ5IHtcclxuICAgICAgICBmcy5ta2RpclN5bmMocGF0aCArICdkYXRhJyk7XHJcbiAgICB9IGNhdGNoIChlKSB7XHJcblxyXG4gICAgfVxyXG59XHJcblxyXG52YXIgZGJFbWl0dGVyID0gbmV3IGV2ZW50cy5FdmVudEVtaXR0ZXIoKTtcclxuLy8gdW5pdCB0ZXN0IGludm9rZSB0aGlzIG11bHRpcGxlIHRpbWVzLCBhdm9pZCBub2RlIGpzIHdhcm5pbmdcclxuZGJFbWl0dGVyLnNldE1heExpc3RlbmVycygwKTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBpbnN0cnVtZW50TW9kZWwobW9kZWw6IG1vbmdvb3NlLk1vZGVsPGFueT4sIHJlY29yZGluZ1BhdGggOiBzdHJpbmcsIHRoZU1vZGU6IHN0cmluZykge1xyXG4gICAgaWYgKHRoZU1vZGUgPT09IFwiUkVDT1JEXCIpIHtcclxuICAgICAgICBpbnN0cnVtZW50TW9kZWxSZWNvcmQobW9kZWwsIHJlY29yZGluZ1BhdGgsIHRoZU1vZGUpO1xyXG4gICAgfSBlbHNlIGlmICh0aGVNb2RlID09PSBcIlJFUExBWVwiKSB7XHJcbiAgICAgICAgLy8gdG9kb1xyXG4gICAgICAgIGluc3RydW1lbnRNb2RlbFJlcGxheShtb2RlbCwgcmVjb3JkaW5nUGF0aCk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gbW9kZWw7XHJcbn1cclxuXHJcblxyXG5mdW5jdGlvbiBtYWtlRmlsZU5hbWUoZGlnZXN0LCByZWNvcmRpbmdQYXRoOiBzdHJpbmcpIHtcclxuICAgIHJldHVybiAocmVjb3JkaW5nUGF0aCArICdkYXRhLycgKyBkaWdlc3QgKyAnLmpzb24nKTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGRpZ2VzdEFyZ3Mob3A6IHN0cmluZywgbmFtZSA6IHN0cmluZywgcXVlcnkgOiBhbnkpIHtcclxuICAgIHZhciBtZDVzdW0gPSBjcnlwdG8uY3JlYXRlSGFzaCgnbWQ1Jyk7XHJcbiAgICBkZWJ1Z2xvZygnaGVyZSB0aGUgbmFtZSAnICsgbmFtZSk7XHJcbiAgICBtZDVzdW0udXBkYXRlKG9wICsgbmFtZSArIEpTT05TdHJpbmdpZnkocXVlcnkpKTtcclxuICAgIHZhciBkaWdlc3QgPSAnJyArIG1kNXN1bS5kaWdlc3QoJ2hleCcpO1xyXG4gICAgcmV0dXJuIGRpZ2VzdDtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHJlY29yZE9wKG9wOiBzdHJpbmcsIG5hbWU6IHN0cmluZywgcXVlcnk6IGFueSwgcmVzOiBhbnksIHJlY29yZGluZ1BhdGggOiBzdHJpbmcpIHtcclxuICAgIHZhciBkaWdlc3QgPSBkaWdlc3RBcmdzKG9wLG5hbWUscXVlcnkpO1xyXG4gICAgdmFyIHJlc1N0ciA9IEpTT04uc3RyaW5naWZ5KHJlcywgdW5kZWZpbmVkLCAyKTtcclxuICAgIHZhciBsZW4gPSAwO1xyXG4gICAgaWYocmVzICYmIEFycmF5LmlzQXJyYXkocmVzKSkge1xyXG4gICAgICAgIGxlbiA9IHJlcy5sZW5ndGg7XHJcbiAgICB9IGVsc2Uge1xyXG4gICAgICAgIGxlbiA9IHJlc1N0ci5sZW5ndGg7XHJcbiAgICB9XHJcbiAgICB2YXIgZmlsZW5hbWUgPSBtYWtlRmlsZU5hbWUoZGlnZXN0LCByZWNvcmRpbmdQYXRoKTtcclxuICAgIGNvbnNvbGUubG9nKCAncmVjb3JkaW5nIHRvIGZpbGU6ICcgKyBmaWxlbmFtZSArICcgKCcgKyBwYXRoLm5vcm1hbGl6ZShmaWxlbmFtZSkgKyAnKS4uLicpO1xyXG4gICAgZnMud3JpdGVGaWxlU3luYyhmaWxlbmFtZSwgcmVzU3RyKTtcclxuICAgIHZhciBrbm93biA9IHt9O1xyXG4gICAgdHJ5IHtcclxuICAgICAgICBrbm93biA9IHJlYWRGaWxlQXNKU09OKHJlY29yZGluZ1BhdGggKyAncXVlcmllcy5qc29uJyk7XHJcbiAgICB9IGNhdGNoIChleCkge1xyXG5cclxuICAgIH1cclxuICAgIGtub3duW2RpZ2VzdF0gPSB7XHJcbiAgICAgICAgb3A6IG9wLFxyXG4gICAgICAgIG5hbWU6IG5hbWUsXHJcbiAgICAgICAgZGlnZXN0OiBkaWdlc3QsXHJcbiAgICAgICAgcXVlcnk6IHF1ZXJ5LFxyXG4gICAgICAgIHJlcyA6IGxlblxyXG4gICAgfTtcclxuICAgIGZzLndyaXRlRmlsZVN5bmMocmVjb3JkaW5nUGF0aCArICdxdWVyaWVzLmpzb24nLCBKU09OU3RyaW5naWZ5KGtub3duKSk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiByZXRyaWV2ZU9wKG9wOiBzdHJpbmcsIG5hbWU6IHN0cmluZywgcXVlcnk6IGFueSwgcmVjb3JkaW5nUGF0aCA6IHN0cmluZykge1xyXG4gICAgdmFyIGRpZ2VzdCA9IGRpZ2VzdEFyZ3Mob3AsbmFtZSwgcXVlcnkpO1xyXG4gICAgdmFyIGZpbGVuYW1lID0gbWFrZUZpbGVOYW1lKGRpZ2VzdCwgcmVjb3JkaW5nUGF0aCk7XHJcbiAgICBkZWJ1Z2xvZygnIHJlYWRpbmcgZnJvbSBmaWxlbmFtZSAnICsgZmlsZW5hbWUpO1xyXG4gICAgdHJ5IHtcclxuICAgICAgICB2YXIgcmVzID0gcmVhZEZpbGVBc0pTT04oZmlsZW5hbWUpO1xyXG4gICAgfSBjYXRjaChlKSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coZSk7XHJcbiAgICAgICAgY29uc29sZS5sb2coZS5zdGFjayk7XHJcbiAgICAgICAgY29uc29sZS5sb2coYGRpZCBub3QgZmluZCBxdWVyeSByZXN1bHQgcmVjb3JkaW5nICgke2ZpbGVuYW1lfSkgXFxuIGZvciBjb2xsZWN0aW9uICR7bmFtZX0gb3BlcmF0aW9uICR7b3B9IFxcbiBxdWVyeSBhcmd1bWVudHM6IGAgKyBKU09OU3RyaW5naWZ5KHF1ZXJ5KSk7XHJcbiAgICAgICAgdGhyb3cgZTtcclxuICAgIH1cclxuICAgIGlmIChyZXMgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIGRlYnVnbG9nKCdlbXB0eSByZXN1bHQgZm9yIHF1ZXJ5ICcgKyBvcCArICcgJyArIEpTT04uc3RyaW5naWZ5KHF1ZXJ5LCB1bmRlZmluZWQsIDIpICsgJ1xcbicgKyBmaWxlbmFtZSk7XHJcbiAgICB9XHJcbiAgICByZXR1cm4gcmVzO1xyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gaW5zdHJ1bWVudE1vZGVsUmVjb3JkKG1vZGVsRG9jOiBtb25nb29zZS5Nb2RlbDxhbnk+LCByZWNvcmRpbmdQYXRoOiBzdHJpbmcsIHRoZU1vZGU6IHN0cmluZykge1xyXG4gICAgZGVidWdsb2coJ21vbmdvb3NlX3JlY29yZF9yZXBsYXkgaXMgaW5zdHJ1bWVudGluZyBtb2RlbCAnICsgbW9kZWxEb2MubW9kZWxOYW1lICsgJyBmb3IgcmVjb3JkaW5nIHRvICcgKyByZWNvcmRpbmdQYXRoICk7XHJcbiAgICB2YXIgb0ZpbmQgPSBtb2RlbERvYy5maW5kO1xyXG4gICAgbW9kZWxEb2MuZmluZCA9IGZ1bmN0aW9uICgpOiBhbnkge1xyXG4gICAgICAgIGRlYnVnbG9nKCdzb21lb25lIGlzIGNhbGxpbmcgZmluZCB3aXRoICcgKyBtb2RlbERvYy5tb2RlbE5hbWUgKyBKU09OLnN0cmluZ2lmeShhcmd1bWVudHMsIHVuZGVmaW5lZCwgMikpO1xyXG4gICAgICAgIHZhciByZXMgPSBvRmluZC5hcHBseShtb2RlbERvYywgYXJndW1lbnRzKTtcclxuICAgICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCAhPT0gMSkge1xyXG4gICAgICAgICAgICB0aHJvdyBFcnJvcignZXhwZWN0ZWQgb25lIGFyZ3VtZW50IGluIGZpbmQsIHdhcyAnICsgYXJndW1lbnRzLmxlbmd0aCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHZhciBxdWVyeSA9IGFyZ3VtZW50c1swXTtcclxuICAgICAgICByZXMubGVhbigpLmV4ZWMoKS50aGVuKChhKSA9PiB7XHJcbiAgICAgICAgICAgIC8vY29uc29sZS5sb2coXCJoZXJlIHJlc3VsdDEgKyBcIiArIEpTT04uc3RyaW5naWZ5KGEsIHVuZGVmaW5lZCwyKSApO1xyXG4gICAgICAgICAgICByZWNvcmRPcChcImZpbmRcIiwgbW9kZWxEb2MubW9kZWxOYW1lLCBxdWVyeSwgYSwgcmVjb3JkaW5nUGF0aCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgICk7XHJcbiAgICAgICAgcmV0dXJuIHJlcztcclxuICAgIH1cclxuICAgIHZhciBvRGlzdGluY3QgPSBtb2RlbERvYy5kaXN0aW5jdDtcclxuICAgIG1vZGVsRG9jLmRpc3RpbmN0ID0gZnVuY3Rpb24gKCk6IGFueSB7XHJcbiAgICAgICAgZGVidWdsb2coJ3NvbWVvbmUgaXMgY2FsbGluZyBkaXN0aW5jdCB3aXRoJyArIEpTT04uc3RyaW5naWZ5KGFyZ3VtZW50cywgdW5kZWZpbmVkLCAyKSk7XHJcbiAgICAgICAgdmFyIHJlcyA9IG9EaXN0aW5jdC5hcHBseShtb2RlbERvYywgYXJndW1lbnRzKTtcclxuICAgICAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCAhPT0gMSkge1xyXG4gICAgICAgICAgICB0aHJvdyBFcnJvcignZXhwZWN0ZWQgb25lIGFyZ3VtZW50ICcgKyBKU09OLnN0cmluZ2lmeShhcmd1bWVudHMpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdmFyIHF1ZXJ5ID0gYXJndW1lbnRzWzBdO1xyXG4gICAgICAgIHZhciByZXMyID0gcmVzLnRoZW4oKGEpID0+IHtcclxuICAgICAgICAgICAgZGVidWdsb2coICgpID0+IFwiaGVyZSByZXN1bHQxICsgXCIgKyBKU09OLnN0cmluZ2lmeShhLCB1bmRlZmluZWQsMikgKTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHJlY29yZE9wKFwiZGlzdGluY3RcIiwgbW9kZWxEb2MubW9kZWxOYW1lLCBxdWVyeSwgYSwgcmVjb3JkaW5nUGF0aCk7XHJcbiAgICAgICAgICAgIH0gY2F0Y2goIGV4KVxyXG4gICAgICAgICAgICB7XHJcbiAgICAgICAgICAgICAgICBjb25zb2xlLmxvZyggJyByZWNvcmRpbmcgdG8gZmlsZSBmYWlsZWQgJyArIGV4ICk7XHJcbiAgICAgICAgICAgICAgICBkZWJ1Z2xvZyggKCkgPT4gXCIgcmVjb3JkaW5nIHRvIGZpbGUgZmFpbGVkIFwiICsgZXggKTtcclxuICAgICAgICAgICAgICAgIHRocm93IGV4O1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBhO1xyXG4gICAgICAgIH1cclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiByZXM7IC8vcmVzMi50aGVuKChiKSA9PiB7IGNvbnNvbGUubG9nKCcgMm5kIHByb21pc2UgdGhlbiAnICsgYiAmJiBiLmxlbmd0aCk7IHJldHVybiBiOyB9KTtcclxuICAgIH1cclxuICAgIHZhciBvQWdncmVnYXRlID0gbW9kZWxEb2MuYWdncmVnYXRlO1xyXG4gICAgbW9kZWxEb2MuYWdncmVnYXRlID0gZnVuY3Rpb24gKCk6IGFueSB7XHJcbiAgICAgICAgZGVidWdsb2coKCkgPT4gJ3NvbWVvbmUgaXMgY2FsbGluZyBhZ2dyZWdhdGUgd2l0aCcgKyBKU09OLnN0cmluZ2lmeShhcmd1bWVudHMsIHVuZGVmaW5lZCwgMikpO1xyXG4gICAgICAgIHZhciBxdWVyeSA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XHJcbiAgICAgICAgdmFyIHJlcyA9IG9BZ2dyZWdhdGUuYXBwbHkobW9kZWxEb2MsIGFyZ3VtZW50cyk7XHJcbiAgICAgICAgcmVzLnRoZW4oKGEpID0+IHtcclxuICAgICAgICAgICAgZGVidWdsb2coKCkgPT4gXCJoZXJlIHJlc3VsdDEgKyBcIiArIEpTT04uc3RyaW5naWZ5KGEsIHVuZGVmaW5lZCwgMikpO1xyXG4gICAgICAgICAgICByZWNvcmRPcChcImFnZ3JlZ2F0ZVwiLCBtb2RlbERvYy5tb2RlbE5hbWUsIHF1ZXJ5LCBhLCByZWNvcmRpbmdQYXRoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gcmVzO1xyXG4gICAgfVxyXG59XHJcblxyXG5leHBvcnQgZnVuY3Rpb24gaW5zdHJ1bWVudE1vZGVsUmVwbGF5KG1vZGVsRG9jOiBtb25nb29zZS5Nb2RlbDxhbnk+LCByZWNvcmRpbmdQYXRoOiBzdHJpbmcpIHtcclxuICAgIGRlYnVnbG9nKCdpbnN0cnVtZW50aW5nIG1vZGVsICcgKyBtb2RlbERvYy5tb2RlbE5hbWUgKyAnIGZvciByZXBsYXkgZnJvbSBwYXRoICcgKyByZWNvcmRpbmdQYXRoICk7XHJcbiAgICB2YXIgb0ZpbmQgPSBtb2RlbERvYy5maW5kO1xyXG4gICAgbW9kZWxEb2MuZmluZCA9IGZ1bmN0aW9uICgpOiBhbnkge1xyXG4gICAgICAgIGRlYnVnbG9nKCgpID0+ICdzb21lb25lIGlzIHJlcGxheWluZyBmaW5kIHdpdGgnICsgSlNPTi5zdHJpbmdpZnkoYXJndW1lbnRzLCB1bmRlZmluZWQsIDIpKTtcclxuICAgICAgICB2YXIgcXVlcnkgPSBhcmd1bWVudHNbMF07XHJcbiAgICAgICAgdmFyIHJlcyA9IHJldHJpZXZlT3AoXCJmaW5kXCIsIG1vZGVsRG9jLm1vZGVsTmFtZSwgcXVlcnksIHJlY29yZGluZ1BhdGgpO1xyXG4gICAgICAgIGRlYnVnbG9nKCgpID0+ICdyZXR1cm5pbmcgcmVzICcgKyBKU09OLnN0cmluZ2lmeShyZXMpICsgJyBmb3IgcXVlcnkgZmluZCcgKyBxdWVyeSk7XHJcbiAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgbGVhbjogZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgICAgICBleGVjOiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKHJlcyk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCAwKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgdmFyIG9EaXN0aW5jdCA9IG1vZGVsRG9jLmRpc3RpbmN0O1xyXG4gICAgbW9kZWxEb2MuZGlzdGluY3QgPSBmdW5jdGlvbiAoKTogYW55IHtcclxuICAgICAgICBkZWJ1Z2xvZygnc29tZW9uZSBpcyByZXBsYXlpbmcgZGlzdGluY3Qgd2l0aCcgKyBKU09OLnN0cmluZ2lmeShhcmd1bWVudHMsIHVuZGVmaW5lZCwgMikpO1xyXG4gICAgICAgIHZhciBxdWVyeSA9IGFyZ3VtZW50c1swXTtcclxuICAgICAgICB2YXIgcmVzID0gcmV0cmlldmVPcChcImRpc3RpbmN0XCIsIG1vZGVsRG9jLm1vZGVsTmFtZSwgcXVlcnksIHJlY29yZGluZ1BhdGgpO1xyXG4gICAgICAgIGRlYnVnbG9nKCdyZXR1cm5pbmcgcmVzICcgKyBKU09OLnN0cmluZ2lmeShyZXMpICsgJyBmb3IgcXVlcnkgZmluZCcgKyBxdWVyeSk7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKGZ1bmN0aW9uIChyZXNvbHZlLCByZWplY3QpIHtcclxuICAgICAgICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7IHJlc29sdmUocmVzKTsgfSwgMCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICB2YXIgb0FnZ3JlZ2F0ZSA9IG1vZGVsRG9jLmFnZ3JlZ2F0ZTtcclxuICAgIG1vZGVsRG9jLmFnZ3JlZ2F0ZSA9IGZ1bmN0aW9uICgpOiBhbnkge1xyXG4gICAgICAgIGRlYnVnbG9nKCdzb21lb25lIGlzIHJlcGxheWluZyBhZ2dyZWdhdGUgd2l0aCcgKyBKU09OLnN0cmluZ2lmeShhcmd1bWVudHMsIHVuZGVmaW5lZCwgMikpO1xyXG4gICAgICAgIHZhciBxdWVyeSA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XHJcbiAgICAgICAgdmFyIHJlcyA9IHJldHJpZXZlT3AoXCJhZ2dyZWdhdGVcIiwgbW9kZWxEb2MubW9kZWxOYW1lLCBxdWVyeSwgcmVjb3JkaW5nUGF0aCk7XHJcbiAgICAgICAgdmFyIHAgPSBuZXcgUHJvbWlzZShmdW5jdGlvbiAocmVzb2x2ZSwgcmVqZWN0KSB7XHJcbiAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkgeyByZXNvbHZlKHJlcyk7IH0sIDApO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIChwIGFzIGFueSkuZXhlYyA9IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHA7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBwO1xyXG4gICAgfVxyXG59XHJcblxyXG4vKipcclxuICogZnVudGlvbiB0byBpbnN0cnVtZW50IG1vbmdvb3NlXHJcbiAqXHJcbiAqXHJcbiAqXHJcbiAqIEBwYXJhbSBtb25nb29zZSBhIHJlYWwgbW9uZ29vc2UgaW5zdGFuY2VcclxuICogQHBhcmFtIFtwYXRoXSB7c3RyaW5nfSBvcHRpb25hbCwgYSBwYXRoIHRvIHdyaXRlL3JlYWQgZmlsZXMgZnJvbSwgZGVmYXVsdHMgdG8gXCJtZ3JlY3JlcC9cIlxyXG4gKiBAcGFyYW0gbW9kZSB7c3RyaW5nfSAgdW5kZWZpbmVkIChlbnZpcm9ubWVudCB2YWx1ZSkgb3IgXCJSRVBMQVlcIiBvciBcIlJFQ09SRFwiXHJcbiAqL1xyXG5leHBvcnQgZnVuY3Rpb24gaW5zdHJ1bWVudE1vbmdvb3NlKG1vbmdvb3NlOiBtb25nb29zZS5Nb25nb29zZSwgcGF0aDogc3RyaW5nLCBtb2RlPzogc3RyaW5nKTogbW9uZ29vc2UuTW9uZ29vc2Uge1xyXG4gICAgZGVidWdsb2coJyBpbnN0cnVtZW50IG1vbmdvb3NlIHdpdGggICcgKyBwYXRoICsgXCIgIFwiICsgbW9kZSk7XHJcbiAgICB2YXIgdGhlTW9kZSA9IG1vZGUgfHwgcHJvY2Vzcy5lbnYuTU9OR09fUkVDT1JEX1JFUExBWTtcclxuICAgIGlmICh0aGVNb2RlICYmIFtcIlJFUExBWVwiLCBcIlJFQ09SRFwiXS5pbmRleE9mKG1vZGUpIDwgMCkge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKCdwYXNzZWQgbW9kZSB2YWx1ZSBvciBlbnYgTU9OR09fUkVDT1JEX1JFUExBWSBtYXkgb25seSBiZSBcIlJFQ09SRFwiIG9yIFwiUkVQTEFZXCIgLCBNT05HT19SRUNPUkQgTU9OR09fUkVQTEFZJyk7XHJcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdtb25nb29zZV9yZWNvcmRfcmVwbGF5IG1vZGUgc2hvdWxkIGJlIG9uZSBvZiBcIlJFUExBWVwiLCBcIlJFQ09SRFwiICB3YXMgJyArIHRoZU1vZGUpO1xyXG4gICAgfVxyXG4gICAgaWYgKHRoZU1vZGUgPT09IFwiUkVDT1JEXCIpIHtcclxuICAgICAgICB2YXIgcmVjb3JkaW5nUGF0aCA9IHBhdGggfHwgcHJvY2Vzcy5lbnYuTU9OR09fUkVDT1JEX1JFUExBWV9QQVRIIHx8IFwibW9uZ29vc2VfcmVjb3JkX3JlcGxheVwiO1xyXG4gICAgICAgIGNvbnNvbGUubG9nKCAnISogbW9kZSBSRUNPUkQgdG8gcGF0aCAnICsgcmVjb3JkaW5nUGF0aCAgKyAnIGluICcgKyBfX2Rpcm5hbWUgKyBcIiBcIiAgKyBtb2RlICk7XHJcbiAgICAgICAgYXNzdXJlUGF0aChyZWNvcmRpbmdQYXRoKTtcclxuICAgICAgICB2YXIgb21vZGVsID0gbW9uZ29vc2UubW9kZWw7XHJcbiAgICAgICAgbW9uZ29vc2UubW9kZWwgPSBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgIGlmIChhcmd1bWVudHMubGVuZ3RoID4gMSkge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIGluc3RydW1lbnRNb2RlbChvbW9kZWwuYXBwbHkobW9uZ29vc2UsIGFyZ3VtZW50cykscmVjb3JkaW5nUGF0aCx0aGVNb2RlKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICByZXR1cm4gb21vZGVsLmFwcGx5KG1vbmdvb3NlLCBhcmd1bWVudHMpO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gbW9uZ29vc2U7XHJcbiAgICB9IGVsc2UgaWYgKHRoZU1vZGUgPT09IFwiUkVQTEFZXCIpIHtcclxuICAgICAgICByZWNvcmRpbmdQYXRoID0gcGF0aCB8fCBwcm9jZXNzLmVudi5NT05HT19SRUNPUkRfUkVQTEFZX1BBVEggfHwgXCJtb25nb29zZV9yZWNvcmRfcmVwbGF5XCI7XHJcbiAgICAgICAgY29uc29sZS5sb2coICchKiBtb2RlIFJFUExBWSBmcm9tIHBhdGggJyArIHJlY29yZGluZ1BhdGggICsgJyBpbiAnICsgX19kaXJuYW1lICsgXCIgXCIgICsgbW9kZSAgKyBcIiBcIiArIHBhdGgpO1xyXG4gICAgICAgIHZhciByID0gbWFrZU1vbmdvb3NlTW9jayhyZWNvcmRpbmdQYXRoLHRoZU1vZGUpO1xyXG4gICAgICAgIHJldHVybiByOyBcclxuICAgIH1cclxuICAgIHJldHVybiBtb25nb29zZTtcclxufVxyXG5cclxudmFyIG1vY2tzUGVyUGF0aCA9IHt9O1xyXG5cclxuZnVuY3Rpb24gbWFrZU1vbmdvb3NlTW9jayhyZWNvcmRpbmdQYXRoOiBzdHJpbmcsIHRoZU1vZGU6IHN0cmluZykge1xyXG4gICAgaWYgKCBtb2Nrc1BlclBhdGhbcmVjb3JkaW5nUGF0aF0gPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgdmFyIHJlcyA9IHtcclxuICAgICAgICAgICAgbW9kZWxzOiB7fSxcclxuICAgICAgICAgICAgcmVjb3JkaW5nUGF0aCA6IHJlY29yZGluZ1BhdGgsXHJcbiAgICAgICAgICAgIHRoZU1vZGUgOiB0aGVNb2RlLFxyXG4gICAgICAgICAgICBtb2RlbE5hbWVzOiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5tb2RlbHMpO1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBTY2hlbWE6IG1vbmdvb3NlLlNjaGVtYSxcclxuICAgICAgICBcclxuICAgICAgICAgICAgbW9kZWw6IGZ1bmN0aW9uIChhLCBiKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoYiA9PT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubW9kZWxzW2FdO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZGVidWdsb2coJ2NyZWF0aW5nIG1vZGVsICAnICsgYSArICcgYXQgbW9jaycpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5tb2RlbHNbYV0gPSBpbnN0cnVtZW50TW9kZWwoe1xyXG4gICAgICAgICAgICAgICAgICAgIGZpbmQ6IGZ1bmN0aW9uICgpIHsgfSxcclxuICAgICAgICAgICAgICAgICAgICBhZ2dyZWdhdGU6IGZ1bmN0aW9uICgpIHsgfSxcclxuICAgICAgICAgICAgICAgICAgICBkaXN0aW5jdDogZnVuY3Rpb24gKCkgeyB9LFxyXG4gICAgICAgICAgICAgICAgICAgIG1vZGVsTmFtZTogYSxcclxuICAgICAgICAgICAgICAgICAgICBzY2hlbWE6IGIsXHJcbiAgICAgICAgICAgICAgICB9IGFzIGFueSwgdGhpcy5yZWNvcmRpbmdQYXRoLCB0aGlzLnRoZU1vZGUpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMubW9kZWxzW2FdO1xyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBkaXNjb25uZWN0OiBmdW5jdGlvbiAoKSB7XHJcbiAgICAgICAgICAgICAgICBkZWJ1Z2xvZygnc2ltdWxhdGlvbmcgZGlzY29ubmVjdCAnKTtcclxuICAgICAgICAgICAgfSxcclxuICAgICAgICAgICAgY29ubmVjdDogZnVuY3Rpb24gKGNvbm5TdHI6IHN0cmluZykge1xyXG4gICAgICAgICAgICAgICAgLy8gdGhpcy5kYi5vbi5lbWl0KCdvbicpO1xyXG4gICAgICAgICAgICAgICAgZGVidWdsb2coJ3NpbXVsYXRpb25nIGNvbm5lY3RpbmcgdG8gJyArIGNvbm5TdHIpO1xyXG4gICAgICAgICAgICAgICAgaWYgKCF0aGlzLl9vbmNlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIHRoYXQgPSB0aGlzO1xyXG4gICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gKCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGF0LmNvbm5lY3Rpb24uZW1pdCgnb3BlbicpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBkZWJ1Z2xvZygnZmlyZWQgZW1pdCcpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0sIDApO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9LFxyXG4gICAgICAgICAgICBjb25uZWN0aW9uOiBkYkVtaXR0ZXJcclxuICAgICAgICB9O1xyXG4gICAgICAgIG1vY2tzUGVyUGF0aFtyZWNvcmRpbmdQYXRoXSA9IHJlcztcclxuICAgIH1cclxuICAgIHJldHVybiBtb2Nrc1BlclBhdGhbcmVjb3JkaW5nUGF0aF07XHJcbn1cclxuXHJcbi8qZXhwb3J0Ki8gLyp2YXIgbW9uZ29vc2VNb2NrMiA9IHtcclxuICAgIG1vZGVsczoge30sXHJcbiAgICByZWNvcmRpbmdQYXRoIDogXCJcIixcclxuICAgIHRoZU1vZGUgOiBcIlwiLFxyXG4gICAgbW9kZWxOYW1lczogZnVuY3Rpb24gKCkge1xyXG4gICAgICAgIHJldHVybiBPYmplY3Qua2V5cyh0aGlzLm1vZGVscyk7XHJcbiAgICB9LFxyXG4gICAgU2NoZW1hOiBtb25nb29zZS5TY2hlbWEsXHJcblxyXG4gICAgbW9kZWw6IGZ1bmN0aW9uIChhLCBiKSB7XHJcbiAgICAgICAgaWYgKGIgPT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5tb2RlbHNbYV07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGRlYnVnbG9nKCdjcmVhdGluZyBtb2RlbCAgJyArIGEgKyAnIGF0IG1vY2snKTtcclxuICAgICAgICB0aGlzLm1vZGVsc1thXSA9IGluc3RydW1lbnRNb2RlbCh7XHJcbiAgICAgICAgICAgIGZpbmQ6IGZ1bmN0aW9uICgpIHsgfSxcclxuICAgICAgICAgICAgYWdncmVnYXRlOiBmdW5jdGlvbiAoKSB7IH0sXHJcbiAgICAgICAgICAgIGRpc3RpbmN0OiBmdW5jdGlvbiAoKSB7IH0sXHJcbiAgICAgICAgICAgIG1vZGVsTmFtZTogYSxcclxuICAgICAgICAgICAgc2NoZW1hOiBiLFxyXG4gICAgICAgIH0gYXMgYW55LCB0aGlzLnJlY29yZGluZ1BhdGgsIHRoaXMudGhlTW9kZSk7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMubW9kZWxzW2FdO1xyXG4gICAgfSxcclxuICAgIGRpc2Nvbm5lY3Q6IGZ1bmN0aW9uICgpIHtcclxuICAgICAgICBkZWJ1Z2xvZygnc2ltdWxhdGlvbmcgZGlzY29ubmVjdCAnKTtcclxuICAgIH0sXHJcbiAgICBjb25uZWN0OiBmdW5jdGlvbiAoY29ublN0cjogc3RyaW5nKSB7XHJcbiAgICAgICAgLy8gdGhpcy5kYi5vbi5lbWl0KCdvbicpO1xyXG4gICAgICAgIGRlYnVnbG9nKCdzaW11bGF0aW9uZyBjb25uZWN0aW5nIHRvICcgKyBjb25uU3RyKTtcclxuICAgICAgICBpZiAoIXRoaXMuX29uY2UpIHtcclxuICAgICAgICAgICAgdmFyIHRoYXQgPSB0aGlzO1xyXG4gICAgICAgICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHtcclxuICAgICAgICAgICAgICAgIHRoYXQuY29ubmVjdGlvbi5lbWl0KCdvcGVuJyk7XHJcbiAgICAgICAgICAgICAgICBkZWJ1Z2xvZygnZmlyZWQgZW1pdCcpO1xyXG4gICAgICAgICAgICB9LCAwKTtcclxuICAgICAgICB9XHJcbiAgICB9LFxyXG4gICAgLy8gZS5nLiBzZXQoJ3VzZUNyZWF0ZUluZGV4Jyx0cnVlKVxyXG4gICAgc2V0IDogZnVuY3Rpb24oYSxiKSB7fSxcclxuICAgIGNvbm5lY3Rpb246IGRiRW1pdHRlclxyXG59O1xyXG4qLyJdfQ==