@decaf-ts/core
Version:
Core persistence module for the decaf framework
276 lines • 32.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MethodQueryBuilder = void 0;
const types_1 = require("./types.cjs");
const utils_1 = require("./utils.cjs");
const lowerFirst = (str) => str.charAt(0).toLowerCase() + str.slice(1);
/**
* @description
* Utility class to build query objects from repository method names.
*
* @summary
* The `MethodQueryBuilder` class parses method names that follow a specific naming convention
* (e.g., `findByNameAndAgeOrderByCountryAsc`) and converts them into structured query objects
* (`QueryAssist`). It extracts clauses such as `select`, `where`, `groupBy`, `orderBy`, `limit`,
* and `offset`, ensuring that developers can declare repository queries using expressive method names.
*
* @param methodName {string} - The repository method name to parse and convert into a query.
* @param values {any[]} - The values corresponding to method parameters used for query conditions.
*
* @return {QueryAssist} A structured query object describing the parsed action, select, where,
* groupBy, orderBy, limit, and offset clauses.
*
* @class
*
* @example
* ```ts
* const query = MethodQueryBuilder.build(
* "findByNameAndAgeOrderByCountryAsc",
* "John",
* 25,
* [["country", "ASC"]]
* );
*
* console.log(query);
* // {
* // action: "find",
* // select: undefined,
* // where: { ... },
* // groupBy: undefined,
* // orderBy: [["country", "ASC"]],
* // limit: undefined,
* // offset: undefined
* // }
* ```
*
* @mermaid
* sequenceDiagram
* participant Repo as Repository Method
* participant MQB as MethodQueryBuilder
* participant Query as QueryAssist
*
* Repo->>MQB: build(methodName, ...values)
* MQB->>MQB: extractCore(methodName)
* MQB->>MQB: extractSelect(methodName)
* MQB->>MQB: extractGroupBy(methodName)
* MQB->>MQB: buildWhere(core, values)
* MQB->>MQB: extractOrderLimitOffset(core, values)
* MQB->>Query: return structured QueryAssist object
*/
class MethodQueryBuilder {
/**
* @description
* Builds a `QueryAssist` object by parsing a repository method name and values.
*
* @summary
* The method validates the method name, extracts clauses (core, select, groupBy, where,
* orderBy, limit, and offset), and assembles them into a structured query object
* that can be executed against a data source.
*
* @param methodName {string} - The repository method name that encodes query information.
* @param values {any[]} - The values corresponding to conditions and extra clauses.
*
* @return {QueryAssist} A structured query object representing the parsed query.
*/
static build(methodName, ...values) {
if (!methodName.startsWith(types_1.QueryClause.FIND_BY)) {
throw new Error(`Unsupported method ${methodName}`);
}
const core = this.extractCore(methodName);
const select = this.extractSelect(methodName);
const groupBy = this.extractGroupBy(methodName);
// const orderBy = this.extractOrderBy(methodName);
const where = this.buildWhere(core, values);
const { orderBy, limit, offset } = this.extractOrderLimitOffset(core, values);
return {
action: "find",
select: select,
where,
groupBy,
orderBy,
limit,
offset,
};
}
/**
* @description
* Extracts the core part of the method name after `findBy` and before any special clauses.
*
* @summary
* Removes prefixes and detects delimiters (`Then`, `OrderBy`, `GroupBy`, `Limit`, `Offset`)
* to isolate the main conditional part of the query.
*
* @param methodName {string} - The method name to parse.
*
* @return {string} The extracted core string used for building conditions.
*/
static extractCore(methodName) {
const afterFindBy = methodName.substring(types_1.QueryClause.FIND_BY.length);
const regex = /(Then[A-Z]|OrderBy|GroupBy|Limit|Offset)/;
const match = afterFindBy.match(regex);
return match ? afterFindBy.substring(0, match.index) : afterFindBy;
}
/**
* @description
* Extracts the select clause from a method name.
*
* @summary
* Detects the `Select` keyword in the method name, isolates the fields following it,
* and returns them as an array of lowercase-first strings.
*
* @param methodName {string} - The method name to parse.
*
* @return {string[] | undefined} An array of selected fields or `undefined` if no select clause exists.
*/
static extractSelect(methodName) {
const selectIndex = methodName.indexOf(types_1.QueryClause.SELECT);
if (selectIndex === -1)
return undefined;
const afterSelect = methodName.substring(selectIndex + types_1.QueryClause.SELECT.length);
// Search for next Then, GroupBy, OrderBy...
const match = afterSelect.match(/(Then[A-Z]|OrderBy|GroupBy|Limit|Offset)/);
const selectPart = match
? afterSelect.substring(0, match.index)
: afterSelect;
return selectPart.split(types_1.QueryClause.AND).map(lowerFirst).filter(Boolean);
}
/**
* @description
* Extracts the group by clause from a method name.
*
* @summary
* Detects the `GroupBy` keyword in the method name, isolates the fields following it,
* and returns them as an array of lowercase-first strings.
*
* @param methodName {string} - The method name to parse.
*
* @return {string[] | undefined} An array of group by fields or `undefined` if no group by clause exists.
*/
static extractGroupBy(methodName) {
const groupByIndex = methodName.indexOf(types_1.QueryClause.GROUP_BY);
if (groupByIndex === -1)
return undefined;
const after = methodName.substring(groupByIndex + types_1.QueryClause.GROUP_BY.length);
const groupByPart = after.split(types_1.QueryClause.ORDER_BY)[0];
return groupByPart
.split(types_1.QueryClause.THEN_BY)
.map(lowerFirst)
.filter(Boolean);
}
// private static extractOrderBy(
// methodName: string
// ): OrderBySelector<any>[] | undefined {
// const orderByIndex = methodName.indexOf(QueryClause.ORDER_BY);
// if (orderByIndex === -1) return undefined;
//
// const after = methodName.substring(
// orderByIndex + QueryClause.ORDER_BY.length
// );
// const orderParts = after.split("ThenBy");
//
// return orderParts.map((part) => {
// const match = part.match(/(.*?)(Asc|Desc|Dsc)$/);
// if (!match) throw new Error(`Invalid OrderBy part: ${part}`);
// const [, field, dir] = match;
// return [
// lowerFirst(field),
// dir.toLowerCase() === "dsc"
// ? OrderDirection.DSC
// : (dir.toLowerCase() as OrderDirection),
// ];
// });
// }
/**
* @description
* Builds the `where` condition object based on the parsed core string and parameter values.
*
* @summary
* Splits the core string by logical operators (`And`, `Or`), parses each token into a field
* and operator, and combines them into a `Condition` object using the provided values.
*
* @param core {string} - The extracted core string from the method name.
* @param values {any[]} - The values corresponding to the conditions.
*
* @return {Condition<any>} A structured condition object representing the query's where clause.
*/
static buildWhere(core, values) {
const parts = core.split(/OrderBy|GroupBy/)[0] || "";
const conditions = parts.split(/And|Or/);
const operators = core.match(/And|Or/g) || [];
let where;
conditions.forEach((token, idx) => {
const { field, operator } = this.parseFieldAndOperator(token);
const parser = operator ? utils_1.OperatorsMap[operator] : utils_1.OperatorsMap.Equals;
if (!parser)
throw new Error(`Unsupported operator ${operator}`);
const conditionValue = values[idx];
if (typeof conditionValue === "undefined") {
throw new Error(`Invalid value for field ${field}`);
}
const condition = parser(field, conditionValue);
where =
idx === 0
? condition
: operators[idx - 1] === types_1.QueryClause.AND
? where.and(condition)
: where.or(condition);
});
if (!where)
throw new Error("No conditions found in method name");
return where;
}
/**
* @description
* Parses a field name and operator from a string token.
*
* @summary
* Identifies the operator suffix (if present) and returns a descriptor containing the field
* name in lowercase-first format along with the operator.
*
* @param str {string} - The token string to parse.
*
* @return {FilterDescriptor} An object containing the field name and operator.
*/
static parseFieldAndOperator(str) {
for (const operator of Object.keys(utils_1.OperatorsMap)) {
if (str.endsWith(operator)) {
const field = str.slice(0, -operator.length);
return { field: lowerFirst(field), operator };
}
}
return { field: lowerFirst(str) };
}
/**
* @description
* Extracts `orderBy`, `limit`, and `offset` clauses from method arguments.
*
* @summary
* Determines the number of condition arguments, then checks the remaining arguments
* to resolve sorting, limiting, and pagination.
*
* @param core {string} - The extracted core string from the method name.
* @param values {any[]} - The values corresponding to method arguments, including conditions and extras.
*
* @return {{
* orderBy?: OrderBySelector<any>[];
* limit?: number;
* offset?: number;
* }} An object containing orderBy, limit, and offset values if present.
*/
static extractOrderLimitOffset(core, values) {
const conditionCount = core.split(/And|Or/).length;
const extraArgs = values.slice(conditionCount);
let orderBy;
let limit;
let offset;
if (extraArgs.length >= 1 && Array.isArray(extraArgs[0]))
orderBy = extraArgs[0];
if (extraArgs.length >= 2 && typeof extraArgs[1] === "number")
limit = extraArgs[1];
if (extraArgs.length >= 3 && typeof extraArgs[2] === "number")
offset = extraArgs[2];
return { orderBy, limit, offset };
}
}
exports.MethodQueryBuilder = MethodQueryBuilder;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWV0aG9kUXVlcnlCdWlsZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3F1ZXJ5L01ldGhvZFF1ZXJ5QnVpbGRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSx1Q0FBcUU7QUFDckUsdUNBQXVDO0FBRXZDLE1BQU0sVUFBVSxHQUFHLENBQUMsR0FBVyxFQUFVLEVBQUUsQ0FDekMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRTdDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBb0RHO0FBQ0gsTUFBYSxrQkFBa0I7SUFDN0I7Ozs7Ozs7Ozs7Ozs7T0FhRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBa0IsRUFBRSxHQUFHLE1BQWE7UUFDL0MsSUFBSSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsbUJBQVcsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ2hELE1BQU0sSUFBSSxLQUFLLENBQUMsc0JBQXNCLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDdEQsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDMUMsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM5QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2hELG1EQUFtRDtRQUNuRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztRQUM1QyxNQUFNLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLENBQUMsdUJBQXVCLENBQzdELElBQUks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LENBQUMsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUN0RCxPQUFPLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBMkIsQ0FBQztRQUVuRCxJQUFJLFNBQVMsQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLE9BQU8sU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7WUFDM0QsS0FBSyxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUV2QixJQUFJLFNBQVMsQ0FBQyxNQUFNLElBQUksQ0FBQyxJQUFJLE9BQU8sU0FBUyxDQUFDLENBQUMsQ0FBQyxLQUFLLFFBQVE7WUFDM0QsTUFBTSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUV4QixPQUFPLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUNwQyxDQUFDO0NBQ0Y7QUFyUEQsZ0RBcVBDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29uZGl0aW9uLCBPcmRlckJ5U2VsZWN0b3IgfSBmcm9tIFwiLi4vcXVlcnlcIjtcbmltcG9ydCB7IEZpbHRlckRlc2NyaXB0b3IsIFF1ZXJ5QXNzaXN0LCBRdWVyeUNsYXVzZSB9IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBPcGVyYXRvcnNNYXAgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5jb25zdCBsb3dlckZpcnN0ID0gKHN0cjogc3RyaW5nKTogc3RyaW5nID0+XG4gIHN0ci5jaGFyQXQoMCkudG9Mb3dlckNhc2UoKSArIHN0ci5zbGljZSgxKTtcblxuLyoqXG4gKiBAZGVzY3JpcHRpb25cbiAqIFV0aWxpdHkgY2xhc3MgdG8gYnVpbGQgcXVlcnkgb2JqZWN0cyBmcm9tIHJlcG9zaXRvcnkgbWV0aG9kIG5hbWVzLlxuICpcbiAqIEBzdW1tYXJ5XG4gKiBUaGUgYE1ldGhvZFF1ZXJ5QnVpbGRlcmAgY2xhc3MgcGFyc2VzIG1ldGhvZCBuYW1lcyB0aGF0IGZvbGxvdyBhIHNwZWNpZmljIG5hbWluZyBjb252ZW50aW9uXG4gKiAoZS5nLiwgYGZpbmRCeU5hbWVBbmRBZ2VPcmRlckJ5Q291bnRyeUFzY2ApIGFuZCBjb252ZXJ0cyB0aGVtIGludG8gc3RydWN0dXJlZCBxdWVyeSBvYmplY3RzXG4gKiAoYFF1ZXJ5QXNzaXN0YCkuIEl0IGV4dHJhY3RzIGNsYXVzZXMgc3VjaCBhcyBgc2VsZWN0YCwgYHdoZXJlYCwgYGdyb3VwQnlgLCBgb3JkZXJCeWAsIGBsaW1pdGAsXG4gKiBhbmQgYG9mZnNldGAsIGVuc3VyaW5nIHRoYXQgZGV2ZWxvcGVycyBjYW4gZGVjbGFyZSByZXBvc2l0b3J5IHF1ZXJpZXMgdXNpbmcgZXhwcmVzc2l2ZSBtZXRob2QgbmFtZXMuXG4gKlxuICogQHBhcmFtIG1ldGhvZE5hbWUge3N0cmluZ30gLSBUaGUgcmVwb3NpdG9yeSBtZXRob2QgbmFtZSB0byBwYXJzZSBhbmQgY29udmVydCBpbnRvIGEgcXVlcnkuXG4gKiBAcGFyYW0gdmFsdWVzIHthbnlbXX0gLSBUaGUgdmFsdWVzIGNvcnJlc3BvbmRpbmcgdG8gbWV0aG9kIHBhcmFtZXRlcnMgdXNlZCBmb3IgcXVlcnkgY29uZGl0aW9ucy5cbiAqXG4gKiBAcmV0dXJuIHtRdWVyeUFzc2lzdH0gQSBzdHJ1Y3R1cmVkIHF1ZXJ5IG9iamVjdCBkZXNjcmliaW5nIHRoZSBwYXJzZWQgYWN0aW9uLCBzZWxlY3QsIHdoZXJlLFxuICogZ3JvdXBCeSwgb3JkZXJCeSwgbGltaXQsIGFuZCBvZmZzZXQgY2xhdXNlcy5cbiAqXG4gKiBAY2xhc3NcbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIGNvbnN0IHF1ZXJ5ID0gTWV0aG9kUXVlcnlCdWlsZGVyLmJ1aWxkKFxuICogICBcImZpbmRCeU5hbWVBbmRBZ2VPcmRlckJ5Q291bnRyeUFzY1wiLFxuICogICBcIkpvaG5cIixcbiAqICAgMjUsXG4gKiAgIFtbXCJjb3VudHJ5XCIsIFwiQVNDXCJdXVxuICogKTtcbiAqXG4gKiBjb25zb2xlLmxvZyhxdWVyeSk7XG4gKiAvLyB7XG4gKiAvLyAgIGFjdGlvbjogXCJmaW5kXCIsXG4gKiAvLyAgIHNlbGVjdDogdW5kZWZpbmVkLFxuICogLy8gICB3aGVyZTogeyAuLi4gfSxcbiAqIC8vICAgZ3JvdXBCeTogdW5kZWZpbmVkLFxuICogLy8gICBvcmRlckJ5OiBbW1wiY291bnRyeVwiLCBcIkFTQ1wiXV0sXG4gKiAvLyAgIGxpbWl0OiB1bmRlZmluZWQsXG4gKiAvLyAgIG9mZnNldDogdW5kZWZpbmVkXG4gKiAvLyB9XG4gKiBgYGBcbiAqXG4gKiBAbWVybWFpZFxuICogc2VxdWVuY2VEaWFncmFtXG4gKiAgIHBhcnRpY2lwYW50IFJlcG8gYXMgUmVwb3NpdG9yeSBNZXRob2RcbiAqICAgcGFydGljaXBhbnQgTVFCIGFzIE1ldGhvZFF1ZXJ5QnVpbGRlclxuICogICBwYXJ0aWNpcGFudCBRdWVyeSBhcyBRdWVyeUFzc2lzdFxuICpcbiAqICAgUmVwby0+Pk1RQjogYnVpbGQobWV0aG9kTmFtZSwgLi4udmFsdWVzKVxuICogICBNUUItPj5NUUI6IGV4dHJhY3RDb3JlKG1ldGhvZE5hbWUpXG4gKiAgIE1RQi0+Pk1RQjogZXh0cmFjdFNlbGVjdChtZXRob2ROYW1lKVxuICogICBNUUItPj5NUUI6IGV4dHJhY3RHcm91cEJ5KG1ldGhvZE5hbWUpXG4gKiAgIE1RQi0+Pk1RQjogYnVpbGRXaGVyZShjb3JlLCB2YWx1ZXMpXG4gKiAgIE1RQi0+Pk1RQjogZXh0cmFjdE9yZGVyTGltaXRPZmZzZXQoY29yZSwgdmFsdWVzKVxuICogICBNUUItPj5RdWVyeTogcmV0dXJuIHN0cnVjdHVyZWQgUXVlcnlBc3Npc3Qgb2JqZWN0XG4gKi9cbmV4cG9ydCBjbGFzcyBNZXRob2RRdWVyeUJ1aWxkZXIge1xuICAvKipcbiAgICogQGRlc2NyaXB0aW9uXG4gICAqIEJ1aWxkcyBhIGBRdWVyeUFzc2lzdGAgb2JqZWN0IGJ5IHBhcnNpbmcgYSByZXBvc2l0b3J5IG1ldGhvZCBuYW1lIGFuZCB2YWx1ZXMuXG4gICAqXG4gICAqIEBzdW1tYXJ5XG4gICAqIFRoZSBtZXRob2QgdmFsaWRhdGVzIHRoZSBtZXRob2QgbmFtZSwgZXh0cmFjdHMgY2xhdXNlcyAoY29yZSwgc2VsZWN0LCBncm91cEJ5LCB3aGVyZSxcbiAgICogb3JkZXJCeSwgbGltaXQsIGFuZCBvZmZzZXQpLCBhbmQgYXNzZW1ibGVzIHRoZW0gaW50byBhIHN0cnVjdHVyZWQgcXVlcnkgb2JqZWN0XG4gICAqIHRoYXQgY2FuIGJlIGV4ZWN1dGVkIGFnYWluc3QgYSBkYXRhIHNvdXJjZS5cbiAgICpcbiAgICogQHBhcmFtIG1ldGhvZE5hbWUge3N0cmluZ30gLSBUaGUgcmVwb3NpdG9yeSBtZXRob2QgbmFtZSB0aGF0IGVuY29kZXMgcXVlcnkgaW5mb3JtYXRpb24uXG4gICAqIEBwYXJhbSB2YWx1ZXMge2FueVtdfSAtIFRoZSB2YWx1ZXMgY29ycmVzcG9uZGluZyB0byBjb25kaXRpb25zIGFuZCBleHRyYSBjbGF1c2VzLlxuICAgKlxuICAgKiBAcmV0dXJuIHtRdWVyeUFzc2lzdH0gQSBzdHJ1Y3R1cmVkIHF1ZXJ5IG9iamVjdCByZXByZXNlbnRpbmcgdGhlIHBhcnNlZCBxdWVyeS5cbiAgICovXG4gIHN0YXRpYyBidWlsZChtZXRob2ROYW1lOiBzdHJpbmcsIC4uLnZhbHVlczogYW55W10pOiBRdWVyeUFzc2lzdCB7XG4gICAgaWYgKCFtZXRob2ROYW1lLnN0YXJ0c1dpdGgoUXVlcnlDbGF1c2UuRklORF9CWSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgbWV0aG9kICR7bWV0aG9kTmFtZX1gKTtcbiAgICB9XG5cbiAgICBjb25zdCBjb3JlID0gdGhpcy5leHRyYWN0Q29yZShtZXRob2ROYW1lKTtcbiAgICBjb25zdCBzZWxlY3QgPSB0aGlzLmV4dHJhY3RTZWxlY3QobWV0aG9kTmFtZSk7XG4gICAgY29uc3QgZ3JvdXBCeSA9IHRoaXMuZXh0cmFjdEdyb3VwQnkobWV0aG9kTmFtZSk7XG4gICAgLy8gY29uc3Qgb3JkZXJCeSA9IHRoaXMuZXh0cmFjdE9yZGVyQnkobWV0aG9kTmFtZSk7XG4gICAgY29uc3Qgd2hlcmUgPSB0aGlzLmJ1aWxkV2hlcmUoY29yZSwgdmFsdWVzKTtcbiAgICBjb25zdCB7IG9yZGVyQnksIGxpbWl0LCBvZmZzZXQgfSA9IHRoaXMuZXh0cmFjdE9yZGVyTGltaXRPZmZzZXQoXG4gICAgICBjb3JlLFxuICAgICAgdmFsdWVzXG4gICAgKTtcblxuICAgIHJldHVybiB7XG4gICAgICBhY3Rpb246IFwiZmluZFwiLFxuICAgICAgc2VsZWN0OiBzZWxlY3QsXG4gICAgICB3aGVyZSxcbiAgICAgIGdyb3VwQnksXG4gICAgICBvcmRlckJ5LFxuICAgICAgbGltaXQsXG4gICAgICBvZmZzZXQsXG4gICAgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogRXh0cmFjdHMgdGhlIGNvcmUgcGFydCBvZiB0aGUgbWV0aG9kIG5hbWUgYWZ0ZXIgYGZpbmRCeWAgYW5kIGJlZm9yZSBhbnkgc3BlY2lhbCBjbGF1c2VzLlxuICAgKlxuICAgKiBAc3VtbWFyeVxuICAgKiBSZW1vdmVzIHByZWZpeGVzIGFuZCBkZXRlY3RzIGRlbGltaXRlcnMgKGBUaGVuYCwgYE9yZGVyQnlgLCBgR3JvdXBCeWAsIGBMaW1pdGAsIGBPZmZzZXRgKVxuICAgKiB0byBpc29sYXRlIHRoZSBtYWluIGNvbmRpdGlvbmFsIHBhcnQgb2YgdGhlIHF1ZXJ5LlxuICAgKlxuICAgKiBAcGFyYW0gbWV0aG9kTmFtZSB7c3RyaW5nfSAtIFRoZSBtZXRob2QgbmFtZSB0byBwYXJzZS5cbiAgICpcbiAgICogQHJldHVybiB7c3RyaW5nfSBUaGUgZXh0cmFjdGVkIGNvcmUgc3RyaW5nIHVzZWQgZm9yIGJ1aWxkaW5nIGNvbmRpdGlvbnMuXG4gICAqL1xuICBwcml2YXRlIHN0YXRpYyBleHRyYWN0Q29yZShtZXRob2ROYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGFmdGVyRmluZEJ5ID0gbWV0aG9kTmFtZS5zdWJzdHJpbmcoUXVlcnlDbGF1c2UuRklORF9CWS5sZW5ndGgpO1xuICAgIGNvbnN0IHJlZ2V4ID0gLyhUaGVuW0EtWl18T3JkZXJCeXxHcm91cEJ5fExpbWl0fE9mZnNldCkvO1xuICAgIGNvbnN0IG1hdGNoID0gYWZ0ZXJGaW5kQnkubWF0Y2gocmVnZXgpO1xuICAgIHJldHVybiBtYXRjaCA/IGFmdGVyRmluZEJ5LnN1YnN0cmluZygwLCBtYXRjaC5pbmRleCkgOiBhZnRlckZpbmRCeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogRXh0cmFjdHMgdGhlIHNlbGVjdCBjbGF1c2UgZnJvbSBhIG1ldGhvZCBuYW1lLlxuICAgKlxuICAgKiBAc3VtbWFyeVxuICAgKiBEZXRlY3RzIHRoZSBgU2VsZWN0YCBrZXl3b3JkIGluIHRoZSBtZXRob2QgbmFtZSwgaXNvbGF0ZXMgdGhlIGZpZWxkcyBmb2xsb3dpbmcgaXQsXG4gICAqIGFuZCByZXR1cm5zIHRoZW0gYXMgYW4gYXJyYXkgb2YgbG93ZXJjYXNlLWZpcnN0IHN0cmluZ3MuXG4gICAqXG4gICAqIEBwYXJhbSBtZXRob2ROYW1lIHtzdHJpbmd9IC0gVGhlIG1ldGhvZCBuYW1lIHRvIHBhcnNlLlxuICAgKlxuICAgKiBAcmV0dXJuIHtzdHJpbmdbXSB8IHVuZGVmaW5lZH0gQW4gYXJyYXkgb2Ygc2VsZWN0ZWQgZmllbGRzIG9yIGB1bmRlZmluZWRgIGlmIG5vIHNlbGVjdCBjbGF1c2UgZXhpc3RzLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZXh0cmFjdFNlbGVjdChtZXRob2ROYW1lOiBzdHJpbmcpOiBzdHJpbmdbXSB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3Qgc2VsZWN0SW5kZXggPSBtZXRob2ROYW1lLmluZGV4T2YoUXVlcnlDbGF1c2UuU0VMRUNUKTtcbiAgICBpZiAoc2VsZWN0SW5kZXggPT09IC0xKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgYWZ0ZXJTZWxlY3QgPSBtZXRob2ROYW1lLnN1YnN0cmluZyhcbiAgICAgIHNlbGVjdEluZGV4ICsgUXVlcnlDbGF1c2UuU0VMRUNULmxlbmd0aFxuICAgICk7XG5cbiAgICAvLyBTZWFyY2ggZm9yIG5leHQgVGhlbiwgR3JvdXBCeSwgT3JkZXJCeS4uLlxuICAgIGNvbnN0IG1hdGNoID0gYWZ0ZXJTZWxlY3QubWF0Y2goLyhUaGVuW0EtWl18T3JkZXJCeXxHcm91cEJ5fExpbWl0fE9mZnNldCkvKTtcblxuICAgIGNvbnN0IHNlbGVjdFBhcnQgPSBtYXRjaFxuICAgICAgPyBhZnRlclNlbGVjdC5zdWJzdHJpbmcoMCwgbWF0Y2guaW5kZXgpXG4gICAgICA6IGFmdGVyU2VsZWN0O1xuXG4gICAgcmV0dXJuIHNlbGVjdFBhcnQuc3BsaXQoUXVlcnlDbGF1c2UuQU5EKS5tYXAobG93ZXJGaXJzdCkuZmlsdGVyKEJvb2xlYW4pO1xuICB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvblxuICAgKiBFeHRyYWN0cyB0aGUgZ3JvdXAgYnkgY2xhdXNlIGZyb20gYSBtZXRob2QgbmFtZS5cbiAgICpcbiAgICogQHN1bW1hcnlcbiAgICogRGV0ZWN0cyB0aGUgYEdyb3VwQnlgIGtleXdvcmQgaW4gdGhlIG1ldGhvZCBuYW1lLCBpc29sYXRlcyB0aGUgZmllbGRzIGZvbGxvd2luZyBpdCxcbiAgICogYW5kIHJldHVybnMgdGhlbSBhcyBhbiBhcnJheSBvZiBsb3dlcmNhc2UtZmlyc3Qgc3RyaW5ncy5cbiAgICpcbiAgICogQHBhcmFtIG1ldGhvZE5hbWUge3N0cmluZ30gLSBUaGUgbWV0aG9kIG5hbWUgdG8gcGFyc2UuXG4gICAqXG4gICAqIEByZXR1cm4ge3N0cmluZ1tdIHwgdW5kZWZpbmVkfSBBbiBhcnJheSBvZiBncm91cCBieSBmaWVsZHMgb3IgYHVuZGVmaW5lZGAgaWYgbm8gZ3JvdXAgYnkgY2xhdXNlIGV4aXN0cy5cbiAgICovXG4gIHByaXZhdGUgc3RhdGljIGV4dHJhY3RHcm91cEJ5KG1ldGhvZE5hbWU6IHN0cmluZyk6IHN0cmluZ1tdIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBncm91cEJ5SW5kZXggPSBtZXRob2ROYW1lLmluZGV4T2YoUXVlcnlDbGF1c2UuR1JPVVBfQlkpO1xuICAgIGlmIChncm91cEJ5SW5kZXggPT09IC0xKSByZXR1cm4gdW5kZWZpbmVkO1xuXG4gICAgY29uc3QgYWZ0ZXIgPSBtZXRob2ROYW1lLnN1YnN0cmluZyhcbiAgICAgIGdyb3VwQnlJbmRleCArIFF1ZXJ5Q2xhdXNlLkdST1VQX0JZLmxlbmd0aFxuICAgICk7XG4gICAgY29uc3QgZ3JvdXBCeVBhcnQgPSBhZnRlci5zcGxpdChRdWVyeUNsYXVzZS5PUkRFUl9CWSlbMF07XG4gICAgcmV0dXJuIGdyb3VwQnlQYXJ0XG4gICAgICAuc3BsaXQoUXVlcnlDbGF1c2UuVEhFTl9CWSlcbiAgICAgIC5tYXAobG93ZXJGaXJzdClcbiAgICAgIC5maWx0ZXIoQm9vbGVhbik7XG4gIH1cblxuICAvLyBwcml2YXRlIHN0YXRpYyBleHRyYWN0T3JkZXJCeShcbiAgLy8gICBtZXRob2ROYW1lOiBzdHJpbmdcbiAgLy8gKTogT3JkZXJCeVNlbGVjdG9yPGFueT5bXSB8IHVuZGVmaW5lZCB7XG4gIC8vICAgY29uc3Qgb3JkZXJCeUluZGV4ID0gbWV0aG9kTmFtZS5pbmRleE9mKFF1ZXJ5Q2xhdXNlLk9SREVSX0JZKTtcbiAgLy8gICBpZiAob3JkZXJCeUluZGV4ID09PSAtMSkgcmV0dXJuIHVuZGVmaW5lZDtcbiAgLy9cbiAgLy8gICBjb25zdCBhZnRlciA9IG1ldGhvZE5hbWUuc3Vic3RyaW5nKFxuICAvLyAgICAgb3JkZXJCeUluZGV4ICsgUXVlcnlDbGF1c2UuT1JERVJfQlkubGVuZ3RoXG4gIC8vICAgKTtcbiAgLy8gICBjb25zdCBvcmRlclBhcnRzID0gYWZ0ZXIuc3BsaXQoXCJUaGVuQnlcIik7XG4gIC8vXG4gIC8vICAgcmV0dXJuIG9yZGVyUGFydHMubWFwKChwYXJ0KSA9PiB7XG4gIC8vICAgICBjb25zdCBtYXRjaCA9IHBhcnQubWF0Y2goLyguKj8pKEFzY3xEZXNjfERzYykkLyk7XG4gIC8vICAgICBpZiAoIW1hdGNoKSB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgT3JkZXJCeSBwYXJ0OiAke3BhcnR9YCk7XG4gIC8vICAgICBjb25zdCBbLCBmaWVsZCwgZGlyXSA9IG1hdGNoO1xuICAvLyAgICAgcmV0dXJuIFtcbiAgLy8gICAgICAgbG93ZXJGaXJzdChmaWVsZCksXG4gIC8vICAgICAgIGRpci50b0xvd2VyQ2FzZSgpID09PSBcImRzY1wiXG4gIC8vICAgICAgICAgPyBPcmRlckRpcmVjdGlvbi5EU0NcbiAgLy8gICAgICAgICA6IChkaXIudG9Mb3dlckNhc2UoKSBhcyBPcmRlckRpcmVjdGlvbiksXG4gIC8vICAgICBdO1xuICAvLyAgIH0pO1xuICAvLyB9XG5cbiAgLyoqXG4gICAqIEBkZXNjcmlwdGlvblxuICAgKiBCdWlsZHMgdGhlIGB3aGVyZWAgY29uZGl0aW9uIG9iamVjdCBiYXNlZCBvbiB0aGUgcGFyc2VkIGNvcmUgc3RyaW5nIGFuZCBwYXJhbWV0ZXIgdmFsdWVzLlxuICAgKlxuICAgKiBAc3VtbWFyeVxuICAgKiBTcGxpdHMgdGhlIGNvcmUgc3RyaW5nIGJ5IGxvZ2ljYWwgb3BlcmF0b3JzIChgQW5kYCwgYE9yYCksIHBhcnNlcyBlYWNoIHRva2VuIGludG8gYSBmaWVsZFxuICAgKiBhbmQgb3BlcmF0b3IsIGFuZCBjb21iaW5lcyB0aGVtIGludG8gYSBgQ29uZGl0aW9uYCBvYmplY3QgdXNpbmcgdGhlIHByb3ZpZGVkIHZhbHVlcy5cbiAgICpcbiAgICogQHBhcmFtIGNvcmUge3N0cmluZ30gLSBUaGUgZXh0cmFjdGVkIGNvcmUgc3RyaW5nIGZyb20gdGhlIG1ldGhvZCBuYW1lLlxuICAgKiBAcGFyYW0gdmFsdWVzIHthbnlbXX0gLSBUaGUgdmFsdWVzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNvbmRpdGlvbnMuXG4gICAqXG4gICAqIEByZXR1cm4ge0NvbmRpdGlvbjxhbnk+fSBBIHN0cnVjdHVyZWQgY29uZGl0aW9uIG9iamVjdCByZXByZXNlbnRpbmcgdGhlIHF1ZXJ5J3Mgd2hlcmUgY2xhdXNlLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgYnVpbGRXaGVyZShjb3JlOiBzdHJpbmcsIHZhbHVlczogYW55W10pOiBDb25kaXRpb248YW55PiB7XG4gICAgY29uc3QgcGFydHMgPSBjb3JlLnNwbGl0KC9PcmRlckJ5fEdyb3VwQnkvKVswXSB8fCBcIlwiO1xuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSBwYXJ0cy5zcGxpdCgvQW5kfE9yLyk7XG5cbiAgICBjb25zdCBvcGVyYXRvcnMgPSBjb3JlLm1hdGNoKC9BbmR8T3IvZykgfHwgW107XG5cbiAgICBsZXQgd2hlcmU6IENvbmRpdGlvbjxhbnk+IHwgdW5kZWZpbmVkO1xuXG4gICAgY29uZGl0aW9ucy5mb3JFYWNoKCh0b2tlbiwgaWR4KSA9PiB7XG4gICAgICBjb25zdCB7IGZpZWxkLCBvcGVyYXRvciB9ID0gdGhpcy5wYXJzZUZpZWxkQW5kT3BlcmF0b3IodG9rZW4pO1xuICAgICAgY29uc3QgcGFyc2VyID0gb3BlcmF0b3IgPyBPcGVyYXRvcnNNYXBbb3BlcmF0b3JdIDogT3BlcmF0b3JzTWFwLkVxdWFscztcbiAgICAgIGlmICghcGFyc2VyKSB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIG9wZXJhdG9yICR7b3BlcmF0b3J9YCk7XG5cbiAgICAgIGNvbnN0IGNvbmRpdGlvblZhbHVlID0gdmFsdWVzW2lkeF07XG4gICAgICBpZiAodHlwZW9mIGNvbmRpdGlvblZhbHVlID09PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCB2YWx1ZSBmb3IgZmllbGQgJHtmaWVsZH1gKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgY29uZGl0aW9uID0gcGFyc2VyKGZpZWxkLCBjb25kaXRpb25WYWx1ZSk7XG4gICAgICB3aGVyZSA9XG4gICAgICAgIGlkeCA9PT0gMFxuICAgICAgICAgID8gY29uZGl0aW9uXG4gICAgICAgICAgOiBvcGVyYXRvcnNbaWR4IC0gMV0gPT09IFF1ZXJ5Q2xhdXNlLkFORFxuICAgICAgICAgICAgPyB3aGVyZSEuYW5kKGNvbmRpdGlvbilcbiAgICAgICAgICAgIDogd2hlcmUhLm9yKGNvbmRpdGlvbik7XG4gICAgfSk7XG5cbiAgICBpZiAoIXdoZXJlKSB0aHJvdyBuZXcgRXJyb3IoXCJObyBjb25kaXRpb25zIGZvdW5kIGluIG1ldGhvZCBuYW1lXCIpO1xuICAgIHJldHVybiB3aGVyZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAZGVzY3JpcHRpb25cbiAgICogUGFyc2VzIGEgZmllbGQgbmFtZSBhbmQgb3BlcmF0b3IgZnJvbSBhIHN0cmluZyB0b2tlbi5cbiAgICpcbiAgICogQHN1bW1hcnlcbiAgICogSWRlbnRpZmllcyB0aGUgb3BlcmF0b3Igc3VmZml4IChpZiBwcmVzZW50KSBhbmQgcmV0dXJucyBhIGRlc2NyaXB0b3IgY29udGFpbmluZyB0aGUgZmllbGRcbiAgICogbmFtZSBpbiBsb3dlcmNhc2UtZmlyc3QgZm9ybWF0IGFsb25nIHdpdGggdGhlIG9wZXJhdG9yLlxuICAgKlxuICAgKiBAcGFyYW0gc3RyIHtzdHJpbmd9IC0gVGhlIHRva2VuIHN0cmluZyB0byBwYXJzZS5cbiAgICpcbiAgICogQHJldHVybiB7RmlsdGVyRGVzY3JpcHRvcn0gQW4gb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGZpZWxkIG5hbWUgYW5kIG9wZXJhdG9yLlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgcGFyc2VGaWVsZEFuZE9wZXJhdG9yKHN0cjogc3RyaW5nKTogRmlsdGVyRGVzY3JpcHRvciB7XG4gICAgZm9yIChjb25zdCBvcGVyYXRvciBvZiBPYmplY3Qua2V5cyhPcGVyYXRvcnNNYXApKSB7XG4gICAgICBpZiAoc3RyLmVuZHNXaXRoKG9wZXJhdG9yKSkge1xuICAgICAgICBjb25zdCBmaWVsZCA9IHN0ci5zbGljZSgwLCAtb3BlcmF0b3IubGVuZ3RoKTtcbiAgICAgICAgcmV0dXJuIHsgZmllbGQ6IGxvd2VyRmlyc3QoZmllbGQpLCBvcGVyYXRvciB9O1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4geyBmaWVsZDogbG93ZXJGaXJzdChzdHIpIH07XG4gIH1cblxuICAvKipcbiAgICogQGRlc2NyaXB0aW9uXG4gICAqIEV4dHJhY3RzIGBvcmRlckJ5YCwgYGxpbWl0YCwgYW5kIGBvZmZzZXRgIGNsYXVzZXMgZnJvbSBtZXRob2QgYXJndW1lbnRzLlxuICAgKlxuICAgKiBAc3VtbWFyeVxuICAgKiBEZXRlcm1pbmVzIHRoZSBudW1iZXIgb2YgY29uZGl0aW9uIGFyZ3VtZW50cywgdGhlbiBjaGVja3MgdGhlIHJlbWFpbmluZyBhcmd1bWVudHNcbiAgICogdG8gcmVzb2x2ZSBzb3J0aW5nLCBsaW1pdGluZywgYW5kIHBhZ2luYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBjb3JlIHtzdHJpbmd9IC0gVGhlIGV4dHJhY3RlZCBjb3JlIHN0cmluZyBmcm9tIHRoZSBtZXRob2QgbmFtZS5cbiAgICogQHBhcmFtIHZhbHVlcyB7YW55W119IC0gVGhlIHZhbHVlcyBjb3JyZXNwb25kaW5nIHRvIG1ldGhvZCBhcmd1bWVudHMsIGluY2x1ZGluZyBjb25kaXRpb25zIGFuZCBleHRyYXMuXG4gICAqXG4gICAqIEByZXR1cm4ge3tcbiAgICogICBvcmRlckJ5PzogT3JkZXJCeVNlbGVjdG9yPGFueT5bXTtcbiAgICogICBsaW1pdD86IG51bWJlcjtcbiAgICogICBvZmZzZXQ/OiBudW1iZXI7XG4gICAqIH19IEFuIG9iamVjdCBjb250YWluaW5nIG9yZGVyQnksIGxpbWl0LCBhbmQgb2Zmc2V0IHZhbHVlcyBpZiBwcmVzZW50LlxuICAgKi9cbiAgcHJpdmF0ZSBzdGF0aWMgZXh0cmFjdE9yZGVyTGltaXRPZmZzZXQoXG4gICAgY29yZTogc3RyaW5nLFxuICAgIHZhbHVlczogYW55W11cbiAgKTogeyBvcmRlckJ5PzogT3JkZXJCeVNlbGVjdG9yPGFueT5bXTsgbGltaXQ/OiBudW1iZXI7IG9mZnNldD86IG51bWJlciB9IHtcbiAgICBjb25zdCBjb25kaXRpb25Db3VudCA9IGNvcmUuc3BsaXQoL0FuZHxPci8pLmxlbmd0aDtcbiAgICBjb25zdCBleHRyYUFyZ3MgPSB2YWx1ZXMuc2xpY2UoY29uZGl0aW9uQ291bnQpO1xuXG4gICAgbGV0IG9yZGVyQnk6IE9yZGVyQnlTZWxlY3Rvcjxhbnk+W10gfCB1bmRlZmluZWQ7XG4gICAgbGV0IGxpbWl0OiBudW1iZXIgfCB1bmRlZmluZWQ7XG4gICAgbGV0IG9mZnNldDogbnVtYmVyIHwgdW5kZWZpbmVkO1xuXG4gICAgaWYgKGV4dHJhQXJncy5sZW5ndGggPj0gMSAmJiBBcnJheS5pc0FycmF5KGV4dHJhQXJnc1swXSkpXG4gICAgICBvcmRlckJ5ID0gZXh0cmFBcmdzWzBdIGFzIE9yZGVyQnlTZWxlY3Rvcjxhbnk+W107XG5cbiAgICBpZiAoZXh0cmFBcmdzLmxlbmd0aCA+PSAyICYmIHR5cGVvZiBleHRyYUFyZ3NbMV0gPT09IFwibnVtYmVyXCIpXG4gICAgICBsaW1pdCA9IGV4dHJhQXJnc1sxXTtcblxuICAgIGlmIChleHRyYUFyZ3MubGVuZ3RoID49IDMgJiYgdHlwZW9mIGV4dHJhQXJnc1syXSA9PT0gXCJudW1iZXJcIilcbiAgICAgIG9mZnNldCA9IGV4dHJhQXJnc1syXTtcblxuICAgIHJldHVybiB7IG9yZGVyQnksIGxpbWl0LCBvZmZzZXQgfTtcbiAgfVxufVxuIl19