redink
Version:
RethinkDB model layer
410 lines (339 loc) • 34.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _Resource = require('./Resource');
var _Resource2 = _interopRequireDefault(_Resource);
var _ResourceArray = require('./ResourceArray');
var _ResourceArray2 = _interopRequireDefault(_ResourceArray);
var _create = require('./constraints/create');
var _create2 = _interopRequireDefault(_create);
var _utils = require('./utils');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Model = function () {
/**
* Instantiates a Model.
*
* @class Model
* @param {Object} conn - RethinkDB connection object.
* @param {String} type
* @param {Schema} schema
*/
function Model(conn, type, schema) {
_classCallCheck(this, Model);
if (!conn) {
throw new TypeError('A valid RethinkDB connection is required to instantiate a Resource.');
}
if (!type) {
throw new TypeError('A valid type is required to instantiate a model.');
}
if (!schema) {
throw new TypeError('A valid schema is required to instantiate a model.');
}
this.conn = conn;
this.type = type;
this.schema = schema;
}
/**
* Finds resources that match the criteria in `pre` and `post` options.
*
* ```
* model('user').find({
* filter: { name: 'Dylan' },
* without: { password: true },
* include: {
* pets: {
* filter: (pet) => pet('age').gt(6),
* pluck: {
* favoriteTreat: true,
* color: true,
* },
* },
* company: true,
* },
* }).then(users => {
* // ResourceArray
* });
* ```
*
* @async
* @method find
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @return {Promise<ResourceArray>}
*
* @todo Write more docs on `options`
*/
_createClass(Model, [{
key: 'find',
value: function find() {
var pre = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var post = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var conn = this.conn;
var schema = this.schema;
var type = schema.type;
var table = _rethinkdb2.default.table(type);
table = (0, _utils.applyOptions)(table, pre);
table = (0, _utils.mergeRelationships)(table, schema, pre);
table = (0, _utils.applyOptions)(table, post);
table = table.coerceTo('array');
return table.run(conn).then(function (records) {
return new _ResourceArray2.default(conn, schema, records);
});
}
/**
* Finds the first resource (out of potentially many) that matches the criteria in `options.`
*
* @async
* @method findOne
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @return {Promise<Resource>}
*/
}, {
key: 'findOne',
value: function findOne() {
var pre = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var post = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return this.find(pre, post).then(function (resources) {
return resources.first();
});
}
/**
* Returns the total count of resources.
* @return {Number}
*/
}, {
key: 'count',
value: function count() {
var conn = this.conn;
var schema = this.schema;
var type = schema.type;
return _rethinkdb2.default.table(type).count().run(conn);
}
/**
* Finds resources using the index named `index`.
*
* ```
* model('user').findByIndex('email', 'dylanslack@gmail.com').then(users => {
* // ResourceArray
* });
* ```
*
* @async
* @method findByIndex
* @param {String} index - The index name.
* @param {*} value
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @return {Promise<ResourceArray>}
*
* @todo Add test.
*/
}, {
key: 'findByIndex',
value: function findByIndex(index, value) {
var pre = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var post = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var conn = this.conn;
var schema = this.schema;
var type = schema.type;
var table = _rethinkdb2.default.table(type);
table = table.getAll(value, { index: index });
table = (0, _utils.applyOptions)(table, pre);
table = (0, _utils.mergeRelationships)(table, schema, pre);
table = (0, _utils.applyOptions)(table, post);
table = table.coerceTo('array');
return table.run(conn).then(function (records) {
return new _ResourceArray2.default(conn, schema, records);
});
}
/**
* Finds a single resource from `index` that matches `value` and that matches the criteria in
* `options`.
*
* @async
* @method findOneByIndex
* @param {String} index - The index name.
* @param {*} value
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @return {Promise<Resource>}
*/
}, {
key: 'findOneByIndex',
value: function findOneByIndex(index, value) {
var pre = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var post = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
return this.findByIndex(index, value, pre, post).then(function (resources) {
return resources.first();
});
}
/**
* Retrieves the resource(s) related to a particular resource identified by `id` according to
* `relationship`. A relationship with a relation of `hasMany` returns a `ResourceArray`, and a
* relationship with a relation of `hasOne` or `belongsTo` returns a `Resource`.
*
* ```js
* // pretend user `1` has a ton of pets
* model('user').findRelated('1', 'pets', {
* filter: { species: 'hamster' },
* }).then(pets => {
* // ResourceArray
* });
*
* // pretend user `1` has a company
* model('user').findRelated('1', 'company').then(company => {
* // Resource
* });
* ```
*
* @async
* @method findRelated
* @param {String} id - The id of the parent resource.
* @param {String} relationship - The relationship to the parent.
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @returns {Promise<Resource|ResourceArray>}
*/
}, {
key: 'findRelated',
value: function findRelated(id, relationship) {
var pre = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var post = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var conn = this.conn;
var schema = this.schema;
var parentType = this.schema.type;
var _schema$relationships = schema.relationships[relationship];
var relatedType = _schema$relationships.type;
var relatedSchema = _schema$relationships.schema;
var relation = _schema$relationships.relation;
var inverse = _schema$relationships.inverse;
var table = _rethinkdb2.default.table(relatedType);
if (relation === 'hasMany') {
if ((0, _utils.requiresIndex)(relation, inverse.relation)) {
table = table.getAll(id, { index: inverse.field });
} else {
table = table.getAll(_rethinkdb2.default.args(_rethinkdb2.default.table(parentType).get(id)(relationship)('id')));
}
table = table.coerceTo('array');
} else {
table = table.get(_rethinkdb2.default.table(parentType).get(id)(relationship)('id'));
}
table = (0, _utils.applyOptions)(table, pre);
table = (0, _utils.mergeRelationships)(table, relatedSchema, pre);
table = (0, _utils.applyOptions)(table, post);
return table.run(conn).then(function (recordOrRecords) {
if (relation === 'hasMany') return new _ResourceArray2.default(conn, relatedSchema, recordOrRecords);
return new _Resource2.default(conn, relatedSchema, recordOrRecords);
});
}
/**
* Retrieves the resource corresponding to `id`.
*
* ```js
* model('user').fetchResource('1').then(user => {
* // Resource
* });
* ```
*
* @async
* @method fetchResource
* @param {String} id - The ID of the resource to retrieve.
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @returns {Promise<Resource>}
*/
}, {
key: 'fetchResource',
value: function fetchResource(id) {
var pre = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var post = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var conn = this.conn;
var schema = this.schema;
var table = _rethinkdb2.default.table(schema.type);
table = table.get(id);
table = (0, _utils.applyOptions)(table, pre);
table = (0, _utils.mergeRelationships)(table, schema, pre);
table = (0, _utils.applyOptions)(table, post);
return table.run(conn).then(function (record) {
return new _Resource2.default(conn, schema, record);
});
}
/**
* Persists a record in the database and creates a resource. The 'record' object must be a
* flattenen JSON with attributes and relationships. All relationships in `record` must be
* represented by either a string for a `hasOne` or `belongsTo` relation, or an array of strings
* for a `hasMany` relation.
*
* ```js
* model('user').create({
* name: 'Dylan',
* email: 'dylanslack@gmail.com',
* password: 'hashedpassword',
* pets: [ '1', '2', '3' ],
* company: '1',
* }).then(user => {
* // Resource
* });
* ```
*
* @async
* @method create
* @param {Object} record
* @param {Object} [pre={}] - Critera before merging relationships.
* @param {Object} [post={}] - Critera after merging relationships.
* @returns {Promise<Resource>}
*/
}, {
key: 'create',
value: function create(record) {
var pre = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var post = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var conn = this.conn;
var schema = this.schema;
var type = schema.type;
var checkComplianceAndNormalizeRecord = function checkComplianceAndNormalizeRecord(compliant) {
if (!compliant) {
throw new Error('Tried to create a record, but \'record\' had some invalid relationships.');
}
return (0, _utils.normalizeRecord)(record, schema);
};
var createRecord = function createRecord(normalizedRecord) {
var createdRecordId = void 0;
var createdResource = void 0;
return _rethinkdb2.default.table(type).insert(normalizedRecord).run(conn)
// retrieve the record that was just created
.then(function (_ref) {
var keys = _ref.generated_keys;
var table = _rethinkdb2.default.table(type);
createdRecordId = keys[0];
table = table.get(createdRecordId);
table = (0, _utils.applyOptions)(table, pre);
table = (0, _utils.mergeRelationships)(table, schema, pre);
table = (0, _utils.applyOptions)(table, post);
return table.run(conn);
})
// create the resource and sync its relationships
.then(function (createdRecord) {
createdResource = new _Resource2.default(conn, schema, createdRecord);
var syncRelationshipsArray = (0, _utils.syncRelationships)(record, schema, createdRecordId);
return _rethinkdb2.default.do(syncRelationshipsArray).run(conn);
})
// return the resource
.then(function () {
return createdResource;
});
};
// check record and it's relationships for Redink constraints
return (0, _create2.default)(record, schema, conn).then(checkComplianceAndNormalizeRecord).then(createRecord);
}
}]);
return Model;
}();
exports.default = Model;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9Nb2RlbC5qcyJdLCJuYW1lcyI6WyJNb2RlbCIsImNvbm4iLCJ0eXBlIiwic2NoZW1hIiwiVHlwZUVycm9yIiwicHJlIiwicG9zdCIsInRhYmxlIiwiY29lcmNlVG8iLCJydW4iLCJ0aGVuIiwicmVjb3JkcyIsImZpbmQiLCJyZXNvdXJjZXMiLCJmaXJzdCIsImNvdW50IiwiaW5kZXgiLCJ2YWx1ZSIsImdldEFsbCIsImZpbmRCeUluZGV4IiwiaWQiLCJyZWxhdGlvbnNoaXAiLCJwYXJlbnRUeXBlIiwicmVsYXRpb25zaGlwcyIsInJlbGF0ZWRUeXBlIiwicmVsYXRlZFNjaGVtYSIsInJlbGF0aW9uIiwiaW52ZXJzZSIsImZpZWxkIiwiYXJncyIsImdldCIsInJlY29yZE9yUmVjb3JkcyIsInJlY29yZCIsImNoZWNrQ29tcGxpYW5jZUFuZE5vcm1hbGl6ZVJlY29yZCIsImNvbXBsaWFudCIsIkVycm9yIiwiY3JlYXRlUmVjb3JkIiwibm9ybWFsaXplZFJlY29yZCIsImNyZWF0ZWRSZWNvcmRJZCIsImNyZWF0ZWRSZXNvdXJjZSIsImluc2VydCIsImtleXMiLCJnZW5lcmF0ZWRfa2V5cyIsImNyZWF0ZWRSZWNvcmQiLCJzeW5jUmVsYXRpb25zaGlwc0FycmF5IiwiZG8iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQUE7Ozs7QUFDQTs7OztBQUNBOzs7O0FBQ0E7Ozs7QUFFQTs7Ozs7O0lBUXFCQSxLO0FBQ25COzs7Ozs7OztBQVFBLGlCQUFZQyxJQUFaLEVBQWtCQyxJQUFsQixFQUF3QkMsTUFBeEIsRUFBZ0M7QUFBQTs7QUFDOUIsUUFBSSxDQUFDRixJQUFMLEVBQVc7QUFDVCxZQUFNLElBQUlHLFNBQUosQ0FBYyxxRUFBZCxDQUFOO0FBQ0Q7O0FBRUQsUUFBSSxDQUFDRixJQUFMLEVBQVc7QUFDVCxZQUFNLElBQUlFLFNBQUosQ0FBYyxrREFBZCxDQUFOO0FBQ0Q7O0FBRUQsUUFBSSxDQUFDRCxNQUFMLEVBQWE7QUFDWCxZQUFNLElBQUlDLFNB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b0JBQUVBLEtBQUYsQ0FBUUwsSUFBUixDQUFaO0FBQ0FvQyw0QkFBa0JHLEtBQUssQ0FBTCxDQUFsQjs7QUFFQWxDLGtCQUFRQSxNQUFNdUIsR0FBTixDQUFVUSxlQUFWLENBQVI7QUFDQS9CLGtCQUFRLHlCQUFhQSxLQUFiLEVBQW9CRixHQUFwQixDQUFSO0FBQ0FFLGtCQUFRLCtCQUFtQkEsS0FBbkIsRUFBMEJKLE1BQTFCLEVBQWtDRSxHQUFsQyxDQUFSO0FBQ0FFLGtCQUFRLHlCQUFhQSxLQUFiLEVBQW9CRCxJQUFwQixDQUFSOztBQUVBLGlCQUFPQyxNQUFNRSxHQUFOLENBQVVSLElBQVYsQ0FBUDtBQUNELFNBYkk7O0FBZUw7QUFmSyxTQWdCSlMsSUFoQkksQ0FnQkMseUJBQWlCO0FBQ3JCNkIsNEJBQWtCLHVCQUFhdEMsSUFBYixFQUFtQkUsTUFBbkIsRUFBMkJ3QyxhQUEzQixDQUFsQjtBQUNBLGNBQU1DLHlCQUF5Qiw4QkFBa0JaLE1BQWxCLEVBQTBCN0IsTUFBMUIsRUFBa0NtQyxlQUFsQyxDQUEvQjs7QUFFQSxpQkFBTyxvQkFBRU8sRUFBRixDQUFLRCxzQkFBTCxFQUE2Qm5DLEdBQTdCLENBQWlDUixJQUFqQyxDQUFQO0FBQ0QsU0FyQkk7O0FBdUJMO0FBdkJLLFNBd0JKUyxJQXhCSSxDQXdCQztBQUFBLGlCQUFNNkIsZUFBTjtBQUFBLFNBeEJELENBQVA7QUF5QkQsT0E3QkQ7O0FBK0JBO0FBQ0EsYUFBTyxzQkFBa0JQLE1BQWxCLEVBQTBCN0IsTUFBMUIsRUFBa0NGLElBQWxDLEVBQ0pTLElBREksQ0FDQ3VCLGlDQURELEVBRUp2QixJQUZJLENBRUMwQixZQUZELENBQVA7QUFHRDs7Ozs7O2tCQTVUa0JwQyxLIiwiZmlsZSI6Ik1vZGVsLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHIgZnJvbSAncmV0aGlua2RiJztcbmltcG9ydCBSZXNvdXJjZSBmcm9tICcuL1Jlc291cmNlJztcbmltcG9ydCBSZXNvdXJjZUFycmF5IGZyb20gJy4vUmVzb3VyY2VBcnJheSc7XG5pbXBvcnQgaXNDcmVhdGVDb21wbGlhbnQgZnJvbSAnLi9jb25zdHJhaW50cy9jcmVhdGUnO1xuXG5pbXBvcnQge1xuICBhcHBseU9wdGlvbnMsXG4gIG1lcmdlUmVsYXRpb25zaGlwcyxcbiAgbm9ybWFsaXplUmVjb3JkLFxuICByZXF1aXJlc0luZGV4LFxuICBzeW5jUmVsYXRpb25zaGlwcyxcbn0gZnJvbSAnLi91dGlscyc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1vZGVsIHtcbiAgLyoqXG4gICAqIEluc3RhbnRpYXRlcyBhIE1vZGVsLlxuICAgKlxuICAgKiBAY2xhc3MgTW9kZWxcbiAgICogQHBhcmFtIHtPYmplY3R9IGNvbm4gLSBSZXRoaW5rREIgY29ubmVjdGlvbiBvYmplY3QuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSB0eXBlXG4gICAqIEBwYXJhbSB7U2NoZW1hfSBzY2hlbWFcbiAgICovXG4gIGNvbnN0cnVjdG9yKGNvbm4sIHR5cGUsIHNjaGVtYSkge1xuICAgIGlmICghY29ubikge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQSB2YWxpZCBSZXRoaW5rREIgY29ubmVjdGlvbiBpcyByZXF1aXJlZCB0byBpbnN0YW50aWF0ZSBhIFJlc291cmNlLicpO1xuICAgIH1cblxuICAgIGlmICghdHlwZSkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignQSB2YWxpZCB0eXBlIGlzIHJlcXVpcmVkIHRvIGluc3RhbnRpYXRlIGEgbW9kZWwuJyk7XG4gICAgfVxuXG4gICAgaWYgKCFzY2hlbWEpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0EgdmFsaWQgc2NoZW1hIGlzIHJlcXVpcmVkIHRvIGluc3RhbnRpYXRlIGEgbW9kZWwuJyk7XG4gICAgfVxuXG4gICAgdGhpcy5jb25uID0gY29ubjtcbiAgICB0aGlzLnR5cGUgPSB0eXBlO1xuICAgIHRoaXMuc2NoZW1hID0gc2NoZW1hO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpbmRzIHJlc291cmNlcyB0aGF0IG1hdGNoIHRoZSBjcml0ZXJpYSBpbiBgcHJlYCBhbmQgYHBvc3RgIG9wdGlvbnMuXG4gICAqXG4gICAqIGBgYFxuICAgKiBtb2RlbCgndXNlcicpLmZpbmQoe1xuICAgKiAgIGZpbHRlcjogeyBuYW1lOiAnRHlsYW4nIH0sXG4gICAqICAgd2l0aG91dDogeyBwYXNzd29yZDogdHJ1ZSB9LFxuICAgKiAgIGluY2x1ZGU6IHtcbiAgICogICAgIHBldHM6IHtcbiAgICogICAgICAgZmlsdGVyOiAocGV0KSA9PiBwZXQoJ2FnZScpLmd0KDYpLFxuICAgKiAgICAgICBwbHVjazoge1xuICAgKiAgICAgICAgIGZhdm9yaXRlVHJlYXQ6IHRydWUsXG4gICAqICAgICAgICAgY29sb3I6IHRydWUsXG4gICAqICAgICAgIH0sXG4gICAqICAgICB9LFxuICAgKiAgICAgY29tcGFueTogdHJ1ZSxcbiAgICogICB9LFxuICAgKiB9KS50aGVuKHVzZXJzID0+IHtcbiAgICogICAvLyBSZXNvdXJjZUFycmF5XG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICpcbiAgICogQGFzeW5jXG4gICAqIEBtZXRob2QgZmluZFxuICAgKiBAcGFyYW0ge09iamVjdH0gW3ByZT17fV0gLSBDcml0ZXJhIGJlZm9yZSBtZXJnaW5nIHJlbGF0aW9uc2hpcHMuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbcG9zdD17fV0gLSBDcml0ZXJhIGFmdGVyIG1lcmdpbmcgcmVsYXRpb25zaGlwcy5cbiAgICogQHJldHVybiB7UHJvbWlzZTxSZXNvdXJjZUFycmF5Pn1cbiAgICpcbiAgICogQHRvZG8gV3JpdGUgbW9yZSBkb2NzIG9uIGBvcHRpb25zYFxuICAgKi9cbiAgZmluZChwcmUgPSB7fSwgcG9zdCA9IHt9KSB7XG4gICAgY29uc3QgeyBjb25uLCBzY2hlbWEgfSA9IHRoaXM7XG4gICAgY29uc3QgeyB0eXBlIH0gPSBzY2hlbWE7XG5cbiAgICBsZXQgdGFibGUgPSByLnRhYmxlKHR5cGUpO1xuXG4gICAgdGFibGUgPSBhcHBseU9wdGlvbnModGFibGUsIHByZSk7XG4gICAgdGFibGUgPSBtZXJnZVJlbGF0aW9uc2hpcHModGFibGUsIHNjaGVtYSwgcHJlKTtcbiAgICB0YWJsZSA9IGFwcGx5T3B0aW9ucyh0YWJsZSwgcG9zdCk7XG4gICAgdGFibGUgPSB0YWJsZS5jb2VyY2VUbygnYXJyYXknKTtcblxuICAgIHJldHVybiB0YWJsZS5ydW4oY29ubilcbiAgICAgIC50aGVuKHJlY29yZHMgPT4gbmV3IFJlc291cmNlQXJyYXkoY29ubiwgc2NoZW1hLCByZWNvcmRzKSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZHMgdGhlIGZpcnN0IHJlc291cmNlIChvdXQgb2YgcG90ZW50aWFsbHkgbWFueSkgdGhhdCBtYXRjaGVzIHRoZSBjcml0ZXJpYSBpbiBgb3B0aW9ucy5gXG4gICAqXG4gICAqIEBhc3luY1xuICAgKiBAbWV0aG9kIGZpbmRPbmVcbiAgICogQHBhcmFtIHtPYmplY3R9IFtwcmU9e31dIC0gQ3JpdGVyYSBiZWZvcmUgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcGFyYW0ge09iamVjdH0gW3Bvc3Q9e31dIC0gQ3JpdGVyYSBhZnRlciBtZXJnaW5nIHJlbGF0aW9uc2hpcHMuXG4gICAqIEByZXR1cm4ge1Byb21pc2U8UmVzb3VyY2U+fVxuICAgKi9cbiAgZmluZE9uZShwcmUgPSB7fSwgcG9zdCA9IHt9KSB7XG4gICAgcmV0dXJuIHRoaXMuZmluZChwcmUsIHBvc3QpLnRoZW4ocmVzb3VyY2VzID0+IHJlc291cmNlcy5maXJzdCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSB0b3RhbCBjb3VudCBvZiByZXNvdXJjZXMuXG4gICAqIEByZXR1cm4ge051bWJlcn1cbiAgICovXG4gIGNvdW50KCkge1xuICAgIGNvbnN0IHsgY29ubiwgc2NoZW1hIH0gPSB0aGlzO1xuICAgIGNvbnN0IHsgdHlwZSB9ID0gc2NoZW1hO1xuXG4gICAgcmV0dXJuIHIudGFibGUodHlwZSkuY291bnQoKS5ydW4oY29ubik7XG4gIH1cblxuICAvKipcbiAgICogRmluZHMgcmVzb3VyY2VzIHVzaW5nIHRoZSBpbmRleCBuYW1lZCBgaW5kZXhgLlxuICAgKlxuICAgKiBgYGBcbiAgICogbW9kZWwoJ3VzZXInKS5maW5kQnlJbmRleCgnZW1haWwnLCAnZHlsYW5zbGFja0BnbWFpbC5jb20nKS50aGVuKHVzZXJzID0+IHtcbiAgICogICAvLyBSZXNvdXJjZUFycmF5XG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICpcbiAgICogQGFzeW5jXG4gICAqIEBtZXRob2QgZmluZEJ5SW5kZXhcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluZGV4IC0gVGhlIGluZGV4IG5hbWUuXG4gICAqIEBwYXJhbSB7Kn0gdmFsdWVcbiAgICogQHBhcmFtIHtPYmplY3R9IFtwcmU9e31dIC0gQ3JpdGVyYSBiZWZvcmUgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcGFyYW0ge09iamVjdH0gW3Bvc3Q9e31dIC0gQ3JpdGVyYSBhZnRlciBtZXJnaW5nIHJlbGF0aW9uc2hpcHMuXG4gICAqIEByZXR1cm4ge1Byb21pc2U8UmVzb3VyY2VBcnJheT59XG4gICAqXG4gICAqIEB0b2RvIEFkZCB0ZXN0LlxuICAgKi9cbiAgZmluZEJ5SW5kZXgoaW5kZXgsIHZhbHVlLCBwcmUgPSB7fSwgcG9zdCA9IHt9KSB7XG4gICAgY29uc3QgeyBjb25uLCBzY2hlbWEgfSA9IHRoaXM7XG4gICAgY29uc3QgeyB0eXBlIH0gPSBzY2hlbWE7XG5cbiAgICBsZXQgdGFibGUgPSByLnRhYmxlKHR5cGUpO1xuXG4gICAgdGFibGUgPSB0YWJsZS5nZXRBbGwodmFsdWUsIHsgaW5kZXggfSk7XG4gICAgdGFibGUgPSBhcHBseU9wdGlvbnModGFibGUsIHByZSk7XG4gICAgdGFibGUgPSBtZXJnZVJlbGF0aW9uc2hpcHModGFibGUsIHNjaGVtYSwgcHJlKTtcbiAgICB0YWJsZSA9IGFwcGx5T3B0aW9ucyh0YWJsZSwgcG9zdCk7XG4gICAgdGFibGUgPSB0YWJsZS5jb2VyY2VUbygnYXJyYXknKTtcblxuICAgIHJldHVybiB0YWJsZS5ydW4oY29ubilcbiAgICAgIC50aGVuKHJlY29yZHMgPT4gbmV3IFJlc291cmNlQXJyYXkoY29ubiwgc2NoZW1hLCByZWNvcmRzKSk7XG4gIH1cblxuICAvKipcbiAgICogRmluZHMgYSBzaW5nbGUgcmVzb3VyY2UgZnJvbSBgaW5kZXhgIHRoYXQgbWF0Y2hlcyBgdmFsdWVgIGFuZCB0aGF0IG1hdGNoZXMgdGhlIGNyaXRlcmlhIGluXG4gICAqIGBvcHRpb25zYC5cbiAgICpcbiAgICogQGFzeW5jXG4gICAqIEBtZXRob2QgZmluZE9uZUJ5SW5kZXhcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluZGV4IC0gVGhlIGluZGV4IG5hbWUuXG4gICAqIEBwYXJhbSB7Kn0gdmFsdWVcbiAgICogQHBhcmFtIHtPYmplY3R9IFtwcmU9e31dIC0gQ3JpdGVyYSBiZWZvcmUgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcGFyYW0ge09iamVjdH0gW3Bvc3Q9e31dIC0gQ3JpdGVyYSBhZnRlciBtZXJnaW5nIHJlbGF0aW9uc2hpcHMuXG4gICAqIEByZXR1cm4ge1Byb21pc2U8UmVzb3VyY2U+fVxuICAgKi9cbiAgZmluZE9uZUJ5SW5kZXgoaW5kZXgsIHZhbHVlLCBwcmUgPSB7fSwgcG9zdCA9IHt9KSB7XG4gICAgcmV0dXJuIHRoaXMuZmluZEJ5SW5kZXgoaW5kZXgsIHZhbHVlLCBwcmUsIHBvc3QpLnRoZW4ocmVzb3VyY2VzID0+IHJlc291cmNlcy5maXJzdCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgdGhlIHJlc291cmNlKHMpIHJlbGF0ZWQgdG8gYSBwYXJ0aWN1bGFyIHJlc291cmNlIGlkZW50aWZpZWQgYnkgYGlkYCBhY2NvcmRpbmcgdG9cbiAgICogYHJlbGF0aW9uc2hpcGAuIEEgcmVsYXRpb25zaGlwIHdpdGggYSByZWxhdGlvbiBvZiBgaGFzTWFueWAgcmV0dXJucyBhIGBSZXNvdXJjZUFycmF5YCwgYW5kIGFcbiAgICogcmVsYXRpb25zaGlwIHdpdGggYSByZWxhdGlvbiBvZiBgaGFzT25lYCBvciBgYmVsb25nc1RvYCByZXR1cm5zIGEgYFJlc291cmNlYC5cbiAgICpcbiAgICogYGBganNcbiAgICogLy8gcHJldGVuZCB1c2VyIGAxYCBoYXMgYSB0b24gb2YgcGV0c1xuICAgKiBtb2RlbCgndXNlcicpLmZpbmRSZWxhdGVkKCcxJywgJ3BldHMnLCB7XG4gICAqICAgZmlsdGVyOiB7IHNwZWNpZXM6ICdoYW1zdGVyJyB9LFxuICAgKiB9KS50aGVuKHBldHMgPT4ge1xuICAgKiAgIC8vIFJlc291cmNlQXJyYXlcbiAgICogfSk7XG4gICAqXG4gICAqIC8vIHByZXRlbmQgdXNlciBgMWAgaGFzIGEgY29tcGFueVxuICAgKiBtb2RlbCgndXNlcicpLmZpbmRSZWxhdGVkKCcxJywgJ2NvbXBhbnknKS50aGVuKGNvbXBhbnkgPT4ge1xuICAgKiAgIC8vIFJlc291cmNlXG4gICAqIH0pO1xuICAgKiBgYGBcbiAgICpcbiAgICogQGFzeW5jXG4gICAqIEBtZXRob2QgZmluZFJlbGF0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGlkIC0gVGhlIGlkIG9mIHRoZSBwYXJlbnQgcmVzb3VyY2UuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSByZWxhdGlvbnNoaXAgLSBUaGUgcmVsYXRpb25zaGlwIHRvIHRoZSBwYXJlbnQuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbcHJlPXt9XSAtIENyaXRlcmEgYmVmb3JlIG1lcmdpbmcgcmVsYXRpb25zaGlwcy5cbiAgICogQHBhcmFtIHtPYmplY3R9IFtwb3N0PXt9XSAtIENyaXRlcmEgYWZ0ZXIgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZXNvdXJjZXxSZXNvdXJjZUFycmF5Pn1cbiAgICovXG4gIGZpbmRSZWxhdGVkKGlkLCByZWxhdGlvbnNoaXAsIHByZSA9IHt9LCBwb3N0ID0ge30pIHtcbiAgICBjb25zdCB7IGNvbm4sIHNjaGVtYSwgc2NoZW1hOiB7IHR5cGU6IHBhcmVudFR5cGUgfSB9ID0gdGhpcztcblxuICAgIGNvbnN0IHtcbiAgICAgIHR5cGU6IHJlbGF0ZWRUeXBlLFxuICAgICAgc2NoZW1hOiByZWxhdGVkU2NoZW1hLFxuICAgICAgcmVsYXRpb24sXG4gICAgICBpbnZlcnNlLFxuICAgIH0gPSBzY2hlbWEucmVsYXRpb25zaGlwc1tyZWxhdGlvbnNoaXBdO1xuXG4gICAgbGV0IHRhYmxlID0gci50YWJsZShyZWxhdGVkVHlwZSk7XG5cbiAgICBpZiAocmVsYXRpb24gPT09ICdoYXNNYW55Jykge1xuICAgICAgaWYgKHJlcXVpcmVzSW5kZXgocmVsYXRpb24sIGludmVyc2UucmVsYXRpb24pKSB7XG4gICAgICAgIHRhYmxlID0gdGFibGUuZ2V0QWxsKGlkLCB7IGluZGV4OiBpbnZlcnNlLmZpZWxkIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGFibGUgPSB0YWJsZS5nZXRBbGwoXG4gICAgICAgICAgci5hcmdzKHIudGFibGUocGFyZW50VHlwZSkuZ2V0KGlkKShyZWxhdGlvbnNoaXApKCdpZCcpKVxuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICB0YWJsZSA9IHRhYmxlLmNvZXJjZVRvKCdhcnJheScpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0YWJsZSA9IHRhYmxlLmdldChcbiAgICAgICAgci50YWJsZShwYXJlbnRUeXBlKS5nZXQoaWQpKHJlbGF0aW9uc2hpcCkoJ2lkJylcbiAgICAgICk7XG4gICAgfVxuXG4gICAgdGFibGUgPSBhcHBseU9wdGlvbnModGFibGUsIHByZSk7XG4gICAgdGFibGUgPSBtZXJnZVJlbGF0aW9uc2hpcHModGFibGUsIHJlbGF0ZWRTY2hlbWEsIHByZSk7XG4gICAgdGFibGUgPSBhcHBseU9wdGlvbnModGFibGUsIHBvc3QpO1xuXG4gICAgcmV0dXJuIHRhYmxlLnJ1bihjb25uKVxuICAgICAgLnRoZW4ocmVjb3JkT3JSZWNvcmRzID0+IHtcbiAgICAgICAgaWYgKHJlbGF0aW9uID09PSAnaGFzTWFueScpIHJldHVybiBuZXcgUmVzb3VyY2VBcnJheShjb25uLCByZWxhdGVkU2NoZW1hLCByZWNvcmRPclJlY29yZHMpO1xuICAgICAgICByZXR1cm4gbmV3IFJlc291cmNlKGNvbm4sIHJlbGF0ZWRTY2hlbWEsIHJlY29yZE9yUmVjb3Jkcyk7XG4gICAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXRyaWV2ZXMgdGhlIHJlc291cmNlIGNvcnJlc3BvbmRpbmcgdG8gYGlkYC5cbiAgICpcbiAgICogYGBganNcbiAgICogbW9kZWwoJ3VzZXInKS5mZXRjaFJlc291cmNlKCcxJykudGhlbih1c2VyID0+IHtcbiAgICogICAvLyBSZXNvdXJjZVxuICAgKiB9KTtcbiAgICogYGBgXG4gICAqXG4gICAqIEBhc3luY1xuICAgKiBAbWV0aG9kIGZldGNoUmVzb3VyY2VcbiAgICogQHBhcmFtIHtTdHJpbmd9IGlkIC0gVGhlIElEIG9mIHRoZSByZXNvdXJjZSB0byByZXRyaWV2ZS5cbiAgICogQHBhcmFtIHtPYmplY3R9IFtwcmU9e31dIC0gQ3JpdGVyYSBiZWZvcmUgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcGFyYW0ge09iamVjdH0gW3Bvc3Q9e31dIC0gQ3JpdGVyYSBhZnRlciBtZXJnaW5nIHJlbGF0aW9uc2hpcHMuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFJlc291cmNlPn1cbiAgICovXG4gIGZldGNoUmVzb3VyY2UoaWQsIHByZSA9IHt9LCBwb3N0ID0ge30pIHtcbiAgICBjb25zdCB7IGNvbm4sIHNjaGVtYSB9ID0gdGhpcztcbiAgICBsZXQgdGFibGUgPSByLnRhYmxlKHNjaGVtYS50eXBlKTtcblxuICAgIHRhYmxlID0gdGFibGUuZ2V0KGlkKTtcbiAgICB0YWJsZSA9IGFwcGx5T3B0aW9ucyh0YWJsZSwgcHJlKTtcbiAgICB0YWJsZSA9IG1lcmdlUmVsYXRpb25zaGlwcyh0YWJsZSwgc2NoZW1hLCBwcmUpO1xuICAgIHRhYmxlID0gYXBwbHlPcHRpb25zKHRhYmxlLCBwb3N0KTtcblxuICAgIHJldHVybiB0YWJsZS5ydW4oY29ubilcbiAgICAgIC50aGVuKHJlY29yZCA9PiBuZXcgUmVzb3VyY2UoY29ubiwgc2NoZW1hLCByZWNvcmQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQZXJzaXN0cyBhIHJlY29yZCBpbiB0aGUgZGF0YWJhc2UgYW5kIGNyZWF0ZXMgYSByZXNvdXJjZS4gVGhlICdyZWNvcmQnIG9iamVjdCBtdXN0IGJlIGFcbiAgICogZmxhdHRlbmVuIEpTT04gd2l0aCBhdHRyaWJ1dGVzIGFuZCByZWxhdGlvbnNoaXBzLiBBbGwgcmVsYXRpb25zaGlwcyBpbiBgcmVjb3JkYCBtdXN0IGJlXG4gICAqIHJlcHJlc2VudGVkIGJ5IGVpdGhlciBhIHN0cmluZyBmb3IgYSBgaGFzT25lYCBvciBgYmVsb25nc1RvYCByZWxhdGlvbiwgb3IgYW4gYXJyYXkgb2Ygc3RyaW5nc1xuICAgKiBmb3IgYSBgaGFzTWFueWAgcmVsYXRpb24uXG4gICAqXG4gICAqIGBgYGpzXG4gICAqIG1vZGVsKCd1c2VyJykuY3JlYXRlKHtcbiAgICogICBuYW1lOiAnRHlsYW4nLFxuICAgKiAgIGVtYWlsOiAnZHlsYW5zbGFja0BnbWFpbC5jb20nLFxuICAgKiAgIHBhc3N3b3JkOiAnaGFzaGVkcGFzc3dvcmQnLFxuICAgKiAgIHBldHM6IFsgJzEnLCAnMicsICczJyBdLFxuICAgKiAgIGNvbXBhbnk6ICcxJyxcbiAgICogfSkudGhlbih1c2VyID0+IHtcbiAgICogICAvLyBSZXNvdXJjZVxuICAgKiB9KTtcbiAgICogYGBgXG4gICAqXG4gICAqIEBhc3luY1xuICAgKiBAbWV0aG9kIGNyZWF0ZVxuICAgKiBAcGFyYW0ge09iamVjdH0gcmVjb3JkXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBbcHJlPXt9XSAtIENyaXRlcmEgYmVmb3JlIG1lcmdpbmcgcmVsYXRpb25zaGlwcy5cbiAgICogQHBhcmFtIHtPYmplY3R9IFtwb3N0PXt9XSAtIENyaXRlcmEgYWZ0ZXIgbWVyZ2luZyByZWxhdGlvbnNoaXBzLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxSZXNvdXJjZT59XG4gICAqL1xuICBjcmVhdGUocmVjb3JkLCBwcmUgPSB7fSwgcG9zdCA9IHt9KSB7XG4gICAgY29uc3QgeyBjb25uLCBzY2hlbWEgfSA9IHRoaXM7XG4gICAgY29uc3QgeyB0eXBlIH0gPSBzY2hlbWE7XG5cbiAgICBjb25zdCBjaGVja0NvbXBsaWFuY2VBbmROb3JtYWxpemVSZWNvcmQgPSAoY29tcGxpYW50KSA9PiB7XG4gICAgICBpZiAoIWNvbXBsaWFudCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1RyaWVkIHRvIGNyZWF0ZSBhIHJlY29yZCwgYnV0IFxcJ3JlY29yZFxcJyBoYWQgc29tZSBpbnZhbGlkIHJlbGF0aW9uc2hpcHMuJ1xuICAgICAgICApO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gbm9ybWFsaXplUmVjb3JkKHJlY29yZCwgc2NoZW1hKTtcbiAgICB9O1xuXG4gICAgY29uc3QgY3JlYXRlUmVjb3JkID0gKG5vcm1hbGl6ZWRSZWNvcmQpID0+IHtcbiAgICAgIGxldCBjcmVhdGVkUmVjb3JkSWQ7XG4gICAgICBsZXQgY3JlYXRlZFJlc291cmNlO1xuXG4gICAgICByZXR1cm4gci50YWJsZSh0eXBlKS5pbnNlcnQobm9ybWFsaXplZFJlY29yZCkucnVuKGNvbm4pXG5cbiAgICAgICAgLy8gcmV0cmlldmUgdGhlIHJlY29yZCB0aGF0IHdhcyBqdXN0IGNyZWF0ZWRcbiAgICAgICAgLnRoZW4oKHsgZ2VuZXJhdGVkX2tleXM6IGtleXMgfSkgPT4ge1xuICAgICAgICAgIGxldCB0YWJsZSA9IHIudGFibGUodHlwZSk7XG4gICAgICAgICAgY3JlYXRlZFJlY29yZElkID0ga2V5c1swXTtcblxuICAgICAgICAgIHRhYmxlID0gdGFibGUuZ2V0KGNyZWF0ZWRSZWNvcmRJZCk7XG4gICAgICAgICAgdGFibGUgPSBhcHBseU9wdGlvbnModGFibGUsIHByZSk7XG4gICAgICAgICAgdGFibGUgPSBtZXJnZVJlbGF0aW9uc2hpcHModGFibGUsIHNjaGVtYSwgcHJlKTtcbiAgICAgICAgICB0YWJsZSA9IGFwcGx5T3B0aW9ucyh0YWJsZSwgcG9zdCk7XG5cbiAgICAgICAgICByZXR1cm4gdGFibGUucnVuKGNvbm4pO1xuICAgICAgICB9KVxuXG4gICAgICAgIC8vIGNyZWF0ZSB0aGUgcmVzb3VyY2UgYW5kIHN5bmMgaXRzIHJlbGF0aW9uc2hpcHNcbiAgICAgICAgLnRoZW4oY3JlYXRlZFJlY29yZCA9PiB7XG4gICAgICAgICAgY3JlYXRlZFJlc291cmNlID0gbmV3IFJlc291cmNlKGNvbm4sIHNjaGVtYSwgY3JlYXRlZFJlY29yZCk7XG4gICAgICAgICAgY29uc3Qgc3luY1JlbGF0aW9uc2hpcHNBcnJheSA9IHN5bmNSZWxhdGlvbnNoaXBzKHJlY29yZCwgc2NoZW1hLCBjcmVhdGVkUmVjb3JkSWQpO1xuXG4gICAgICAgICAgcmV0dXJuIHIuZG8oc3luY1JlbGF0aW9uc2hpcHNBcnJheSkucnVuKGNvbm4pO1xuICAgICAgICB9KVxuXG4gICAgICAgIC8vIHJldHVybiB0aGUgcmVzb3VyY2VcbiAgICAgICAgLnRoZW4oKCkgPT4gY3JlYXRlZFJlc291cmNlKTtcbiAgICB9O1xuXG4gICAgLy8gY2hlY2sgcmVjb3JkIGFuZCBpdCdzIHJlbGF0aW9uc2hpcHMgZm9yIFJlZGluayBjb25zdHJhaW50c1xuICAgIHJldHVybiBpc0NyZWF0ZUNvbXBsaWFudChyZWNvcmQsIHNjaGVtYSwgY29ubilcbiAgICAgIC50aGVuKGNoZWNrQ29tcGxpYW5jZUFuZE5vcm1hbGl6ZVJlY29yZClcbiAgICAgIC50aGVuKGNyZWF0ZVJlY29yZCk7XG4gIH1cbn1cbiJdfQ==