@decaf-ts/core
Version:
Core persistence module for the decaf framework
187 lines • 26.2 kB
JavaScript
import { GroupOperator, Operator, QueryError, } from "./../query/index.js";
import { RamPaginator } from "./RamPaginator.js";
import { InternalError } from "@decaf-ts/db-decorators";
import { Statement } from "./../query/Statement.js";
import { Reflection } from "@decaf-ts/reflection";
/**
* @description RAM-specific query statement builder
* @summary Extends the base Statement class to provide query building functionality for the RAM adapter.
* This class translates high-level query operations into predicates that can filter and sort
* in-memory data structures.
* @template M - The model type being queried
* @template R - The result type returned by the query
* @param {RamAdapter} adapter - The RAM adapter instance to use for executing queries
* @class RamStatement
* @category Ram
* @example
* ```typescript
* // Create a statement for querying User models
* const statement = new RamStatement<User, User>(ramAdapter);
*
* // Build a query to find active users with age > 18
* const results = await statement
* .from(User)
* .where(Condition.and(
* Condition.eq('active', true),
* Condition.gt('age', 18)
* ))
* .orderBy('lastName', 'asc')
* .limit(10)
* .execute();
* ```
*/
export class RamStatement extends Statement {
constructor(adapter) {
super(adapter);
}
/**
* @description Creates a sort comparator function
* @summary Generates a function that compares two model instances based on the orderBy criteria.
* This method handles different data types (string, number, date) and sort directions (asc, desc).
* @return {function(Model, Model): number} A comparator function for sorting model instances
*/
getSort() {
return (el1, el2) => {
if (!this.orderBySelector)
throw new InternalError("orderBySelector not set. Should be impossible");
const selector = this.orderBySelector;
const [key, direction] = selector;
const type = Reflection.getTypeFromDecorator(el1, key);
if (!type)
throw new QueryError(`type not compatible with sorting: ${type}`);
switch (type) {
case "string":
case "String":
return ((direction === "asc" ? 1 : -1) *
el1[key].localeCompare(el2[key]));
case "number":
case "Number":
return ((direction === "asc" ? 1 : -1) *
(el1[key] -
el2[key]));
case "object":
case "Object":
if (el1[key] instanceof Date &&
el2[key] instanceof Date)
return ((direction === "asc" ? 1 : -1) *
(el1[key].valueOf() -
el2[key].valueOf()));
throw new QueryError(`Sorting not supported for not date classes`);
default:
throw new QueryError(`sorting not supported for type ${type}`);
}
};
}
/**
* @description Builds a RAM query from the statement
* @summary Converts the statement's selectors and conditions into a RawRamQuery object
* that can be executed by the RAM adapter. This method assembles all query components
* (select, from, where, limit, offset, sort) into the final query structure.
* @return {RawRamQuery<M>} The constructed RAM query object
*/
build() {
const result = {
select: this.selectSelector,
from: this.fromSelector,
where: this.whereCondition
? this.parseCondition(this.whereCondition).where
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
(el) => {
return true;
},
limit: this.limitSelector,
skip: this.offsetSelector,
};
if (this.orderBySelector)
result.sort = this.getSort();
return result;
}
/**
* @description Creates a paginator for the query
* @summary Builds the query and wraps it in a RamPaginator to enable pagination of results.
* This allows retrieving large result sets in smaller chunks.
* @param {number} size - The page size (number of results per page)
* @return {Promise<Paginator<M, R, RawRamQuery<M>>>} A promise that resolves to a paginator for the query
*/
async paginate(size) {
try {
const query = this.build();
return new RamPaginator(this.adapter, query, size, this.fromSelector);
}
catch (e) {
throw new InternalError(e);
}
}
/**
* @description Parses a condition into a RAM query predicate
* @summary Converts a Condition object into a predicate function that can be used
* to filter model instances in memory. This method handles both simple conditions
* (equals, greater than, etc.) and complex conditions with logical operators (AND, OR).
* @template M - The model type for the condition
* @param {Condition<M>} condition - The condition to parse
* @return {RawRamQuery<M>} A RAM query object with a where predicate function
* @mermaid
* sequenceDiagram
* participant Caller
* participant RamStatement
* participant SimpleCondition
* participant ComplexCondition
*
* Caller->>RamStatement: parseCondition(condition)
* alt Simple condition (eq, gt, lt, etc.)
* RamStatement->>SimpleCondition: Extract attr1, operator, comparison
* SimpleCondition-->>RamStatement: Return predicate function
* else Logical operator (AND, OR)
* RamStatement->>ComplexCondition: Extract nested conditions
* RamStatement->>RamStatement: parseCondition(leftCondition)
* RamStatement->>RamStatement: parseCondition(rightCondition)
* ComplexCondition-->>RamStatement: Combine predicates with logical operator
* end
* RamStatement-->>Caller: Return query with where predicate
*/
parseCondition(condition) {
return {
where: (m) => {
const { attr1, operator, comparison } = condition;
if ([GroupOperator.AND, GroupOperator.OR, Operator.NOT].indexOf(operator) === -1) {
switch (operator) {
case Operator.BIGGER:
return m[attr1] > comparison;
case Operator.BIGGER_EQ:
return m[attr1] >= comparison;
case Operator.DIFFERENT:
return m[attr1] !== comparison;
case Operator.EQUAL:
return m[attr1] === comparison;
case Operator.REGEXP:
if (typeof m[attr1] !== "string")
throw new QueryError(`Invalid regexp comparison on a non string attribute: ${m[attr1]}`);
return !!m[attr1].match(new RegExp(comparison, "g"));
case Operator.SMALLER:
return m[attr1] < comparison;
case Operator.SMALLER_EQ:
return m[attr1] <= comparison;
default:
throw new InternalError(`Invalid operator for standard comparisons: ${operator}`);
}
}
else if (operator === Operator.NOT) {
throw new InternalError("Not implemented");
}
else {
const op1 = this.parseCondition(attr1);
const op2 = this.parseCondition(comparison);
switch (operator) {
case GroupOperator.AND:
return op1.where(m) && op2.where(m);
case GroupOperator.OR:
return op1.where(m) || op2.where(m);
default:
throw new InternalError(`Invalid operator for And/Or comparisons: ${operator}`);
}
}
},
};
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmFtU3RhdGVtZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3JhbS9SYW1TdGF0ZW1lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUVMLGFBQWEsRUFDYixRQUFRLEVBRVIsVUFBVSxHQUNYLDRCQUFpQjtBQUdsQixPQUFPLEVBQUUsWUFBWSxFQUFFLDBCQUF1QjtBQUM5QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDeEQsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQ0FBMkI7QUFDL0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBR2xEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBCRztBQUNILE1BQU0sT0FBTyxZQUFpQyxTQUFRLFNBSXJEO0lBQ0MsWUFBWSxPQUFtQjtRQUM3QixLQUFLLENBQUMsT0FBYyxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ssT0FBTztRQUNiLE9BQU8sQ0FBQyxHQUFVLEVBQUUsR0FBVSxFQUFFLEVBQUU7WUFDaEMsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlO2dCQUN2QixNQUFNLElBQUksYUFBYSxDQUNyQiwrQ0FBK0MsQ0FDaEQsQ0FBQztZQUNKLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxlQUFlLENBQUM7WUFDdEMsTUFBTSxDQUFDLEdBQUcsRUFBRSxTQUFTLENBQUMsR0FBRyxRQUFRLENBQUM7WUFDbEMsTUFBTSxJQUFJLEdBQUcsVUFBVSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxHQUFhLENBQUMsQ0FBQztZQUNqRSxJQUFJLENBQUMsSUFBSTtnQkFDUCxNQUFNLElBQUksVUFBVSxDQUFDLHFDQUFxQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1lBRXBFLFFBQVEsSUFBSSxFQUFFLENBQUM7Z0JBQ2IsS0FBSyxRQUFRLENBQUM7Z0JBQ2QsS0FBSyxRQUFRO29CQUNYLE9B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USxLQUFLLFFBQVEsQ0FBQyxHQUFHLEVBQUUsQ0FBQztvQkFDckMsTUFBTSxJQUFJLGFBQWEsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO2dCQUM3QyxDQUFDO3FCQUFNLENBQUM7b0JBQ04sTUFBTSxHQUFHLEdBQXFCLElBQUksQ0FBQyxjQUFjLENBQy9DLEtBQXFCLENBQ3RCLENBQUM7b0JBQ0YsTUFBTSxHQUFHLEdBQXFCLElBQUksQ0FBQyxjQUFjLENBQy9DLFVBQTBCLENBQzNCLENBQUM7b0JBQ0YsUUFBUSxRQUFRLEVBQUUsQ0FBQzt3QkFDakIsS0FBSyxhQUFhLENBQUMsR0FBRzs0QkFDcEIsT0FBTyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7d0JBQ3RDLEtBQUssYUFBYSxDQUFDLEVBQUU7NEJBQ25CLE9BQU8sR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO3dCQUN0Qzs0QkFDRSxNQUFNLElBQUksYUFBYSxDQUNyQiw0Q0FBNEMsUUFBUSxFQUFFLENBQ3ZELENBQUM7b0JBQ04sQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztTQUNrQixDQUFDO0lBQ3hCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIENvbmRpdGlvbixcbiAgR3JvdXBPcGVyYXRvcixcbiAgT3BlcmF0b3IsXG4gIFBhZ2luYXRvcixcbiAgUXVlcnlFcnJvcixcbn0gZnJvbSBcIi4uL3F1ZXJ5XCI7XG5pbXBvcnQgeyBSYXdSYW1RdWVyeSB9IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBNb2RlbCB9IGZyb20gXCJAZGVjYWYtdHMvZGVjb3JhdG9yLXZhbGlkYXRpb25cIjtcbmltcG9ydCB7IFJhbVBhZ2luYXRvciB9IGZyb20gXCIuL1JhbVBhZ2luYXRvclwiO1xuaW1wb3J0IHsgSW50ZXJuYWxFcnJvciB9IGZyb20gXCJAZGVjYWYtdHMvZGItZGVjb3JhdG9yc1wiO1xuaW1wb3J0IHsgU3RhdGVtZW50IH0gZnJvbSBcIi4uL3F1ZXJ5L1N0YXRlbWVudFwiO1xuaW1wb3J0IHsgUmVmbGVjdGlvbiB9IGZyb20gXCJAZGVjYWYtdHMvcmVmbGVjdGlvblwiO1xuaW1wb3J0IHsgUmFtQWRhcHRlciB9IGZyb20gXCIuL1JhbUFkYXB0ZXJcIjtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb24gUkFNLXNwZWNpZmljIHF1ZXJ5IHN0YXRlbWVudCBidWlsZGVyXG4gKiBAc3VtbWFyeSBFeHRlbmRzIHRoZSBiYXNlIFN0YXRlbWVudCBjbGFzcyB0byBwcm92aWRlIHF1ZXJ5IGJ1aWxkaW5nIGZ1bmN0aW9uYWxpdHkgZm9yIHRoZSBSQU0gYWRhcHRlci5cbiAqIFRoaXMgY2xhc3MgdHJhbnNsYXRlcyBoaWdoLWxldmVsIHF1ZXJ5IG9wZXJhdGlvbnMgaW50byBwcmVkaWNhdGVzIHRoYXQgY2FuIGZpbHRlciBhbmQgc29ydFxuICogaW4tbWVtb3J5IGRhdGEgc3RydWN0dXJlcy5cbiAqIEB0ZW1wbGF0ZSBNIC0gVGhlIG1vZGVsIHR5cGUgYmVpbmcgcXVlcmllZFxuICogQHRlbXBsYXRlIFIgLSBUaGUgcmVzdWx0IHR5cGUgcmV0dXJuZWQgYnkgdGhlIHF1ZXJ5XG4gKiBAcGFyYW0ge1JhbUFkYXB0ZXJ9IGFkYXB0ZXIgLSBUaGUgUkFNIGFkYXB0ZXIgaW5zdGFuY2UgdG8gdXNlIGZvciBleGVjdXRpbmcgcXVlcmllc1xuICogQGNsYXNzIFJhbVN0YXRlbWVudFxuICogQGNhdGVnb3J5IFJhbVxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIENyZWF0ZSBhIHN0YXRlbWVudCBmb3IgcXVlcnlpbmcgVXNlciBtb2RlbHNcbiAqIGNvbnN0IHN0YXRlbWVudCA9IG5ldyBSYW1TdGF0ZW1lbnQ8VXNlciwgVXNlcj4ocmFtQWRhcHRlcik7XG4gKlxuICogLy8gQnVpbGQgYSBxdWVyeSB0byBmaW5kIGFjdGl2ZSB1c2VycyB3aXRoIGFnZSA+IDE4XG4gKiBjb25zdCByZXN1bHRzID0gYXdhaXQgc3RhdGVtZW50XG4gKiAgIC5mcm9tKFVzZXIpXG4gKiAgIC53aGVyZShDb25kaXRpb24uYW5kKFxuICogICAgIENvbmRpdGlvbi5lcSgnYWN0aXZlJywgdHJ1ZSksXG4gKiAgICAgQ29uZGl0aW9uLmd0KCdhZ2UnLCAxOClcbiAqICAgKSlcbiAqICAgLm9yZGVyQnkoJ2xhc3ROYW1lJywgJ2FzYycpXG4gKiAgIC5saW1pdCgxMClcbiAqICAgLmV4ZWN1dGUoKTtcbiAqIGBgYFxuICovXG5leHBvcnQgY2xhc3MgUmFtU3RhdGVtZW50PE0gZXh0ZW5kcyBNb2RlbCwgUj4gZXh0ZW5kcyBTdGF0ZW1lbnQ8XG4gIFJhd1JhbVF1ZXJ5PE0+LFxuICBNLFxuICBSXG4+IHtcbiAgY29uc3RydWN0b3IoYWRhcHRlcjogUmFtQWRhcHRlcikge1xuICAgIHN1cGVyKGFkYXB0ZXIgYXMgYW55KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQ3JlYXRlcyBhIHNvcnQgY29tcGFyYXRvciBmdW5jdGlvblxuICAgKiBAc3VtbWFyeSBHZW5lcmF0ZXMgYSBmdW5jdGlvbiB0aGF0IGNvbXBhcmVzIHR3byBtb2RlbCBpbnN0YW5jZXMgYmFzZWQgb24gdGhlIG9yZGVyQnkgY3JpdGVyaWEuXG4gICAqIFRoaXMgbWV0aG9kIGhhbmRsZXMgZGlmZmVyZW50IGRhdGEgdHlwZXMgKHN0cmluZywgbnVtYmVyLCBkYXRlKSBhbmQgc29ydCBkaXJlY3Rpb25zIChhc2MsIGRlc2MpLlxuICAgKiBAcmV0dXJuIHtmdW5jdGlvbihNb2RlbCwgTW9kZWwpOiBudW1iZXJ9IEEgY29tcGFyYXRvciBmdW5jdGlvbiBmb3Igc29ydGluZyBtb2RlbCBpbnN0YW5jZXNcbiAgICovXG4gIHByaXZhdGUgZ2V0U29ydCgpIHtcbiAgICByZXR1cm4gKGVsMTogTW9kZWwsIGVsMjogTW9kZWwpID0+IHtcbiAgICAgIGlmICghdGhpcy5vcmRlckJ5U2VsZWN0b3IpXG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICAgIFwib3JkZXJCeVNlbGVjdG9yIG5vdCBzZXQuIFNob3VsZCBiZSBpbXBvc3NpYmxlXCJcbiAgICAgICAgKTtcbiAgICAgIGNvbnN0IHNlbGVjdG9yID0gdGhpcy5vcmRlckJ5U2VsZWN0b3I7XG4gICAgICBjb25zdCBba2V5LCBkaXJlY3Rpb25dID0gc2VsZWN0b3I7XG4gICAgICBjb25zdCB0eXBlID0gUmVmbGVjdGlvbi5nZXRUeXBlRnJvbURlY29yYXRvcihlbDEsIGtleSBhcyBzdHJpbmcpO1xuICAgICAgaWYgKCF0eXBlKVxuICAgICAgICB0aHJvdyBuZXcgUXVlcnlFcnJvcihgdHlwZSBub3QgY29tcGF0aWJsZSB3aXRoIHNvcnRpbmc6ICR7dHlwZX1gKTtcblxuICAgICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICAgIGNhc2UgXCJzdHJpbmdcIjpcbiAgICAgICAgY2FzZSBcIlN0cmluZ1wiOlxuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAoZGlyZWN0aW9uID09PSBcImFzY1wiID8gMSA6IC0xKSAqXG4gICAgICAgICAgICAoZWwxW2tleSBhcyBrZXlvZiBNb2RlbF0gYXMgdW5rbm93biBhcyBzdHJpbmcpLmxvY2FsZUNvbXBhcmUoXG4gICAgICAgICAgICAgIGVsMltrZXkgYXMga2V5b2YgTW9kZWxdIGFzIHVua25vd24gYXMgc3RyaW5nXG4gICAgICAgICAgICApXG4gICAgICAgICAgKTtcbiAgICAgICAgY2FzZSBcIm51bWJlclwiOlxuICAgICAgICBjYXNlIFwiTnVtYmVyXCI6XG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIChkaXJlY3Rpb24gPT09IFwiYXNjXCIgPyAxIDogLTEpICpcbiAgICAgICAgICAgICgoZWwxW2tleSBhcyBrZXlvZiBNb2RlbF0gYXMgdW5rbm93biBhcyBudW1iZXIpIC1cbiAgICAgICAgICAgICAgKGVsMltrZXkgYXMga2V5b2YgTW9kZWxdIGFzIHVua25vd24gYXMgbnVtYmVyKSlcbiAgICAgICAgICApO1xuICAgICAgICBjYXNlIFwib2JqZWN0XCI6XG4gICAgICAgIGNhc2UgXCJPYmplY3RcIjpcbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICBlbDFba2V5IGFzIGtleW9mIE1vZGVsXSBpbnN0YW5jZW9mIERhdGUgJiZcbiAgICAgICAgICAgIGVsMltrZXkgYXMga2V5b2YgTW9kZWxdIGluc3RhbmNlb2YgRGF0ZVxuICAgICAgICAgIClcbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgIChkaXJlY3Rpb24gPT09IFwiYXNjXCIgPyAxIDogLTEpICpcbiAgICAgICAgICAgICAgKChlbDFba2V5IGFzIGtleW9mIE1vZGVsXSBhcyB1bmtub3duIGFzIERhdGUpLnZhbHVlT2YoKSAtXG4gICAgICAgICAgICAgICAgKGVsMltrZXkgYXMga2V5b2YgTW9kZWxdIGFzIHVua25vd24gYXMgRGF0ZSkudmFsdWVPZigpKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB0aHJvdyBuZXcgUXVlcnlFcnJvcihgU29ydGluZyBub3Qgc3VwcG9ydGVkIGZvciBub3QgZGF0ZSBjbGFzc2VzYCk7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhyb3cgbmV3IFF1ZXJ5RXJyb3IoYHNvcnRpbmcgbm90IHN1cHBvcnRlZCBmb3IgdHlwZSAke3R5cGV9YCk7XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb24gQnVpbGRzIGEgUkFNIHF1ZXJ5IGZyb20gdGhlIHN0YXRlbWVudFxuICAgKiBAc3VtbWFyeSBDb252ZXJ0cyB0aGUgc3RhdGVtZW50J3Mgc2VsZWN0b3JzIGFuZCBjb25kaXRpb25zIGludG8gYSBSYXdSYW1RdWVyeSBvYmplY3RcbiAgICogdGhhdCBjYW4gYmUgZXhlY3V0ZWQgYnkgdGhlIFJBTSBhZGFwdGVyLiBUaGlzIG1ldGhvZCBhc3NlbWJsZXMgYWxsIHF1ZXJ5IGNvbXBvbmVudHNcbiAgICogKHNlbGVjdCwgZnJvbSwgd2hlcmUsIGxpbWl0LCBvZmZzZXQsIHNvcnQpIGludG8gdGhlIGZpbmFsIHF1ZXJ5IHN0cnVjdHVyZS5cbiAgICogQHJldHVybiB7UmF3UmFtUXVlcnk8TT59IFRoZSBjb25zdHJ1Y3RlZCBSQU0gcXVlcnkgb2JqZWN0XG4gICAqL1xuICBwcm90ZWN0ZWQgYnVpbGQoKTogUmF3UmFtUXVlcnk8TT4ge1xuICAgIGNvbnN0IHJlc3VsdDogUmF3UmFtUXVlcnk8TT4gPSB7XG4gICAgICBzZWxlY3Q6IHRoaXMuc2VsZWN0U2VsZWN0b3IsXG4gICAgICBmcm9tOiB0aGlzLmZyb21TZWxlY3RvcixcbiAgICAgIHdoZXJlOiB0aGlzLndoZXJlQ29uZGl0aW9uXG4gICAgICAgID8gdGhpcy5wYXJzZUNvbmRpdGlvbih0aGlzLndoZXJlQ29uZGl0aW9uKS53aGVyZVxuICAgICAgICA6IC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tdW51c2VkLXZhcnNcbiAgICAgICAgICAoZWw6IE0pID0+IHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgIH0sXG4gICAgICBsaW1pdDogdGhpcy5saW1pdFNlbGVjdG9yLFxuICAgICAgc2tpcDogdGhpcy5vZmZzZXRTZWxlY3RvcixcbiAgICB9O1xuICAgIGlmICh0aGlzLm9yZGVyQnlTZWxlY3RvcikgcmVzdWx0LnNvcnQgPSB0aGlzLmdldFNvcnQoKTtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBDcmVhdGVzIGEgcGFnaW5hdG9yIGZvciB0aGUgcXVlcnlcbiAgICogQHN1bW1hcnkgQnVpbGRzIHRoZSBxdWVyeSBhbmQgd3JhcHMgaXQgaW4gYSBSYW1QYWdpbmF0b3IgdG8gZW5hYmxlIHBhZ2luYXRpb24gb2YgcmVzdWx0cy5cbiAgICogVGhpcyBhbGxvd3MgcmV0cmlldmluZyBsYXJnZSByZXN1bHQgc2V0cyBpbiBzbWFsbGVyIGNodW5rcy5cbiAgICogQHBhcmFtIHtudW1iZXJ9IHNpemUgLSBUaGUgcGFnZSBzaXplIChudW1iZXIgb2YgcmVzdWx0cyBwZXIgcGFnZSlcbiAgICogQHJldHVybiB7UHJvbWlzZTxQYWdpbmF0b3I8TSwgUiwgUmF3UmFtUXVlcnk8TT4+Pn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgdG8gYSBwYWdpbmF0b3IgZm9yIHRoZSBxdWVyeVxuICAgKi9cbiAgYXN5bmMgcGFnaW5hdGUoc2l6ZTogbnVtYmVyKTogUHJvbWlzZTxQYWdpbmF0b3I8TSwgUiwgUmF3UmFtUXVlcnk8TT4+PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHF1ZXJ5ID0gdGhpcy5idWlsZCgpO1xuICAgICAgcmV0dXJuIG5ldyBSYW1QYWdpbmF0b3I8TSwgUj4oXG4gICAgICAgIHRoaXMuYWRhcHRlcixcbiAgICAgICAgcXVlcnksXG4gICAgICAgIHNpemUsXG4gICAgICAgIHRoaXMuZnJvbVNlbGVjdG9yXG4gICAgICApO1xuICAgIH0gY2F0Y2ggKGU6IGFueSkge1xuICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvbiBQYXJzZXMgYSBjb25kaXRpb24gaW50byBhIFJBTSBxdWVyeSBwcmVkaWNhdGVcbiAgICogQHN1bW1hcnkgQ29udmVydHMgYSBDb25kaXRpb24gb2JqZWN0IGludG8gYSBwcmVkaWNhdGUgZnVuY3Rpb24gdGhhdCBjYW4gYmUgdXNlZFxuICAgKiB0byBmaWx0ZXIgbW9kZWwgaW5zdGFuY2VzIGluIG1lbW9yeS4gVGhpcyBtZXRob2QgaGFuZGxlcyBib3RoIHNpbXBsZSBjb25kaXRpb25zXG4gICAqIChlcXVhbHMsIGdyZWF0ZXIgdGhhbiwgZXRjLikgYW5kIGNvbXBsZXggY29uZGl0aW9ucyB3aXRoIGxvZ2ljYWwgb3BlcmF0b3JzIChBTkQsIE9SKS5cbiAgICogQHRlbXBsYXRlIE0gLSBUaGUgbW9kZWwgdHlwZSBmb3IgdGhlIGNvbmRpdGlvblxuICAgKiBAcGFyYW0ge0NvbmRpdGlvbjxNPn0gY29uZGl0aW9uIC0gVGhlIGNvbmRpdGlvbiB0byBwYXJzZVxuICAgKiBAcmV0dXJuIHtSYXdSYW1RdWVyeTxNPn0gQSBSQU0gcXVlcnkgb2JqZWN0IHdpdGggYSB3aGVyZSBwcmVkaWNhdGUgZnVuY3Rpb25cbiAgICogQG1lcm1haWRcbiAgICogc2VxdWVuY2VEaWFncmFtXG4gICAqICAgcGFydGljaXBhbnQgQ2FsbGVyXG4gICAqICAgcGFydGljaXBhbnQgUmFtU3RhdGVtZW50XG4gICAqICAgcGFydGljaXBhbnQgU2ltcGxlQ29uZGl0aW9uXG4gICAqICAgcGFydGljaXBhbnQgQ29tcGxleENvbmRpdGlvblxuICAgKlxuICAgKiAgIENhbGxlci0+PlJhbVN0YXRlbWVudDogcGFyc2VDb25kaXRpb24oY29uZGl0aW9uKVxuICAgKiAgIGFsdCBTaW1wbGUgY29uZGl0aW9uIChlcSwgZ3QsIGx0LCBldGMuKVxuICAgKiAgICAgUmFtU3RhdGVtZW50LT4+U2ltcGxlQ29uZGl0aW9uOiBFeHRyYWN0IGF0dHIxLCBvcGVyYXRvciwgY29tcGFyaXNvblxuICAgKiAgICAgU2ltcGxlQ29uZGl0aW9uLS0+PlJhbVN0YXRlbWVudDogUmV0dXJuIHByZWRpY2F0ZSBmdW5jdGlvblxuICAgKiAgIGVsc2UgTG9naWNhbCBvcGVyYXRvciAoQU5ELCBPUilcbiAgICogICAgIFJhbVN0YXRlbWVudC0+PkNvbXBsZXhDb25kaXRpb246IEV4dHJhY3QgbmVzdGVkIGNvbmRpdGlvbnNcbiAgICogICAgIFJhbVN0YXRlbWVudC0+PlJhbVN0YXRlbWVudDogcGFyc2VDb25kaXRpb24obGVmdENvbmRpdGlvbilcbiAgICogICAgIFJhbVN0YXRlbWVudC0+PlJhbVN0YXRlbWVudDogcGFyc2VDb25kaXRpb24ocmlnaHRDb25kaXRpb24pXG4gICAqICAgICBDb21wbGV4Q29uZGl0aW9uLS0+PlJhbVN0YXRlbWVudDogQ29tYmluZSBwcmVkaWNhdGVzIHdpdGggbG9naWNhbCBvcGVyYXRvclxuICAgKiAgIGVuZFxuICAgKiAgIFJhbVN0YXRlbWVudC0tPj5DYWxsZXI6IFJldHVybiBxdWVyeSB3aXRoIHdoZXJlIHByZWRpY2F0ZVxuICAgKi9cbiAgcGFyc2VDb25kaXRpb248TSBleHRlbmRzIE1vZGVsPihjb25kaXRpb246IENvbmRpdGlvbjxNPik6IFJhd1JhbVF1ZXJ5PE0+IHtcbiAgICByZXR1cm4ge1xuICAgICAgd2hlcmU6IChtOiBNb2RlbCkgPT4ge1xuICAgICAgICBjb25zdCB7IGF0dHIxLCBvcGVyYXRvciwgY29tcGFyaXNvbiB9ID0gY29uZGl0aW9uIGFzIHVua25vd24gYXMge1xuICAgICAgICAgIGF0dHIxOiBzdHJpbmcgfCBDb25kaXRpb248TT47XG4gICAgICAgICAgb3BlcmF0b3I6IE9wZXJhdG9yIHwgR3JvdXBPcGVyYXRvcjtcbiAgICAgICAgICBjb21wYXJpc29uOiBhbnk7XG4gICAgICAgIH07XG5cbiAgICAgICAgaWYgKFxuICAgICAgICAgIFtHcm91cE9wZXJhdG9yLkFORCwgR3JvdXBPcGVyYXRvci5PUiwgT3BlcmF0b3IuTk9UXS5pbmRleE9mKFxuICAgICAgICAgICAgb3BlcmF0b3IgYXMgR3JvdXBPcGVyYXRvclxuICAgICAgICAgICkgPT09IC0xXG4gICAgICAgICkge1xuICAgICAgICAgIHN3aXRjaCAob3BlcmF0b3IpIHtcbiAgICAgICAgICAgIGNhc2UgT3BlcmF0b3IuQklHR0VSOlxuICAgICAgICAgICAgICByZXR1cm4gbVthdHRyMSBhcyBrZXlvZiBNb2RlbF0gPiBjb21wYXJpc29uO1xuICAgICAgICAgICAgY2FzZSBPcGVyYXRvci5CSUdHRVJfRVE6XG4gICAgICAgICAgICAgIHJldHVybiBtW2F0dHIxIGFzIGtleW9mIE1vZGVsXSA+PSBjb21wYXJpc29uO1xuICAgICAgICAgICAgY2FzZSBPcGVyYXRvci5ESUZGRVJFTlQ6XG4gICAgICAgICAgICAgIHJldHVybiBtW2F0dHIxIGFzIGtleW9mIE1vZGVsXSAhPT0gY29tcGFyaXNvbjtcbiAgICAgICAgICAgIGNhc2UgT3BlcmF0b3IuRVFVQUw6XG4gICAgICAgICAgICAgIHJldHVybiBtW2F0dHIxIGFzIGtleW9mIE1vZGVsXSA9PT0gY29tcGFyaXNvbjtcbiAgICAgICAgICAgIGNhc2UgT3BlcmF0b3IuUkVHRVhQOlxuICAgICAgICAgICAgICBpZiAodHlwZW9mIG1bYXR0cjEgYXMga2V5b2YgTW9kZWxdICE9PSBcInN0cmluZ1wiKVxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBRdWVyeUVycm9yKFxuICAgICAgICAgICAgICAgICAgYEludmFsaWQgcmVnZXhwIGNvbXBhcmlzb24gb24gYSBub24gc3RyaW5nIGF0dHJpYnV0ZTogJHttW2F0dHIxIGFzIGtleW9mIE1vZGVsXX1gXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgcmV0dXJuICEhKG1bYXR0cjEgYXMga2V5b2YgTW9kZWxdIGFzIHVua25vd24gYXMgc3RyaW5nKS5tYXRjaChcbiAgICAgICAgICAgICAgICBuZXcgUmVnRXhwKGNvbXBhcmlzb24sIFwiZ1wiKVxuICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgY2FzZSBPcGVyYXRvci5TTUFMTEVSOlxuICAgICAgICAgICAgICByZXR1cm4gbVthdHRyMSBhcyBrZXlvZiBNb2RlbF0gPCBjb21wYXJpc29uO1xuICAgICAgICAgICAgY2FzZSBPcGVyYXRvci5TTUFMTEVSX0VROlxuICAgICAgICAgICAgICByZXR1cm4gbVthdHRyMSBhcyBrZXlvZiBNb2RlbF0gPD0gY29tcGFyaXNvbjtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICAgICAgICAgIGBJbnZhbGlkIG9wZXJhdG9yIGZvciBzdGFuZGFyZCBjb21wYXJpc29uczogJHtvcGVyYXRvcn1gXG4gICAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKG9wZXJhdG9yID09PSBPcGVyYXRvci5OT1QpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihcIk5vdCBpbXBsZW1lbnRlZFwiKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBvcDE6IFJhd1JhbVF1ZXJ5PGFueT4gPSB0aGlzLnBhcnNlQ29uZGl0aW9uKFxuICAgICAgICAgICAgYXR0cjEgYXMgQ29uZGl0aW9uPE0+XG4gICAgICAgICAgKTtcbiAgICAgICAgICBjb25zdCBvcDI6IFJhd1JhbVF1ZXJ5PGFueT4gPSB0aGlzLnBhcnNlQ29uZGl0aW9uKFxuICAgICAgICAgICAgY29tcGFyaXNvbiBhcyBDb25kaXRpb248TT5cbiAgICAgICAgICApO1xuICAgICAgICAgIHN3aXRjaCAob3BlcmF0b3IpIHtcbiAgICAgICAgICAgIGNhc2UgR3JvdXBPcGVyYXRvci5BTkQ6XG4gICAgICAgICAgICAgIHJldHVybiBvcDEud2hlcmUobSkgJiYgb3AyLndoZXJlKG0pO1xuICAgICAgICAgICAgY2FzZSBHcm91cE9wZXJhdG9yLk9SOlxuICAgICAgICAgICAgICByZXR1cm4gb3AxLndoZXJlKG0pIHx8IG9wMi53aGVyZShtKTtcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFxuICAgICAgICAgICAgICAgIGBJbnZhbGlkIG9wZXJhdG9yIGZvciBBbmQvT3IgY29tcGFyaXNvbnM6ICR7b3BlcmF0b3J9YFxuICAgICAgICAgICAgICApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9IGFzIFJhd1JhbVF1ZXJ5PGFueT47XG4gIH1cbn1cbiJdfQ==