redink
Version:
RethinkDB model layer
413 lines (328 loc) • 36.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _rethinkdb = require('rethinkdb');
var _rethinkdb2 = _interopRequireDefault(_rethinkdb);
var _bluebird = require('bluebird');
var _bluebird2 = _interopRequireDefault(_bluebird);
var _chalk = require('chalk');
var _chalk2 = _interopRequireDefault(_chalk);
var _Model = require('./Model');
var _Model2 = _interopRequireDefault(_Model);
var _ModelArray = require('./ModelArray');
var _ModelArray2 = _interopRequireDefault(_ModelArray);
var _utils = require('./utils');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DEFAULT_RETHINKDB_PORT = 28015;
var Redink = function () {
/**
* @class Redink
* @param {Object} [options={}]
* @param {String} [options.db='']
* @param {String} [options.host='']
* @param {String} [options.user='']
* @param {String} [options.password='']
* @param {Object} [options.schemas={}]
* @param {Boolean} [options.verbse=false]
* @param {Number} [options.port=28015]
*/
function Redink(_ref) {
var _ref$db = _ref.db;
var db = _ref$db === undefined ? '' : _ref$db;
var _ref$host = _ref.host;
var host = _ref$host === undefined ? '' : _ref$host;
var _ref$user = _ref.user;
var user = _ref$user === undefined ? '' : _ref$user;
var _ref$password = _ref.password;
var password = _ref$password === undefined ? '' : _ref$password;
var _ref$schemas = _ref.schemas;
var schemas = _ref$schemas === undefined ? {} : _ref$schemas;
var _ref$verbose = _ref.verbose;
var verbose = _ref$verbose === undefined ? false : _ref$verbose;
var _ref$port = _ref.port;
var port = _ref$port === undefined ? DEFAULT_RETHINKDB_PORT : _ref$port;
_classCallCheck(this, Redink);
this.db = db;
this.host = host;
this.user = user;
this.password = password;
this.schemas = schemas;
this.verbose = verbose;
this.port = port;
this.indices = {};
this.models = {};
}
/**
* Connects to the RethinkDB database, registers the schemas, and configures indices.
*
* @async
* @method connect
* @return {Promise}
*/
_createClass(Redink, [{
key: 'connect',
value: function connect() {
var _this = this;
var options = {
db: this.db,
host: this.host,
port: this.port
};
if (this.user) options.user = this.user;
if (this.password) options.password = this.password;
return _rethinkdb2.default.connect(options).then(function (conn) {
return _this.conn = conn;
}).then(this.registerSchemas.bind(this)).then(this.configureIndices.bind(this));
}
/**
* Disconnects from the RethinkDB database.
*
* @async
* @method disconnect
* @return {Promise}
*/
}, {
key: 'disconnect',
value: function disconnect() {
if (!this.conn) {
throw new Error('Tried disconnecting a Redink instance that was never connected.');
}
return this.conn.close();
}
/**
* Registers Redink schemas. In other words, this completes the schema graph by hydrating inverse
* relationships where necessary. After finishing the graph, it ensures that all proper tables are
* created with each schema `type` as the table name.
*
* @private
* @method registerSchemas
* @param {Object} schemas - Redink schemas.
* @return {Promise<Object>}
*/
}, {
key: 'registerSchemas',
value: function registerSchemas() {
var _this2 = this;
var conn = this.conn;
var schemas = this.schemas;
var db = this.db;
var visitSchemas = this.visitSchemas;
var types = [];
var newSchemas = _extends({}, schemas);
/**
* First pass: visit each schema and invoke every `hasMany`, `hasOne`, and `belongsTo`
* relationship function. Along the way, add each schema type to `types`.
*/
visitSchemas(newSchemas, {
enterSchema: function enterSchema(_ref2) {
var type = _ref2.type;
return !types.includes(type) && types.push(type);
},
enterRelationship: function enterRelationship(_ref3) {
var type = _ref3.type;
var field = _ref3.field;
var relationship = _ref3.relationship;
if (typeof relationship !== 'function') {
throw new TypeError('Tried registering the \'' + type + '\' schema\'s \'' + field + '\' relationship, but it wasn\'t a ' + 'function. Please use \'hasMany\', \'belongsTo\', or \'hasOne\' from ' + '\'redink/schema\'.');
}
newSchemas[type].relationships[field] = relationship(field);
}
});
/**
* Second pass: hydrate every inverse relationship in the schema graph.
*/
visitSchemas(newSchemas, {
enterRelationship: function enterRelationship(_ref4) {
var type = _ref4.relationship.type;
return (0, _utils.hydrateInverse)(newSchemas, type);
}
});
/**
* Third pass: add a schema key to every relationship and build up the indices registry.
*/
visitSchemas(newSchemas, {
enterRelationship: function enterRelationship(_ref5) {
var type = _ref5.type;
var field = _ref5.field;
var relationship = _ref5.relationship;
var relation = relationship.relation;
var relatedType = relationship.type;
var _relationship$inverse = relationship.inverse;
var inverseRelation = _relationship$inverse.relation;
var inverseField = _relationship$inverse.field;
newSchemas[type].relationships[field].schema = newSchemas[relatedType];
if ((0, _utils.requiresIndex)(relation, inverseRelation)) {
if (!_this2.indices[relatedType]) _this2.indices[relatedType] = {};
_this2.indices[relatedType][inverseField] = true;
}
}
});
// ensure that the schemas will never be mutated
this.schemas = Object.freeze(newSchemas);
// create missing tables
return _rethinkdb2.default.db(db).tableList().run(conn)
// compute difference between available tables and schema types and create them
.then(function (tables) {
var missingTables = types.filter(function (type) {
return !tables.includes(type);
});
return _bluebird2.default.props(missingTables.reduce(function (prev, next) {
return _defineProperty({}, next, _rethinkdb2.default.tableCreate(next).run(conn));
}, {}));
})
// register the Models
.then(function () {
types.forEach(function (type) {
_this2.models[type] = new _Model2.default(conn, type, _this2.schemas[type]);
});
});
}
/**
* Walks through each schema, and calls `actions.enterSchema` upon entering each schema and calls
* `actions.enterRelationship` upon entering each relationship of that schema.
*
* @param {Object} schemas
* @param {Object} [actions={}]
* @param {Function} [actions.enterSchema]
* @param {Function} [actions.enterRelationship]
*/
}, {
key: 'visitSchemas',
value: function visitSchemas(schemas) {
var actions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var keys = Object.keys;
keys(schemas).forEach(function (type) {
var schema = schemas[type];
if (typeof actions.enterSchema === 'function') {
actions.enterSchema({ type: type, schema: schema });
}
keys(schema.relationships).forEach(function (field) {
if (typeof actions.enterRelationship === 'function') {
actions.enterRelationship({
relationship: schema.relationships[field],
type: type,
schema: schema,
field: field
});
}
});
});
}
/**
* Determines which indices are missing on `table` and creates them.
*
* @private
* @method reconcileMissingTableIndices
* @param {String} table - The table to reconcile missing indices on.
* @return {Promise<Array>}
*/
}, {
key: 'reconcileMissingTableIndices',
value: function reconcileMissingTableIndices(table) {
var conn = this.conn;
var indices = this.indices;
var verbose = this.verbose;
if (!indices[table]) {
throw new Error('Tried to reconcile the missing indices on table \'' + table + '\', but \'' + table + '\' was not ' + 'in the \'indices\' registry.');
}
var fieldsToIndex = Object.keys(indices[table]);
return _rethinkdb2.default.table(table).indexList().run(conn)
// determine which indices are missing and create them
.then(function (existingIndices) {
var missingIndices = fieldsToIndex.filter(function (field) {
return !existingIndices.includes(field);
});
if (verbose && missingIndices.length) {
var formattedMissingIndices = missingIndices.toString().split(',').join(', ');
// eslint-disable-next-line
console.log('Configuring the ' + _chalk2.default.blue(formattedMissingIndices) + ' ' + ((formattedMissingIndices.length > 1 ? 'index' : 'indices') + ' on table ') + (_chalk2.default.blue(table) + '...'));
}
return _bluebird2.default.all(missingIndices.map(function (index) {
return _rethinkdb2.default.table(table).indexCreate(index, _rethinkdb2.default.row(index)('id')).run(conn);
}));
})
// wait for all the indices on `table` to finish creating
.then(function () {
return _rethinkdb2.default.table(table).indexWait().run(conn);
});
}
/**
* Configures indices where necessary.
*
* @async
* @private
* @return {Promise}
*
* @todo Expand on this documentation.
*/
}, {
key: 'configureIndices',
value: function configureIndices() {
var _this3 = this;
var indices = this.indices;
var verbose = this.verbose;
var keys = Object.keys;
return _bluebird2.default.props(keys(indices).reduce(function (prev, next) {
return _extends({}, prev, _defineProperty({}, next, _this3.reconcileMissingTableIndices(next)));
}, {})).then(function (reconciledTables) {
if (verbose) {
keys(reconciledTables).forEach(function (table) {
reconciledTables[table].forEach(function (index) {
// eslint-disable-next-line
console.log('Index ' + _chalk2.default.blue(index.index) + ' on table ' + _chalk2.default.blue(table) + ' status: ' + ((index.ready ? _chalk2.default.green('ready') : _chalk2.default.red('error')) + '.'));
});
});
}
});
}
/**
* Returns a `Model` or `ModelArray` instance with the matching type(s) from Redink's model
* registry. Any string in `types` after a colon is interpreted as an alias.
*
* ```js
* const model = model('user'); // user model
* const modelArray = model('user', 'animal:pets'); // animal model with 'pets' alias
* ```
*
* @method model
* @param {...String} types
* @returns {(Model|ModelArray)}
*/
}, {
key: 'model',
value: function model() {
var _this4 = this;
for (var _len = arguments.length, types = Array(_len), _key = 0; _key < _len; _key++) {
types[_key] = arguments[_key];
}
if (types.length === 0) {
throw new TypeError('A defined type is required to access a Model instance.');
}
if (types.length === 1) {
var model = this.models[types[0]];
if (!model) {
throw new Error('Tried accessing a model with type \'' + types[0] + '\', but no such model was ever registered.');
}
return model;
}
var models = types.reduce(function (prev, type) {
var _destructureAlias = (0, _utils.destructureAlias)(type);
var model = _destructureAlias.model;
var alias = _destructureAlias.alias;
return _extends({}, prev, _defineProperty({}, type, {
model: _this4.models[model],
alias: alias
}));
}, {});
return new _ModelArray2.default(models);
}
}]);
return Redink;
}();
exports.default = Redink;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9SZWRpbmsuanMiXSwibmFtZXMiOlsiREVGQVVMVF9SRVRISU5LREJfUE9SVCIsIlJlZGluayIsImRiIiwiaG9zdCIsInVzZXIiLCJwYXNzd29yZCIsInNjaGVtYXMiLCJ2ZXJib3NlIiwicG9ydCIsImluZGljZXMiLCJtb2RlbHMiLCJvcHRpb25zIiwiY29ubmVjdCIsInRoZW4iLCJjb25uIiwicmVnaXN0ZXJTY2hlbWFzIiwiY29uZmlndXJlSW5kaWNlcyIsIkVycm9yIiwiY2xvc2UiLCJ2aXNpdFNjaGVtYXMiLCJ0eXBlcyIsIm5ld1NjaGVtYXMiLCJlbnRlclNjaGVtYSIsInR5cGUiLCJpbmNsdWRlcyIsInB1c2giLCJlbnRlclJlbGF0aW9uc2hpcCIsImZpZWxkIiwicmVsYXRpb25zaGlwIiwiVHlwZUVycm9yIiwicmVsYXRpb25zaGlwcyIsInJlbGF0aW9uIiwicmVsYXRlZFR5cGUiLCJpbnZlcnNlIiwiaW52ZXJzZVJlbGF0aW9uIiwiaW52ZXJzZUZpZWxkIiwic2NoZW1hIiwiT2JqZWN0IiwiZnJlZXplIiwidGFibGVMaXN0IiwicnVuIiwibWlzc2luZ1RhYmxlcyIsImZpbHRlciIsInRhYmxlcyIsInByb3BzIiwicmVkdWNlIiwicHJldiIsIm5leHQiLCJ0YWJsZUNyZWF0ZSIsImZvckVhY2giLCJhY3Rpb25zIiwia2V5cyIsInRhYmxlIiwiZmllbGRzVG9JbmRleCIsImluZGV4TGlzdCIsIm1pc3NpbmdJbmRpY2VzIiwiZXhpc3RpbmdJbmRpY2VzIiwibGVuZ3RoIiwiZm9ybWF0dGVkTWlzc2luZ0luZGljZXMiLCJ0b1N0cmluZyIsInNwbGl0Iiwiam9pbiIsImNvbnNvbGUiLCJsb2ciLCJibHVlIiwiYWxsIiwibWFwIiwiaW5kZXhDcmVhdGUiLCJpbmRleCIsInJvdyIsImluZGV4V2FpdCIsInJlY29uY2lsZU1pc3NpbmdUYWJsZUluZGljZXMiLCJyZWNvbmNpbGVkVGFibGVzIiwicmVhZHkiLCJncmVlbiIsInJlZCIsIm1vZGVs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QSxJQUFqQixDQURpQjs7QUFBQSxZQUNsQ3FELEtBRGtDLHFCQUNsQ0EsS0FEa0M7QUFBQSxZQUMzQkMsS0FEMkIscUJBQzNCQSxLQUQyQjs7O0FBRzFDLDRCQUNLL0IsSUFETCxzQkFFR3ZCLElBRkgsRUFFVTtBQUNOcUQsaUJBQU8sT0FBS2xFLE1BQUwsQ0FBWWtFLEtBQVosQ0FERDtBQUVOQztBQUZNLFNBRlY7QUFPRCxPQVZjLEVBVVosRUFWWSxDQUFmOztBQVlBLGFBQU8seUJBQWVuRSxNQUFmLENBQVA7QUFDRDs7Ozs7O2tCQWpVa0JULE0iLCJmaWxlIjoiUmVkaW5rLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHIgZnJvbSAncmV0aGlua2RiJztcbmltcG9ydCBQcm9taXNlIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQgTW9kZWwgZnJvbSAnLi9Nb2RlbCc7XG5pbXBvcnQgTW9kZWxBcnJheSBmcm9tICcuL01vZGVsQXJyYXknO1xuaW1wb3J0IHsgZGVzdHJ1Y3R1cmVBbGlhcywgaHlkcmF0ZUludmVyc2UsIHJlcXVpcmVzSW5kZXggfSBmcm9tICcuL3V0aWxzJztcblxuY29uc3QgREVGQVVMVF9SRVRISU5LREJfUE9SVCA9IDI4MDE1O1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBSZWRpbmsge1xuICAvKipcbiAgICogQGNsYXNzIFJlZGlua1xuICAgKiBAcGFyYW0ge09iamVjdH0gW29wdGlvbnM9e31dXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbb3B0aW9ucy5kYj0nJ11cbiAgICogQHBhcmFtIHtTdHJpbmd9IFtvcHRpb25zLmhvc3Q9JyddXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbb3B0aW9ucy51c2VyPScnXVxuICAgKiBAcGFyYW0ge1N0cmluZ30gW29wdGlvbnMucGFzc3dvcmQ9JyddXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbb3B0aW9ucy5zY2hlbWFzPXt9XVxuICAgKiBAcGFyYW0ge0Jvb2xlYW59IFtvcHRpb25zLnZlcmJzZT1mYWxzZV1cbiAgICogQHBhcmFtIHtOdW1iZXJ9IFtvcHRpb25zLnBvcnQ9MjgwMTVdXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7XG4gICAgZGIgPSAnJyxcbiAgICBob3N0ID0gJycsXG4gICAgdXNlciA9ICcnLFxuICAgIHBhc3N3b3JkID0gJycsXG4gICAgc2NoZW1hcyA9IHt9LFxuICAgIHZlcmJvc2UgPSBmYWxzZSxcbiAgICBwb3J0ID0gREVGQVVMVF9SRVRISU5LREJfUE9SVCxcbiAgfSkge1xuICAgIHRoaXMuZGIgPSBkYjtcbiAgICB0aGlzLmhvc3QgPSBob3N0O1xuICAgIHRoaXMudXNlciA9IHVzZXI7XG4gICAgdGhpcy5wYXNzd29yZCA9IHBhc3N3b3JkO1xuICAgIHRoaXMuc2NoZW1hcyA9IHNjaGVtYXM7XG4gICAgdGhpcy52ZXJib3NlID0gdmVyYm9zZTtcbiAgICB0aGlzLnBvcnQgPSBwb3J0O1xuXG4gICAgdGhpcy5pbmRpY2VzID0ge307XG4gICAgdGhpcy5tb2RlbHMgPSB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25uZWN0cyB0byB0aGUgUmV0aGlua0RCIGRhdGFiYXNlLCByZWdpc3RlcnMgdGhlIHNjaGVtYXMsIGFuZCBjb25maWd1cmVzIGluZGljZXMuXG4gICAqXG4gICAqIEBhc3luY1xuICAgKiBAbWV0aG9kIGNvbm5lY3RcbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICovXG4gIGNvbm5lY3QoKSB7XG4gICAgY29uc3Qgb3B0aW9ucyA9IHtcbiAgICAgIGRiOiB0aGlzLmRiLFxuICAgICAgaG9zdDogdGhpcy5ob3N0LFxuICAgICAgcG9ydDogdGhpcy5wb3J0LFxuICAgIH07XG5cbiAgICBpZiAodGhpcy51c2VyKSBvcHRpb25zLnVzZXIgPSB0aGlzLnVzZXI7XG4gICAgaWYgKHRoaXMucGFzc3dvcmQpIG9wdGlvbnMucGFzc3dvcmQgPSB0aGlzLnBhc3N3b3JkO1xuXG4gICAgcmV0dXJuIHIuY29ubmVjdChvcHRpb25zKVxuICAgICAgLnRoZW4oY29ubiA9PiAodGhpcy5jb25uID0gY29ubikpXG4gICAgICAudGhlbig6OnRoaXMucmVnaXN0ZXJTY2hlbWFzKVxuICAgICAgLnRoZW4oOjp0aGlzLmNvbmZpZ3VyZUluZGljZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIERpc2Nvbm5lY3RzIGZyb20gdGhlIFJldGhpbmtEQiBkYXRhYmFzZS5cbiAgICpcbiAgICogQGFzeW5jXG4gICAqIEBtZXRob2QgZGlzY29ubmVjdFxuICAgKiBAcmV0dXJuIHtQcm9taXNlfVxuICAgKi9cbiAgZGlzY29ubmVjdCgpIHtcbiAgICBpZiAoIXRoaXMuY29ubikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdUcmllZCBkaXNjb25uZWN0aW5nIGEgUmVkaW5rIGluc3RhbmNlIHRoYXQgd2FzIG5ldmVyIGNvbm5lY3RlZC4nKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5jb25uLmNsb3NlKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXJzIFJlZGluayBzY2hlbWFzLiBJbiBvdGhlciB3b3JkcywgdGhpcyBjb21wbGV0ZXMgdGhlIHNjaGVtYSBncmFwaCBieSBoeWRyYXRpbmcgaW52ZXJzZVxuICAgKiByZWxhdGlvbnNoaXBzIHdoZXJlIG5lY2Vzc2FyeS4gQWZ0ZXIgZmluaXNoaW5nIHRoZSBncmFwaCwgaXQgZW5zdXJlcyB0aGF0IGFsbCBwcm9wZXIgdGFibGVzIGFyZVxuICAgKiBjcmVhdGVkIHdpdGggZWFjaCBzY2hlbWEgYHR5cGVgIGFzIHRoZSB0YWJsZSBuYW1lLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAbWV0aG9kIHJlZ2lzdGVyU2NoZW1hc1xuICAgKiBAcGFyYW0ge09iamVjdH0gc2NoZW1hcyAtIFJlZGluayBzY2hlbWFzLlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPE9iamVjdD59XG4gICAqL1xuICByZWdpc3RlclNjaGVtYXMoKSB7XG4gICAgY29uc3QgeyBjb25uLCBzY2hlbWFzLCBkYiwgdmlzaXRTY2hlbWFzIH0gPSB0aGlzO1xuICAgIGNvbnN0IHR5cGVzID0gW107XG5cbiAgICBjb25zdCBuZXdTY2hlbWFzID0geyAuLi5zY2hlbWFzIH07XG5cbiAgICAvKipcbiAgICAgKiBGaXJzdCBwYXNzOiB2aXNpdCBlYWNoIHNjaGVtYSBhbmQgaW52b2tlIGV2ZXJ5IGBoYXNNYW55YCwgYGhhc09uZWAsIGFuZCBgYmVsb25nc1RvYFxuICAgICAqIHJlbGF0aW9uc2hpcCBmdW5jdGlvbi4gQWxvbmcgdGhlIHdheSwgYWRkIGVhY2ggc2NoZW1hIHR5cGUgdG8gYHR5cGVzYC5cbiAgICAgKi9cbiAgICB2aXNpdFNjaGVtYXMobmV3U2NoZW1hcywge1xuICAgICAgZW50ZXJTY2hlbWE6ICh7IHR5cGUgfSkgPT4gIXR5cGVzLmluY2x1ZGVzKHR5cGUpICYmIHR5cGVzLnB1c2godHlwZSksXG4gICAgICBlbnRlclJlbGF0aW9uc2hpcDogKHsgdHlwZSwgZmllbGQsIHJlbGF0aW9uc2hpcCB9KSA9PiB7XG4gICAgICAgIGlmICh0eXBlb2YgcmVsYXRpb25zaGlwICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcihcbiAgICAgICAgICAgIGBUcmllZCByZWdpc3RlcmluZyB0aGUgJyR7dHlwZX0nIHNjaGVtYSdzICcke2ZpZWxkfScgcmVsYXRpb25zaGlwLCBidXQgaXQgd2Fzbid0IGEgYCArXG4gICAgICAgICAgICAnZnVuY3Rpb24uIFBsZWFzZSB1c2UgXFwnaGFzTWFueVxcJywgXFwnYmVsb25nc1RvXFwnLCBvciBcXCdoYXNPbmVcXCcgZnJvbSAnICtcbiAgICAgICAgICAgICdcXCdyZWRpbmsvc2NoZW1hXFwnLidcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgbmV3U2NoZW1hc1t0eXBlXS5yZWxhdGlvbnNoaXBzW2ZpZWxkXSA9IHJlbGF0aW9uc2hpcChmaWVsZCk7XG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgLyoqXG4gICAgICogU2Vjb25kIHBhc3M6IGh5ZHJhdGUgZXZlcnkgaW52ZXJzZSByZWxhdGlvbnNoaXAgaW4gdGhlIHNjaGVtYSBncmFwaC5cbiAgICAgKi9cbiAgICB2aXNpdFNjaGVtYXMobmV3U2NoZW1hcywge1xuICAgICAgZW50ZXJSZWxhdGlvbnNoaXA6ICh7IHJlbGF0aW9uc2hpcDogeyB0eXBlIH0gfSkgPT4gaHlkcmF0ZUludmVyc2UobmV3U2NoZW1hcywgdHlwZSksXG4gICAgfSk7XG5cbiAgICAvKipcbiAgICAgKiBUaGlyZCBwYXNzOiBhZGQgYSBzY2hlbWEga2V5IHRvIGV2ZXJ5IHJlbGF0aW9uc2hpcCBhbmQgYnVpbGQgdXAgdGhlIGluZGljZXMgcmVnaXN0cnkuXG4gICAgICovXG4gICAgdmlzaXRTY2hlbWFzKG5ld1NjaGVtYXMsIHtcbiAgICAgIGVudGVyUmVsYXRpb25zaGlwOiAoeyB0eXBlLCBmaWVsZCwgcmVsYXRpb25zaGlwIH0pID0+IHtcbiAgICAgICAgY29uc3Qge1xuICAgICAgICAgIHJlbGF0aW9uLFxuICAgICAgICAgIHR5cGU6IHJlbGF0ZWRUeXBlLFxuICAgICAgICAgIGludmVyc2U6IHtcbiAgICAgICAgICAgIHJlbGF0aW9uOiBpbnZlcnNlUmVsYXRpb24sXG4gICAgICAgICAgICBmaWVsZDogaW52ZXJzZUZpZWxkLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0gPSByZWxhdGlvbnNoaXA7XG5cbiAgICAgICAgbmV3U2NoZW1hc1t0eXBlXS5yZWxhdGlvbnNoaXBzW2ZpZWxkXS5zY2hlbWEgPSBuZXdTY2hlbWFzW3JlbGF0ZWRUeXBlXTtcblxuICAgICAgICBpZiAocmVxdWlyZXNJbmRleChyZWxhdGlvbiwgaW52ZXJzZVJlbGF0aW9uKSkge1xuICAgICAgICAgIGlmICghdGhpcy5pbmRpY2VzW3JlbGF0ZWRUeXBlXSkgdGhpcy5pbmRpY2VzW3JlbGF0ZWRUeXBlXSA9IHt9O1xuICAgICAgICAgIHRoaXMuaW5kaWNlc1tyZWxhdGVkVHlwZV1baW52ZXJzZUZpZWxkXSA9IHRydWU7XG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICAvLyBlbnN1cmUgdGhhdCB0aGUgc2NoZW1hcyB3aWxsIG5ldmVyIGJlIG11dGF0ZWRcbiAgICB0aGlzLnNjaGVtYXMgPSBPYmplY3QuZnJlZXplKG5ld1NjaGVtYXMpO1xuXG4gICAgLy8gY3JlYXRlIG1pc3NpbmcgdGFibGVzXG4gICAgcmV0dXJuIHIuZGIoZGIpLnRhYmxlTGlzdCgpLnJ1bihjb25uKVxuXG4gICAgICAvLyBjb21wdXRlIGRpZmZlcmVuY2UgYmV0d2VlbiBhdmFpbGFibGUgdGFibGVzIGFuZCBzY2hlbWEgdHlwZXMgYW5kIGNyZWF0ZSB0aGVtXG4gICAgICAudGhlbih0YWJsZXMgPT4ge1xuICAgICAgICBjb25zdCBtaXNzaW5nVGFibGVzID0gdHlwZXMuZmlsdGVyKHR5cGUgPT4gIXRhYmxlcy5pbmNsdWRlcyh0eXBlKSk7XG5cbiAgICAgICAgcmV0dXJuIFByb21pc2UucHJvcHMoXG4gICAgICAgICAgbWlzc2luZ1RhYmxlcy5yZWR1Y2UoKHByZXYsIG5leHQpID0+ICh7XG4gICAgICAgICAgICBbbmV4dF06IHIudGFibGVDcmVhdGUobmV4dCkucnVuKGNvbm4pLFxuICAgICAgICAgIH0pLCB7fSlcbiAgICAgICAgKTtcbiAgICAgIH0pXG5cbiAgICAgIC8vIHJlZ2lzdGVyIHRoZSBNb2RlbHNcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgdHlwZXMuZm9yRWFjaCh0eXBlID0+IHtcbiAgICAgICAgICB0aGlzLm1vZGVsc1t0eXBlXSA9IG5ldyBNb2RlbChjb25uLCB0eXBlLCB0aGlzLnNjaGVtYXNbdHlwZV0pO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFdhbGtzIHRocm91Z2ggZWFjaCBzY2hlbWEsIGFuZCBjYWxscyBgYWN0aW9ucy5lbnRlclNjaGVtYWAgdXBvbiBlbnRlcmluZyBlYWNoIHNjaGVtYSBhbmQgY2FsbHNcbiAgICogYGFjdGlvbnMuZW50ZXJSZWxhdGlvbnNoaXBgIHVwb24gZW50ZXJpbmcgZWFjaCByZWxhdGlvbnNoaXAgb2YgdGhhdCBzY2hlbWEuXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBzY2hlbWFzXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbYWN0aW9ucz17fV1cbiAgICogQHBhcmFtIHtGdW5jdGlvbn0gW2FjdGlvbnMuZW50ZXJTY2hlbWFdXG4gICAqIEBwYXJhbSB7RnVuY3Rpb259IFthY3Rpb25zLmVudGVyUmVsYXRpb25zaGlwXVxuICAgKi9cbiAgdmlzaXRTY2hlbWFzKHNjaGVtYXMsIGFjdGlvbnMgPSB7fSkge1xuICAgIGNvbnN0IHsga2V5cyB9ID0gT2JqZWN0O1xuXG4gICAga2V5cyhzY2hlbWFzKS5mb3JFYWNoKHR5cGUgPT4ge1xuICAgICAgY29uc3Qgc2NoZW1hID0gc2NoZW1hc1t0eXBlXTtcblxuICAgICAgaWYgKHR5cGVvZiBhY3Rpb25zLmVudGVyU2NoZW1hID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIGFjdGlvbnMuZW50ZXJTY2hlbWEoeyB0eXBlLCBzY2hlbWEgfSk7XG4gICAgICB9XG5cbiAgICAgIGtleXMoc2NoZW1hLnJlbGF0aW9uc2hpcHMpLmZvckVhY2goZmllbGQgPT4ge1xuICAgICAgICBpZiAodHlwZW9mIGFjdGlvbnMuZW50ZXJSZWxhdGlvbnNoaXAgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICBhY3Rpb25zLmVudGVyUmVsYXRpb25zaGlwKHtcbiAgICAgICAgICAgIHJlbGF0aW9uc2hpcDogc2NoZW1hLnJlbGF0aW9uc2hpcHNbZmllbGRdLFxuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIHNjaGVtYSxcbiAgICAgICAgICAgIGZpZWxkLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmVzIHdoaWNoIGluZGljZXMgYXJlIG1pc3Npbmcgb24gYHRhYmxlYCBhbmQgY3JlYXRlcyB0aGVtLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAbWV0aG9kIHJlY29uY2lsZU1pc3NpbmdUYWJsZUluZGljZXNcbiAgICogQHBhcmFtIHtTdHJpbmd9IHRhYmxlIC0gVGhlIHRhYmxlIHRvIHJlY29uY2lsZSBtaXNzaW5nIGluZGljZXMgb24uXG4gICAqIEByZXR1cm4ge1Byb21pc2U8QXJyYXk+fVxuICAgKi9cbiAgcmVjb25jaWxlTWlzc2luZ1RhYmxlSW5kaWNlcyh0YWJsZSkge1xuICAgIGNvbnN0IHsgY29ubiwgaW5kaWNlcywgdmVyYm9zZSB9ID0gdGhpcztcblxuICAgIGlmICghaW5kaWNlc1t0YWJsZV0pIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYFRyaWVkIHRvIHJlY29uY2lsZSB0aGUgbWlzc2luZyBpbmRpY2VzIG9uIHRhYmxlICcke3RhYmxlfScsIGJ1dCAnJHt0YWJsZX0nIHdhcyBub3QgYCArXG4gICAgICAgICdpbiB0aGUgXFwnaW5kaWNlc1xcJyByZWdpc3RyeS4nXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGZpZWxkc1RvSW5kZXggPSBPYmplY3Qua2V5cyhpbmRpY2VzW3RhYmxlXSk7XG5cbiAgICByZXR1cm4gci50YWJsZSh0YWJsZSkuaW5kZXhMaXN0KCkucnVuKGNvbm4pXG5cbiAgICAgIC8vIGRldGVybWluZSB3aGljaCBpbmRpY2VzIGFyZSBtaXNzaW5nIGFuZCBjcmVhdGUgdGhlbVxuICAgICAgLnRoZW4oZXhpc3RpbmdJbmRpY2VzID0+IHtcbiAgICAgICAgY29uc3QgbWlzc2luZ0luZGljZXMgPSBmaWVsZHNUb0luZGV4LmZpbHRlcihcbiAgICAgICAgICBmaWVsZCA9PiAhZXhpc3RpbmdJbmRpY2VzLmluY2x1ZGVzKGZpZWxkKVxuICAgICAgICApO1xuXG4gICAgICAgIGlmICh2ZXJib3NlICYmIG1pc3NpbmdJbmRpY2VzLmxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IGZvcm1hdHRlZE1pc3NpbmdJbmRpY2VzID0gbWlzc2luZ0luZGljZXMudG9TdHJpbmcoKS5zcGxpdCgnLCcpLmpvaW4oJywgJyk7XG5cbiAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmVcbiAgICAgICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgICAgIGBDb25maWd1cmluZyB0aGUgJHtjaGFsay5ibHVlKGZvcm1hdHRlZE1pc3NpbmdJbmRpY2VzKX0gYCArXG4gICAgICAgICAgICBgJHtmb3JtYXR0ZWRNaXNzaW5nSW5kaWNlcy5sZW5ndGggPiAxID8gJ2luZGV4JyA6ICdpbmRpY2VzJ30gb24gdGFibGUgYCArXG4gICAgICAgICAgICBgJHtjaGFsay5ibHVlKHRhYmxlKX0uLi5gXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBQcm9taXNlLmFsbChcbiAgICAgICAgICBtaXNzaW5nSW5kaWNlcy5tYXAoaW5kZXggPT5cbiAgICAgICAgICAgIHIudGFibGUodGFibGUpLmluZGV4Q3JlYXRlKGluZGV4LCByLnJvdyhpbmRleCkoJ2lkJykpLnJ1bihjb25uKVxuICAgICAgICAgIClcbiAgICAgICAgKTtcbiAgICAgIH0pXG5cbiAgICAgIC8vIHdhaXQgZm9yIGFsbCB0aGUgaW5kaWNlcyBvbiBgdGFibGVgIHRvIGZpbmlzaCBjcmVhdGluZ1xuICAgICAgLnRoZW4oKCkgPT4gci50YWJsZSh0YWJsZSkuaW5kZXhXYWl0KCkucnVuKGNvbm4pKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDb25maWd1cmVzIGluZGljZXMgd2hlcmUgbmVjZXNzYXJ5LlxuICAgKlxuICAgKiBAYXN5bmNcbiAgICogQHByaXZhdGVcbiAgICogQHJldHVybiB7UHJvbWlzZX1cbiAgICpcbiAgICogQHRvZG8gRXhwYW5kIG9uIHRoaXMgZG9jdW1lbnRhdGlvbi5cbiAgICovXG4gIGNvbmZpZ3VyZUluZGljZXMoKSB7XG4gICAgY29uc3QgeyBpbmRpY2VzLCB2ZXJib3NlIH0gPSB0aGlzO1xuICAgIGNvbnN0IHsga2V5cyB9ID0gT2JqZWN0O1xuXG4gICAgcmV0dXJuIFByb21pc2UucHJvcHMoXG4gICAgICBrZXlzKGluZGljZXMpLnJlZHVjZSgocHJldiwgbmV4dCkgPT4gKHtcbiAgICAgICAgLi4ucHJldixcbiAgICAgICAgW25leHRdOiB0aGlzLnJlY29uY2lsZU1pc3NpbmdUYWJsZUluZGljZXMobmV4dCksXG4gICAgICB9KSwge30pXG4gICAgKS50aGVuKHJlY29uY2lsZWRUYWJsZXMgPT4ge1xuICAgICAgaWYgKHZlcmJvc2UpIHtcbiAgICAgICAga2V5cyhyZWNvbmNpbGVkVGFibGVzKS5mb3JFYWNoKHRhYmxlID0+IHtcbiAgICAgICAgICByZWNvbmNpbGVkVGFibGVzW3RhYmxlXS5mb3JFYWNoKGluZGV4ID0+IHtcbiAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZVxuICAgICAgICAgICAgY29uc29sZS5sb2coXG4gICAgICAgICAgICAgIGBJbmRleCAke2NoYWxrLmJsdWUoaW5kZXguaW5kZXgpfSBvbiB0YWJsZSAke2NoYWxrLmJsdWUodGFibGUpfSBzdGF0dXM6IGAgK1xuICAgICAgICAgICAgICBgJHtpbmRleC5yZWFkeSA/IGNoYWxrLmdyZWVuKCdyZWFkeScpIDogY2hhbGsucmVkKCdlcnJvcicpfS5gXG4gICAgICAgICAgICApO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgYE1vZGVsYCBvciBgTW9kZWxBcnJheWAgaW5zdGFuY2Ugd2l0aCB0aGUgbWF0Y2hpbmcgdHlwZShzKSBmcm9tIFJlZGluaydzIG1vZGVsXG4gICAqIHJlZ2lzdHJ5LiBBbnkgc3RyaW5nIGluIGB0eXBlc2AgYWZ0ZXIgYSBjb2xvbiBpcyBpbnRlcnByZXRlZCBhcyBhbiBhbGlhcy5cbiAgICpcbiAgICogYGBganNcbiAgICogY29uc3QgbW9kZWwgPSBtb2RlbCgndXNlcicpOyAvLyB1c2VyIG1vZGVsXG4gICAqIGNvbnN0IG1vZGVsQXJyYXkgPSBtb2RlbCgndXNlcicsICdhbmltYWw6cGV0cycpOyAvLyBhbmltYWwgbW9kZWwgd2l0aCAncGV0cycgYWxpYXNcbiAgICogYGBgXG4gICAqXG4gICAqIEBtZXRob2QgbW9kZWxcbiAgICogQHBhcmFtIHsuLi5TdHJpbmd9IHR5cGVzXG4gICAqIEByZXR1cm5zIHsoTW9kZWx8TW9kZWxBcnJheSl9XG4gICAqL1xuICBtb2RlbCguLi50eXBlcykge1xuICAgIGlmICh0eXBlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoXG4gICAgICAgICdBIGRlZmluZWQgdHlwZSBpcyByZXF1aXJlZCB0byBhY2Nlc3MgYSBNb2RlbCBpbnN0YW5jZS4nXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICh0eXBlcy5sZW5ndGggPT09IDEpIHtcbiAgICAgIGNvbnN0IG1vZGVsID0gdGhpcy5tb2RlbHNbdHlwZXNbMF1dO1xuXG4gICAgICBpZiAoIW1vZGVsKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgVHJpZWQgYWNjZXNzaW5nIGEgbW9kZWwgd2l0aCB0eXBlICcke3R5cGVzWzBdfScsIGJ1dCBubyBzdWNoIG1vZGVsIHdhcyBldmVyIHJlZ2lzdGVyZWQuYFxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbW9kZWw7XG4gICAgfVxuXG4gICAgY29uc3QgbW9kZWxzID0gdHlwZXMucmVkdWNlKChwcmV2LCB0eXBlKSA9PiB7XG4gICAgICBjb25zdCB7IG1vZGVsLCBhbGlhcyB9ID0gZGVzdHJ1Y3R1cmVBbGlhcyh0eXBlKTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ucHJldixcbiAgICAgICAgW3R5cGVdOiB7XG4gICAgICAgICAgbW9kZWw6IHRoaXMubW9kZWxzW21vZGVsXSxcbiAgICAgICAgICBhbGlhcyxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSwge30pO1xuXG4gICAgcmV0dXJuIG5ldyBNb2RlbEFycmF5KG1vZGVscyk7XG4gIH1cbn1cbiJdfQ==