@react-querybuilder/core
Version:
React Query Builder component for constructing queries and filters, with utilities for executing them in various database and evaluation contexts
267 lines (264 loc) • 13.4 kB
JavaScript
import "./optGroupUtils-VeZ3k7-1.mjs";
import { t as isRuleGroup } from "./isRuleGroup-CYcfPgbg.mjs";
import { n as fieldIsValidUtil, r as getFieldsArray, t as prepareRuleGroup } from "./prepareQueryObjects-DPCC-iHp.mjs";
import jsonata from "jsonata";
//#region src/utils/parseJSONata/utils.ts
const isJSONataExprNode = (expr) => {
return expr && typeof expr === "object" && typeof expr.type === "string";
};
const isJSONataBinaryNode = (expr) => isJSONataExprNode(expr) && expr.type === "binary";
const isJSONataPath = (expr) => isJSONataExprNode(expr) && expr.type === "path" && Array.isArray(expr.steps) && expr.steps.length > 0 && isJSONataExprNode(expr.steps[0]);
const isJSONataName = (expr) => isJSONataExprNode(expr) && expr.type === "name" && typeof expr.value === "string" && expr.value.length > 0;
const isJSONataIdentifier = (expr) => isJSONataPath(expr) && expr.steps.every((v) => isJSONataName(v));
const isJSONataBlock = (expr) => isJSONataExprNode(expr) && expr.type === "block" && Array.isArray(expr.expressions) && expr.expressions.length > 0 && isJSONataExprNode(expr.expressions[0]);
const isJSONataString = (expr) => isJSONataExprNode(expr) && expr.type === "string" && typeof expr.value === "string";
const isJSONataNumber = (expr) => isJSONataExprNode(expr) && expr.type === "number" && typeof expr.value === "number";
const isJSONataBoolean = (expr) => isJSONataExprNode(expr) && expr.type === "value" && typeof expr.value === "boolean";
const isJSONataNull = (expr) => isJSONataExprNode(expr) && expr.type === "value" && expr.value === null;
const isJSONataRegex = (expr) => isJSONataExprNode(expr) && expr.type === "regex" && expr.value instanceof RegExp;
const isJSONataAnd = (expr) => isJSONataBinaryNode(expr) && expr.value === "and";
const isJSONataOr = (expr) => isJSONataBinaryNode(expr) && expr.value === "or";
const isJSONataEqual = (expr) => isJSONataBinaryNode(expr) && expr.value === "=";
const isJSONataNotEqual = (expr) => isJSONataBinaryNode(expr) && expr.value === "!=";
const isJSONataGreaterThan = (expr) => isJSONataBinaryNode(expr) && expr.value === ">";
const isJSONataGreaterThanOrEqual = (expr) => isJSONataBinaryNode(expr) && expr.value === ">=";
const isJSONataLessThan = (expr) => isJSONataBinaryNode(expr) && expr.value === "<";
const isJSONataLessThanOrEqual = (expr) => isJSONataBinaryNode(expr) && expr.value === "<=";
const isJSONataIn = (expr) => isJSONataBinaryNode(expr) && expr.value === "in" && isJSONataPath(expr.lhs) && isJSONataList(expr.rhs);
const isJSONataNot = (expr) => isJSONataExprNode(expr) && expr.type === "function" && expr.value === "(" && Array.isArray(expr.arguments) && isJSONataExprNode(expr.arguments[0]) && isJSONataExprNode(expr.procedure) && expr.procedure.value === "not" && expr.procedure.type === "variable";
const isJSONataContains = (expr) => isJSONataExprNode(expr) && expr.type === "function" && expr.value === "(" && Array.isArray(expr.arguments) && expr.arguments.length >= 2 && isJSONataExprNode(expr.arguments[0]) && isJSONataExprNode(expr.procedure) && expr.procedure.value === "contains" && expr.procedure.type === "variable";
const isJSONataToMillis = (expr) => isJSONataExprNode(expr) && expr.type === "function" && expr.value === "(" && Array.isArray(expr.arguments) && expr.arguments.length > 0 && isJSONataString(expr.arguments[0]) && isJSONataExprNode(expr.procedure) && expr.procedure.value === "toMillis" && expr.procedure.type === "variable";
const isJSONataList = (expr) => isJSONataExprNode(expr) && expr.type === "unary" && expr.value === "[" && Array.isArray(expr.expressions);
const isJSONataPrimitive = (expr) => {
return isJSONataString(expr) || isJSONataNumber(expr) || isJSONataBoolean(expr) || isJSONataNull(expr) || isJSONataToMillis(expr);
};
const isJSONataPrimitiveList = (expr) => isJSONataList(expr) && expr.expressions.every((v) => isJSONataPrimitive(v));
const isJSONataIdentifierList = (expr) => isJSONataList(expr) && expr.expressions.every((v) => isJSONataIdentifier(v));
const isJSONataValidValue = (expr) => isJSONataPrimitive(expr) || isJSONataRegex(expr) || isJSONataIdentifier(expr) || isJSONataPrimitiveList(expr) || isJSONataIdentifierList(expr) || isJSONataToMillis(expr);
const isJSONataComparison = (expr) => isJSONataEqual(expr) || isJSONataNotEqual(expr) || isJSONataGreaterThan(expr) || isJSONataGreaterThanOrEqual(expr) || isJSONataLessThan(expr) || isJSONataLessThanOrEqual(expr);
const getValidValue = (expr) => {
if (isJSONataToMillis(expr)) return getValidValue(expr.arguments[0]);
else if (isJSONataIdentifier(expr)) return getFieldFromPath(expr);
else if (isJSONataPrimitiveList(expr)) return expr.expressions.map((v) => getValidValue(v));
else if (isJSONataIdentifierList(expr)) return expr.expressions.map((v) => getFieldFromPath(v));
return expr.value;
};
const getFieldFromPath = (path) => isJSONataIdentifier(path) ? path.steps.map((s) => s.value).join(".") : "";
const normalizeOperator = (opType, flip) => {
if (flip) {
if (opType === "<") return ">";
if (opType === "<=") return ">=";
if (opType === ">") return "<";
if (opType === ">=") return "<=";
}
return opType;
};
const negatedLikeOperators = {
contains: "doesNotContain",
beginsWith: "doesNotBeginWith",
endsWith: "doesNotEndWith"
};
const generateFlatAndOrList = (expr) => {
// istanbul ignore else
if (isJSONataAnd(expr) || isJSONataOr(expr)) {
const { lhs, rhs, value: combinator } = expr;
if (isJSONataAnd(lhs) || isJSONataOr(lhs)) return [
...generateFlatAndOrList(lhs),
combinator,
rhs
];
return [
lhs,
combinator,
rhs
];
}
// istanbul ignore next
return [];
};
const generateMixedAndOrList = (expr) => {
const arr = generateFlatAndOrList(expr);
const returnArray = [];
let startIndex = 0;
for (let i = 0; i < arr.length; i += 2) if (arr[i + 1] === "and") {
startIndex = i;
let j = 1;
while (arr[startIndex + j] === "and") {
i += 2;
j += 2;
}
const tempAndArray = arr.slice(startIndex, i + 1);
returnArray.push(tempAndArray);
i -= 2;
} else if (arr[i + 1] === "or") if (i === 0 || i === arr.length - 3) {
if (i === 0 || arr[i - 1] === "or") returnArray.push(arr[i]);
returnArray.push(arr[i + 1]);
if (i === arr.length - 3) returnArray.push(arr[i + 2]);
} else if (arr[i - 1] === "and") returnArray.push(arr[i + 1]);
else returnArray.push(arr[i], arr[i + 1]);
if (returnArray.length === 1 && Array.isArray(returnArray[0])) return returnArray[0];
return returnArray;
};
//#endregion
//#region src/utils/parseJSONata/parseJSONata.ts
function parseJSONata(jsonataInput, options = {}) {
const { fields, independentCombinators, listsAsArrays: _laa } = options;
const ic = !!independentCombinators;
const fieldsFlat = getFieldsArray(fields);
const fieldIsValid = (fieldName, operator, subordinateFieldName) => fieldIsValidUtil({
fieldName,
fieldsFlat,
operator,
subordinateFieldName,
getValueSources: options?.getValueSources
});
const emptyQuery = {
rules: [],
...ic ? {} : { combinator: "and" }
};
const parseJSONataAST = (expr, processOpts = {}) => {
const { forwardNegation: _forwardedNegation, groupOnlyIfNecessary: _g } = processOpts;
if (isJSONataBlock(expr)) {
if (isJSONataAnd(expr.expressions[0]) || isJSONataOr(expr.expressions[0]) || isJSONataBlock(expr.expressions[0])) return parseJSONataAST(expr.expressions[0]);
const blockOfExpr = parseJSONataAST(expr.expressions[0]);
// istanbul ignore else
if (blockOfExpr) return ic ? { rules: [blockOfExpr] } : {
combinator: "and",
rules: [blockOfExpr]
};
} else if (isJSONataAnd(expr) || isJSONataOr(expr)) {
if (ic) {
const rules$1 = generateFlatAndOrList(expr).map((v) => {
if (typeof v === "string") return v;
return parseJSONataAST(v);
});
if (!rules$1.every(Boolean)) return null;
if (((rs) => rs.length === 3 && (rs[1] === "and" || rs[1] === "or") && !isRuleGroup(rs[0]) && !isRuleGroup(rs[2]))(rules$1) && rules$1[0].field === rules$1[2].field && (rules$1[0].valueSource ?? "value") === (rules$1[2].valueSource ?? "value") && (rules$1[1] === "and" && (rules$1[0].operator === ">=" && rules$1[2].operator === "<=" || rules$1[0].operator === "<=" && rules$1[2].operator === ">=") || rules$1[1] === "or" && (rules$1[0].operator === ">" && rules$1[2].operator === "<" || rules$1[0].operator === "<" && rules$1[2].operator === ">"))) return {
field: rules$1[0].field,
operator: rules$1[1] === "and" ? "between" : "notBetween",
value: rules$1[1] === "and" && rules$1[0].operator === "<=" || rules$1[1] === "or" && rules$1[0].operator === ">" ? [rules$1[2].value, rules$1[0].value] : [rules$1[0].value, rules$1[2].value],
...rules$1[0].valueSource ? { valueSource: rules$1[0].valueSource } : null
};
return { rules: rules$1 };
}
const andOrList = generateMixedAndOrList(expr);
const combinator = andOrList[1];
const rules = andOrList.filter((v) => Array.isArray(v) || !!v && typeof v !== "string" && "type" in v).map((v) => Array.isArray(v) ? v.filter((vf) => !!v && typeof vf !== "string" && "type" in vf) : v).map((exp) => {
if (Array.isArray(exp)) return {
combinator: "and",
rules: exp.map((e) => parseJSONataAST(e)).filter(Boolean)
};
return parseJSONataAST(exp);
}).filter(Boolean);
if (((rs) => rs.length === 2 && !isRuleGroup(rs[0]) && !isRuleGroup(rs[1]))(rules) && rules[0].field === rules[1].field && (rules[0].valueSource ?? "value") === (rules[1].valueSource ?? "value") && (combinator === "and" && (rules[0].operator === ">=" && rules[1].operator === "<=" || rules[0].operator === "<=" && rules[1].operator === ">=") || combinator === "or" && (rules[0].operator === ">" && rules[1].operator === "<" || rules[0].operator === "<" && rules[1].operator === ">"))) return {
field: rules[0].field,
operator: combinator === "and" ? "between" : "notBetween",
value: combinator === "and" && rules[0].operator === "<=" || combinator === "or" && rules[0].operator === ">" ? [rules[1].value, rules[0].value] : [rules[0].value, rules[1].value],
...rules[0].valueSource ? { valueSource: rules[0].valueSource } : null
};
// istanbul ignore else
if (rules.length > 0) return {
combinator,
rules
};
} else if (isJSONataNot(expr)) {
const negatedExpr = parseJSONataAST(expr.arguments[0]);
// istanbul ignore else
if (negatedExpr) {
if (!isRuleGroup(negatedExpr) && (negatedExpr.operator === "contains" || negatedExpr.operator === "beginsWith" || negatedExpr.operator === "endsWith")) return {
...negatedExpr,
operator: negatedLikeOperators[negatedExpr.operator]
};
return ic ? {
rules: [negatedExpr],
not: true
} : {
combinator: "and",
rules: [negatedExpr],
not: true
};
}
} else if (isJSONataContains(expr)) {
const [arg1, arg2] = expr.arguments;
let field = "";
let regex = "";
let valueSource = void 0;
// istanbul ignore else
if (isJSONataIdentifier(arg1)) {
field = getFieldFromPath(arg1);
if (isJSONataIdentifier(arg2)) {
regex = getFieldFromPath(arg2);
valueSource = "field";
} else if (isJSONataString(arg2) || isJSONataRegex(arg2)) regex = getValidValue(arg2);
}
// istanbul ignore else
if (valueSource === "field" ? fieldIsValid(field, "contains", regex) : fieldIsValid(field, "contains")) return {
field,
operator: "contains",
value: regex,
...valueSource ? { valueSource } : {}
};
} else if (isJSONataIn(expr)) {
const field = getFieldFromPath(expr.lhs);
let valueSource = void 0;
if (isJSONataIdentifierList(expr.rhs)) valueSource = "field";
if (isJSONataValidValue(expr.rhs)) {
const value = getValidValue(expr.rhs);
// istanbul ignore else
if (field && value.every((v) => fieldIsValid(field, "in", valueSource === "field" ? v : void 0))) return {
field,
operator: "in",
value,
...valueSource ? { valueSource } : {}
};
}
} else if (isJSONataComparison(expr)) {
let field = null;
let value = void 0;
let valueSource = void 0;
let flip = false;
const { lhs, rhs } = expr;
if (isJSONataIdentifier(lhs) && isJSONataValidValue(rhs)) {
field = getFieldFromPath(lhs);
value = getValidValue(rhs);
if (isJSONataIdentifier(rhs)) valueSource = "field";
} else if (isJSONataIdentifier(rhs) && isJSONataValidValue(lhs)) {
flip = true;
field = getFieldFromPath(rhs);
value = getValidValue(lhs);
}
let operator = normalizeOperator(expr.value, flip);
if (value === null && (operator === "=" || operator === "!=")) operator = operator === "=" ? "null" : "notNull";
if (field && fieldIsValid(field, operator, valueSource === "field" ? value : void 0) && value !== void 0) return valueSource ? {
field,
operator,
value,
valueSource
} : {
field,
operator,
value
};
}
return null;
};
const prepare = options.generateIDs ? prepareRuleGroup : (g) => g;
let jsonataExpr;
try {
jsonataExpr = jsonata(jsonataInput);
} catch {
return prepare(emptyQuery);
}
const result = parseJSONataAST(jsonataExpr.ast());
if (result) {
if (isRuleGroup(result)) return prepare(result);
return prepare({
rules: [result],
...ic ? {} : { combinator: "and" }
});
}
return prepare(emptyQuery);
}
//#endregion
export { parseJSONata };
//# sourceMappingURL=parseJSONata.mjs.map