UNPKG

@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
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