UNPKG

react-querybuilder

Version:

React Query Builder component for constructing queries and filters, with utilities for executing them in various database and evaluation contexts

999 lines (974 loc) 290 kB
// src/context/QueryBuilderContext.ts import { createContext } from "react"; var QueryBuilderContext = /* @__PURE__ */ createContext({}); // src/components/ActionElement.tsx import { jsx } from "react/jsx-runtime"; var ActionElement = (props) => /* @__PURE__ */ jsx("button", { type: "button", "data-testid": props.testID, disabled: props.disabled && !props.disabledTranslation, className: props.className, title: props.disabledTranslation && props.disabled ? props.disabledTranslation.title : props.title, onClick: (e) => props.handleOnClick(e), children: props.disabledTranslation && props.disabled ? props.disabledTranslation.label : props.label }); // src/components/DragHandle.tsx import { jsx as jsx2 } from "react/jsx-runtime"; import { forwardRef } from "react"; var DragHandle = /* @__PURE__ */ forwardRef((props, dragRef) => /* @__PURE__ */ jsx2("span", { "data-testid": props.testID, ref: dragRef, className: props.className, title: props.title, children: props.label })); // src/defaults.ts var defaultPlaceholderName = "~"; var defaultPlaceholderLabel = "------"; var defaultPlaceholderFieldName = defaultPlaceholderName; var defaultPlaceholderFieldLabel = defaultPlaceholderLabel; var defaultPlaceholderFieldGroupLabel = defaultPlaceholderLabel; var defaultPlaceholderOperatorName = defaultPlaceholderName; var defaultPlaceholderOperatorLabel = defaultPlaceholderLabel; var defaultPlaceholderOperatorGroupLabel = defaultPlaceholderLabel; var defaultPlaceholderValueName = defaultPlaceholderName; var defaultPlaceholderValueLabel = defaultPlaceholderLabel; var defaultPlaceholderValueGroupLabel = defaultPlaceholderLabel; var defaultJoinChar = ","; var defaultOperatorLabelMap = { "=": "=", "!=": "!=", "<": "<", ">": ">", "<=": "<=", ">=": ">=", contains: "contains", beginsWith: "begins with", endsWith: "ends with", doesNotContain: "does not contain", doesNotBeginWith: "does not begin with", doesNotEndWith: "does not end with", null: "is null", notNull: "is not null", in: "in", notIn: "not in", between: "between", notBetween: "not between" }; var defaultCombinatorLabelMap = { and: "AND", or: "OR", xor: "XOR" }; var defaultOperators = [{ name: "=", value: "=", label: "=" }, { name: "!=", value: "!=", label: "!=" }, { name: "<", value: "<", label: "<" }, { name: ">", value: ">", label: ">" }, { name: "<=", value: "<=", label: "<=" }, { name: ">=", value: ">=", label: ">=" }, { name: "contains", value: "contains", label: "contains" }, { name: "beginsWith", value: "beginsWith", label: "begins with" }, { name: "endsWith", value: "endsWith", label: "ends with" }, { name: "doesNotContain", value: "doesNotContain", label: "does not contain" }, { name: "doesNotBeginWith", value: "doesNotBeginWith", label: "does not begin with" }, { name: "doesNotEndWith", value: "doesNotEndWith", label: "does not end with" }, { name: "null", value: "null", label: "is null" }, { name: "notNull", value: "notNull", label: "is not null" }, { name: "in", value: "in", label: "in" }, { name: "notIn", value: "notIn", label: "not in" }, { name: "between", value: "between", label: "between" }, { name: "notBetween", value: "notBetween", label: "not between" }]; var defaultOperatorNegationMap = { "=": "!=", "!=": "=", "<": ">=", "<=": ">", ">": "<=", ">=": "<", beginsWith: "doesNotBeginWith", doesNotBeginWith: "beginsWith", endsWith: "doesNotEndWith", doesNotEndWith: "endsWith", contains: "doesNotContain", doesNotContain: "contains", between: "notBetween", notBetween: "between", in: "notIn", notIn: "in", notNull: "null", null: "notNull" }; var defaultCombinators = [{ name: "and", value: "and", label: "AND" }, { name: "or", value: "or", label: "OR" }]; var defaultCombinatorsExtended = [...defaultCombinators, { name: "xor", value: "xor", label: "XOR" }]; var defaultMatchModes = [{ name: "all", value: "all", label: "all" }, { name: "some", value: "some", label: "some" }, { name: "none", value: "none", label: "none" }, { name: "atLeast", value: "atLeast", label: "at least" }, { name: "atMost", value: "atMost", label: "at most" }, { name: "exactly", value: "exactly", label: "exactly" }]; var standardClassnames = { queryBuilder: "queryBuilder", ruleGroup: "ruleGroup", header: "ruleGroup-header", body: "ruleGroup-body", combinators: "ruleGroup-combinators", addRule: "ruleGroup-addRule", addGroup: "ruleGroup-addGroup", cloneRule: "rule-cloneRule", cloneGroup: "ruleGroup-cloneGroup", removeGroup: "ruleGroup-remove", notToggle: "ruleGroup-notToggle", rule: "rule", fields: "rule-fields", matchMode: "rule-matchMode", matchThreshold: "rule-matchThreshold", operators: "rule-operators", value: "rule-value", removeRule: "rule-remove", betweenRules: "betweenRules", valid: "queryBuilder-valid", invalid: "queryBuilder-invalid", shiftActions: "shiftActions", dndDragging: "dndDragging", dndOver: "dndOver", dndCopy: "dndCopy", dndGroup: "dndGroup", dragHandle: "queryBuilder-dragHandle", disabled: "queryBuilder-disabled", lockRule: "rule-lock", lockGroup: "ruleGroup-lock", valueSource: "rule-valueSource", valueListItem: "rule-value-list-item", branches: "queryBuilder-branches", justified: "queryBuilder-justified", hasSubQuery: "rule-hasSubQuery" }; var defaultControlClassnames = { queryBuilder: "", ruleGroup: "", header: "", body: "", combinators: "", addRule: "", addGroup: "", cloneRule: "", cloneGroup: "", removeGroup: "", notToggle: "", rule: "", fields: "", matchMode: "", matchThreshold: "", operators: "", value: "", removeRule: "", shiftActions: "", dragHandle: "", lockRule: "", lockGroup: "", valueSource: "", actionElement: "", valueSelector: "", betweenRules: "", valid: "", invalid: "", dndDragging: "", dndOver: "", dndGroup: "", dndCopy: "", disabled: "", valueListItem: "", branches: "", hasSubQuery: "" }; var groupInvalidReasons = { empty: "empty", invalidCombinator: "invalid combinator", invalidIndependentCombinators: "invalid independent combinators" }; var TestID = { rule: "rule", ruleGroup: "rule-group", inlineCombinator: "inline-combinator", addGroup: "add-group", removeGroup: "remove-group", cloneGroup: "clone-group", cloneRule: "clone-rule", addRule: "add-rule", removeRule: "remove-rule", combinators: "combinators", fields: "fields", operators: "operators", valueEditor: "value-editor", notToggle: "not-toggle", shiftActions: "shift-actions", dragHandle: "drag-handle", lockRule: "lock-rule", lockGroup: "lock-group", valueSourceSelector: "value-source-selector", matchModeEditor: "match-mode-editor" }; var LogType = { parentPathDisabled: "action aborted: parent path disabled", pathDisabled: "action aborted: path is disabled", queryUpdate: "query updated", onAddRuleFalse: "onAddRule callback returned false", onAddGroupFalse: "onAddGroup callback returned false", onGroupRuleFalse: "onGroupRule callback returned false", onGroupGroupFalse: "onGroupGroup callback returned false", onMoveRuleFalse: "onMoveRule callback returned false", onMoveGroupFalse: "onMoveGroup callback returned false", onRemoveFalse: "onRemove callback returned false", add: "rule or group added", remove: "rule or group removed", update: "rule or group updated", move: "rule or group moved", group: "rule or group grouped with another" }; var rootPath = []; // src/components/InlineCombinator.tsx import { jsx as jsx3 } from "react/jsx-runtime"; // src/utils/clsx.ts function toVal(mix) { let k; let y; let str = ""; if (typeof mix === "string" || typeof mix === "number") { str += mix; } else if (typeof mix === "object") { if (Array.isArray(mix)) { const len = mix.length; for (k = 0; k < len; k++) { if (mix[k] && (y = toVal(mix[k]))) { str && (str += " "); str += y; } } } else { for (y in mix) { if (mix[y]) { str && (str += " "); str += y; } } } } return str; } function clsx() { let i = 0; let tmp; let x; let str = ""; const len = arguments.length; for (; i < len; i++) { if ((tmp = i < 0 || arguments.length <= i ? void 0 : arguments[i]) && (x = toVal(tmp))) { str && (str += " "); str += x; } } return str; } var clsx_default = clsx; // src/components/InlineCombinator.tsx var InlineCombinator = (allProps) => { const { component: CombinatorSelectorComponent, ...props } = allProps; const className = clsx(props.schema.suppressStandardClassnames || standardClassnames.betweenRules, props.schema.classNames.betweenRules); return /* @__PURE__ */ jsx3("div", { className, "data-testid": TestID.inlineCombinator, children: /* @__PURE__ */ jsx3(CombinatorSelectorComponent, { ...props, testID: TestID.combinators }) }); }; // src/utils/arrayUtils.ts var splitBy = function(str) { let splitChar = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : defaultJoinChar; return typeof str === "string" ? str.split(`\\${splitChar}`).map((c2) => c2.split(splitChar)).reduce((prev, curr, idx) => { if (idx === 0) { return curr; } return [...prev.slice(0, -1), `${prev.at(-1)}${splitChar}${curr[0]}`, ...curr.slice(1)]; }, []) : []; }; var joinWith = function(strArr) { let joinChar = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : defaultJoinChar; return strArr.map((str) => `${str ?? ""}`.replaceAll(joinChar[0], `\\${joinChar[0]}`)).join(joinChar); }; var trimIfString = (val) => typeof val === "string" ? val.trim() : val; var toArray = function(v) { let { retainEmptyStrings } = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; return Array.isArray(v) ? v.map((v2) => trimIfString(v2)) : typeof v === "string" ? splitBy(v, defaultJoinChar).filter(retainEmptyStrings ? () => true : (s) => !/^\s*$/.test(s)).map((s) => s.trim()) : typeof v === "number" ? [v] : []; }; var nullFreeArray = (arr) => arr.every((el) => el === false || (el ?? false) !== false); // src/utils/misc.ts import { numericRegex as numericQuantityRegex } from "numeric-quantity"; var lc = (v) => typeof v === "string" ? v.toLowerCase() : v; var numericRegex = new RegExp(numericQuantityRegex.source.replace(/^\^/, String.raw`^\s*`).replace(/\$$/, String.raw`\s*$`)); var isPojo = (obj) => obj === null || typeof obj !== "object" ? false : Object.getPrototypeOf(obj) === Object.prototype; var nullOrUndefinedOrEmpty = (value) => value === null || value === void 0 || value === ""; // src/utils/isRuleGroup.ts var isRuleGroup = (rg) => isPojo(rg) && Array.isArray(rg.rules); var isRuleGroupType = (rg) => isRuleGroup(rg) && typeof rg.combinator === "string"; var isRuleGroupTypeIC = (rg) => isRuleGroup(rg) && rg.combinator === void 0; // src/utils/convertQuery.ts import { produce } from "immer"; var combinatorLevels = ["or", "xor", "and"]; var isSameString = (a, b) => lc(a) === b; var generateRuleGroupICWithConsistentCombinators = function(rg) { let baseCombinatorLevel = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0; const baseCombinator = combinatorLevels[baseCombinatorLevel]; if (!rg.rules.includes(baseCombinator)) { return baseCombinatorLevel < combinatorLevels.length - 2 ? generateRuleGroupICWithConsistentCombinators(rg, baseCombinatorLevel + 1) : rg; } return produce(rg, (draft) => { let cursor = 0; while (cursor < draft.rules.length - 2) { if (isSameString(draft.rules[cursor + 1], baseCombinator)) { cursor += 2; continue; } const nextBaseCombinatorIndex = draft.rules.findIndex((r, i) => i > cursor && typeof r === "string" && lc(r) === baseCombinator); if (nextBaseCombinatorIndex === -1) { draft.rules.splice(cursor, draft.rules.length, generateRuleGroupICWithConsistentCombinators( // oxlint-disable-next-line typescript/no-explicit-any { rules: draft.rules.slice(cursor) }, baseCombinatorLevel + 1 )); break; } else { draft.rules.splice(cursor, nextBaseCombinatorIndex - cursor, generateRuleGroupICWithConsistentCombinators( // oxlint-disable-next-line typescript/no-explicit-any { rules: draft.rules.slice(cursor, nextBaseCombinatorIndex) }, baseCombinatorLevel + 1 )); } } }); }; var convertFromIC = (rg) => { if (isRuleGroupType(rg)) { return rg; } const processedRG = generateRuleGroupICWithConsistentCombinators(rg); const rulesAsMixedList = processedRG.rules.map((r) => typeof r === "string" || !isRuleGroup(r) ? r : convertFromIC(r)); const combinator = rulesAsMixedList.length < 2 ? "and" : rulesAsMixedList[1]; const rules = rulesAsMixedList.filter((r) => typeof r !== "string"); return { ...processedRG, combinator, rules }; }; var convertToIC = (rg) => { if (isRuleGroupTypeIC(rg)) { return rg; } const { combinator, ...queryWithoutCombinator } = rg; const rules = []; const { length } = rg.rules; for (const [idx, r] of rg.rules.entries()) { if (isRuleGroup(r)) { rules.push(convertToIC(r)); } else { rules.push(r); } if (combinator && idx < length - 1) { rules.push(combinator); } } return { ...queryWithoutCombinator, rules }; }; function convertQuery(query) { return isRuleGroupTypeIC(query) ? convertFromIC(query) : convertToIC(query); } // src/utils/defaultValidator.ts var defaultValidator = (query) => { const result = {}; const validateRule = (_rule) => { }; const validateGroup = (rg) => { const reasons = []; if (rg.rules.length === 0) { reasons.push(groupInvalidReasons.empty); } else if (!isRuleGroupType(rg)) { let invalidICs = false; for (let i = 0; i < rg.rules.length && !invalidICs; i++) { if (i % 2 === 0 && typeof rg.rules[i] === "string" || i % 2 === 1 && typeof rg.rules[i] !== "string" || i % 2 === 1 && typeof rg.rules[i] === "string" && !defaultCombinators.map((c2) => c2.name).includes(rg.rules[i])) { invalidICs = true; } } if (invalidICs) { reasons.push(groupInvalidReasons.invalidIndependentCombinators); } } if (isRuleGroupType(rg) && !defaultCombinators.map((c2) => c2.name).includes(rg.combinator) && rg.rules.length > 1) { reasons.push(groupInvalidReasons.invalidCombinator); } if (rg.id) { result[rg.id] = reasons.length > 0 ? { valid: false, reasons } : true; } for (const r of rg.rules) { if (typeof r === "string") { } else if (isRuleGroup(r)) { validateGroup(r); } else { validateRule(r); } } }; validateGroup(query); return result; }; // src/utils/optGroupUtils.ts import { produce as produce2 } from "immer"; var isOptionWithName = (opt) => isPojo(opt) && "name" in opt && typeof opt.name === "string"; var isOptionWithValue = (opt) => isPojo(opt) && "value" in opt && typeof opt.value === "string"; function toFullOption(opt, baseProperties, labelMap) { const recipe = produce2((draft) => { const idObj = {}; let needsUpdating = !!baseProperties; if (typeof draft === "string") { return { ...baseProperties, name: draft, value: draft, label: labelMap?.[draft] ?? draft }; } if (isOptionWithName(draft) && !isOptionWithValue(draft)) { idObj.value = draft.name; needsUpdating = true; } else if (!isOptionWithName(draft) && isOptionWithValue(draft)) { idObj.name = draft.value; needsUpdating = true; } if (needsUpdating) { return Object.assign({}, baseProperties, draft, idObj); } }); return recipe(opt); } function toFullOptionList(optList, baseProperties, labelMap) { if (!Array.isArray(optList)) { return []; } const recipe = produce2((draft) => { if (isFlexibleOptionGroupArray(draft)) { for (const optGroup of draft) { for (const [idx, opt] of optGroup.options.entries()) optGroup.options[idx] = toFullOption(opt, baseProperties, labelMap); } } else { for (const [idx, opt] of draft.entries()) draft[idx] = toFullOption(opt, baseProperties, labelMap); } }); return recipe(optList); } function toFullOptionMap(optMap, baseProperties) { return Object.fromEntries(Object.entries(optMap).map((_ref) => { let [k, v] = _ref; return [k, toFullOption(v, baseProperties)]; })); } var uniqByName = (originalArray) => uniqByIdentifier(originalArray); var uniqByIdentifier = (originalArray) => { const names = /* @__PURE__ */ new Set(); const newArray = []; for (const el of originalArray) { if (!names.has(el.value ?? el.name)) { names.add(el.value ?? el.name); newArray.push(el); } } return originalArray.length === newArray.length ? originalArray : newArray; }; var isOptionGroupArray = (arr) => Array.isArray(arr) && arr.length > 0 && isPojo(arr[0]) && "options" in arr[0] && Array.isArray(arr[0].options); var isFlexibleOptionArray = (arr) => { let isFOA = false; if (Array.isArray(arr)) { for (const o of arr) { if (isOptionWithName(o) || isOptionWithValue(o)) { isFOA = true; } else { return false; } } } return isFOA; }; var isFullOptionArray = (arr) => { let isFOA = false; if (Array.isArray(arr)) { for (const o of arr) { if (isOptionWithName(o) && isOptionWithValue(o)) { isFOA = true; } else { return false; } } } return isFOA; }; var isFlexibleOptionGroupArray = function(arr) { let { allowEmpty = false } = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; let isFOGA = false; if (Array.isArray(arr)) { for (const og of arr) { if (isPojo(og) && "options" in og && (isFlexibleOptionArray(og.options) || allowEmpty && Array.isArray(og.options) && og.options.length === 0)) { isFOGA = true; } else { return false; } } } return isFOGA; }; var isFullOptionGroupArray = function(arr) { let { allowEmpty = false } = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; let isFOGA = false; if (Array.isArray(arr)) { for (const og of arr) { if (isPojo(og) && "options" in og && (isFullOptionArray(og.options) || allowEmpty && Array.isArray(og.options) && og.options.length === 0)) { isFOGA = true; } else { return false; } } } return isFOGA; }; var getOption = (arr, name) => (isFlexibleOptionGroupArray(arr, { allowEmpty: true }) ? arr.flatMap((og) => og.options) : arr).find((op) => op.value === name || op.name === name); var getFirstOption = (arr) => { if (!Array.isArray(arr) || arr.length === 0) { return null; } else if (isFlexibleOptionGroupArray(arr, { allowEmpty: true })) { for (const og of arr) { if (og.options.length > 0) { return og.options[0].value ?? og.options[0].name; } } return null; } return arr[0].value ?? arr[0].name; }; var toFlatOptionArray = (arr) => uniqByIdentifier(isOptionGroupArray(arr) ? arr.flatMap((og) => og.options) : arr); var uniqOptGroups = (originalArray) => { const labels = /* @__PURE__ */ new Set(); const names = /* @__PURE__ */ new Set(); const newArray = []; for (const el of originalArray) { if (!labels.has(el.label)) { labels.add(el.label); const optionsForThisGroup = []; for (const opt of el.options) { if (!names.has(opt.value ?? opt.name)) { names.add(opt.value ?? opt.name); optionsForThisGroup.push(toFullOption(opt)); } } newArray.push({ ...el, options: optionsForThisGroup }); } } return newArray; }; var uniqOptList = (originalArray) => { if (isFlexibleOptionGroupArray(originalArray)) { return uniqOptGroups(originalArray); } return uniqByIdentifier(originalArray.map((o) => toFullOption(o))); }; // src/utils/filterFieldsByComparator.ts var filterByComparator = (field, operator, fieldToCompare) => { const fullField = toFullOption(field); const fullFieldToCompare = toFullOption(fieldToCompare); if (fullField.value === fullFieldToCompare.value) { return false; } if (typeof fullField.comparator === "string") { return fullField[fullField.comparator] === fullFieldToCompare[fullField.comparator]; } return fullField.comparator?.(fullFieldToCompare, operator) ?? /* istanbul ignore next */ false; }; var filterFieldsByComparator = (field, fields, operator) => { if (!field.comparator) { const filterOutSameField = (f) => (f.value ?? /* istanbul ignore next */ f.name) !== (field.value ?? /* istanbul ignore next */ field.name); if (isFlexibleOptionGroupArray(fields)) { return fields.map((og) => ({ ...og, options: og.options.filter((v) => filterOutSameField(v)) })); } return fields.filter((v) => filterOutSameField(v)); } if (isFlexibleOptionGroupArray(fields)) { return fields.map((og) => ({ ...og, options: og.options.filter((f) => filterByComparator(field, operator, f)) })).filter((og) => og.options.length > 0); } return fields.filter((f) => filterByComparator(field, operator, f)); }; // src/utils/parseNumber.ts import { numericQuantity } from "numeric-quantity"; var parseNumber = function(val) { let { parseNumbers, bigIntOnOverflow } = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; if (!parseNumbers || typeof val === "bigint" || typeof val === "number") { return val; } if (parseNumbers === "native") { return Number.parseFloat(val); } const valAsNum = ( // TODO: Should these options be configurable? numericQuantity(val, { allowTrailingInvalid: parseNumbers === "enhanced", bigIntOnOverflow, romanNumerals: false, round: false }) ); return typeof valAsNum === "bigint" || !Number.isNaN(valAsNum) ? valAsNum : val; }; // src/utils/transformQuery.ts import { produce as produce3 } from "immer"; var remapProperties = (obj, propertyMap, deleteRemappedProperties) => produce3(obj, (draft) => { for (const [k, v] of Object.entries(propertyMap)) { if (v === false) { delete draft[k]; } else if (!!v && k !== v && k in draft) { draft[v] = draft[k]; if (deleteRemappedProperties) { delete draft[k]; } } } }); function transformQuery(query) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const { ruleProcessor = (r) => r, ruleGroupProcessor = (rg) => rg, propertyMap = {}, combinatorMap = {}, operatorMap = {}, omitPath = false, deleteRemappedProperties = true } = options; const processGroup = (rg) => ({ ...ruleGroupProcessor(remapProperties({ ...rg, ...isRuleGroupType(rg) ? { combinator: combinatorMap[rg.combinator] ?? rg.combinator } : {} }, propertyMap, deleteRemappedProperties)), ...propertyMap["rules"] === false ? null : { // oxlint-disable-next-line typescript/no-explicit-any [propertyMap["rules"] ?? "rules"]: rg.rules.map((r, idx) => { const pathObject = omitPath ? null : { path: [...rg.path, idx] }; if (typeof r === "string") { return combinatorMap[r] ?? r; } else if (isRuleGroup(r)) { return processGroup({ ...r, ...pathObject }); } return ruleProcessor(remapProperties({ ...r, ...pathObject, ..."operator" in r ? { operator: operatorMap[r.operator] ?? r.operator } : {} }, propertyMap, deleteRemappedProperties)); }) } }); return processGroup({ ...query, ...omitPath ? null : { path: [] } }); } // src/utils/isRuleOrGroupValid.ts var isValidationResult = (vr) => isPojo(vr) && typeof vr.valid === "boolean"; var isRuleOrGroupValid = (rg, validationResult, validator) => { if (typeof validationResult === "boolean") { return validationResult; } if (isValidationResult(validationResult)) { return validationResult.valid; } if (typeof validator === "function" && !isRuleGroup(rg)) { const vr = validator(rg); if (typeof vr === "boolean") { return vr; } if (isValidationResult(vr)) { return vr.valid; } } return true; }; // src/utils/getParseNumberMethod.ts var getParseNumberMethod = (_ref) => { let { parseNumbers, inputType } = _ref; if (typeof parseNumbers === "string") { const [method, level] = parseNumbers.split("-"); if (level === "limited") { return inputType === "number" ? method : false; } return method; } return parseNumbers ? "strict" : false; }; // src/utils/formatQuery/utils.ts var mapSQLOperator = (rqbOperator) => { switch (lc(rqbOperator)) { case "null": return "is null"; case "notnull": return "is not null"; case "notin": return "not in"; case "notbetween": return "not between"; case "contains": case "beginswith": case "endswith": return "like"; case "doesnotcontain": case "doesnotbeginwith": case "doesnotendwith": return "not like"; default: return rqbOperator; } }; var mongoOperators = { "=": "$eq", "!=": "$ne", "<": "$lt", "<=": "$lte", ">": "$gt", ">=": "$gte", in: "$in", notin: "$nin", notIn: "$nin" // only here for backwards compatibility }; var prismaOperators = { "=": "equals", "!=": "not", "<": "lt", "<=": "lte", ">": "gt", ">=": "gte", in: "in", notin: "notIn" }; var celCombinatorMap = { and: "&&", or: "||" }; var jsonLogicAdditionalOperators = { startsWith: (a, b) => typeof a === "string" && a.startsWith(b), endsWith: (a, b) => typeof a === "string" && a.endsWith(b) }; var numerifyValues = (rg, options) => ({ ...rg, // @ts-expect-error TS doesn't keep track of odd/even indexes here rules: rg.rules.map((r) => { if (typeof r === "string") { return r; } if (isRuleGroup(r)) { return numerifyValues(r, options); } const fieldData = getOption(options.fields, r.field); const parseNumbers = getParseNumberMethod({ parseNumbers: options.parseNumbers, inputType: fieldData?.inputType }); if (Array.isArray(r.value)) { return { ...r, value: r.value.map((v) => parseNumber(v, { parseNumbers })) }; } const valAsArray = toArray(r.value, { retainEmptyStrings: true }).map((v) => parseNumber(v, { parseNumbers })); if (valAsArray.every((v) => typeof v === "number")) { if (valAsArray.length > 1) { return { ...r, value: valAsArray }; } else if (valAsArray.length === 1) { return { ...r, value: valAsArray[0] }; } } return r; }) }); var isValidValue = (value) => typeof value === "string" && value.length > 0 || typeof value === "number" && !Number.isNaN(value) || typeof value !== "string" && typeof value !== "number"; var shouldRenderAsNumber = (value, parseNumbers) => !!parseNumbers && (typeof value === "number" || typeof value === "bigint" || typeof value === "string" && numericRegex.test(value)); var isValueProcessorLegacy = (valueProcessor) => valueProcessor.length >= 3; var getQuoteFieldNamesWithArray = function() { let quoteFieldNamesWith = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : ["", ""]; return Array.isArray(quoteFieldNamesWith) ? quoteFieldNamesWith : typeof quoteFieldNamesWith === "string" ? [quoteFieldNamesWith, quoteFieldNamesWith] : quoteFieldNamesWith ?? ["", ""]; }; var getQuotedFieldName = (fieldName, _ref) => { let { quoteFieldNamesWith, fieldIdentifierSeparator } = _ref; const [qPre, qPost] = getQuoteFieldNamesWithArray(quoteFieldNamesWith); return typeof fieldIdentifierSeparator === "string" && fieldIdentifierSeparator.length > 0 ? joinWith(splitBy(fieldName, fieldIdentifierSeparator).map((part) => `${qPre}${part}${qPost}`), fieldIdentifierSeparator) : `${qPre}${fieldName}${qPost}`; }; var defaultWordOrder = ["S", "V", "O"]; var normalizeConstituentWordOrder = (input) => { const result = []; const letterSet = new Set(defaultWordOrder); for (const char of input.toUpperCase()) { if (letterSet.has(char)) { result.push(char); letterSet.delete(char); if (letterSet.size === 0) break; } } for (const letter of defaultWordOrder) { if (letterSet.has(letter)) { result.push(letter); } } return result; }; var defaultNLTranslations = { // and: 'and', // or: 'or', // true: 'true', // false: 'false', groupPrefix: "", // groupPrefix_not: '', groupPrefix_not_xor: "either zero or more than one of", groupPrefix_xor: "exactly one of", groupSuffix: "is true", groupSuffix_not: "is not true" // groupSuffix_not_xor: 'is true', // groupSuffix_xor: 'is true', }; var translationMatchFilter = (key, keyToTest, conditions) => ( // The translation matches the base key keyToTest.startsWith(key) && // The translation specifies all conditions conditions.every((c2) => ( // This translation specifies _this_ condition keyToTest.includes(`_${c2}`) && // This translation specifies the same _total number_ of conditions keyToTest.match(/_/g)?.length === conditions.length )) ); var getNLTranslataion = function(key, translations) { let conditions = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : []; return conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ?? /* istanbul ignore next */ "" : Object.entries(translations).find((_ref2) => { let [keyToTest] = _ref2; return translationMatchFilter(key, keyToTest, conditions); })?.[1] ?? Object.entries(defaultNLTranslations).find((_ref3) => { let [keyToTest] = _ref3; return translationMatchFilter(key, keyToTest, conditions); })?.[1] ?? defaultNLTranslations[key] ?? /* istanbul ignore next */ ""; }; var processMatchMode = (rule) => { const { mode, threshold } = rule.match ?? {}; if (mode) { if (!isRuleGroup(rule.value)) return false; const matchModeLC = lc(mode); const matchModeCoerced = matchModeLC === "atleast" && threshold === 1 ? "some" : matchModeLC === "atmost" && threshold === 0 ? "none" : matchModeLC; if ((matchModeCoerced === "atleast" || matchModeCoerced === "atmost" || matchModeCoerced === "exactly") && (typeof threshold !== "number" || threshold < 0)) { return false; } return { mode: matchModeCoerced, threshold }; } }; var bigIntJsonStringifyReplacer = (_key, value) => typeof value === "bigint" ? { $bigint: value.toString() } : value; var bigIntJsonParseReviver = (_key, value) => isPojo(value) && Object.keys(value).length === 1 && typeof value.$bigint === "string" ? BigInt(value.$bigint) : value; // src/utils/formatQuery/defaultRuleGroupProcessorCEL.ts var defaultRuleGroupProcessorCEL = (ruleGroup, options) => { const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? fallbackExpression : ""; } const expression = rg.rules.map((rule) => { if (typeof rule === "string") { return celCombinatorMap[rule]; } if (isRuleGroup(rule)) { return processRuleGroup(rule); } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return ""; } const fieldData = getOption(fields, rule.field); return ruleProcessor(rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), escapeQuotes: (rule.valueSource ?? "value") === "value", fieldData }); }).filter(Boolean).join(isRuleGroupType(rg) ? ` ${celCombinatorMap[rg.combinator]} ` : " "); const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""]; return expression ? `${prefix}${expression}${suffix}` : fallbackExpression; }; return processRuleGroup(ruleGroup, true); }; // src/utils/formatQuery/defaultRuleProcessorCEL.ts var shouldNegate = (op) => op.startsWith("not") || op.startsWith("doesnot"); var escapeDoubleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? v : v.replaceAll(`"`, `\\"`); var defaultRuleProcessorCEL = function(rule) { let opts = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const { escapeQuotes, parseNumbers, preserveValueOrder } = opts; const { field, operator, value, valueSource } = rule; const valueIsField = valueSource === "field"; const operatorTL = lc(operator === "=" ? "==" : operator); const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers); const matchEval = processMatchMode(rule); if (matchEval === false) { return ""; } else if (matchEval) { const { mode, threshold } = matchEval; const arrayElementAlias = "elem_alias"; const celQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({ ...r, field: `${arrayElementAlias}${r.field ? `.${r.field}` : ""}` }) }); const nestedArrayFilter = defaultRuleGroupProcessorCEL(celQuery, opts); switch (mode) { case "all": return `${field}.all(${arrayElementAlias}, ${nestedArrayFilter})`; case "none": case "some": return `${mode === "none" ? "!" : ""}${field}.exists(${arrayElementAlias}, ${nestedArrayFilter})`; case "atleast": case "atmost": case "exactly": { const totalCount = `double(${field}.size())`; const filteredCount = `${field}.filter(${arrayElementAlias}, ${nestedArrayFilter}).size()`; const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=="; if (threshold > 0 && threshold < 1) { return `${filteredCount} ${op} (${totalCount} * ${threshold})`; } return `${filteredCount} ${op} ${threshold}`; } } } switch (operatorTL) { case "<": case "<=": case "==": case "!=": case ">": case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`}`; case "contains": case "doesnotcontain": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.contains(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "beginswith": case "doesnotbeginwith": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.startsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "endswith": case "doesnotendwith": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.endsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "null": return `${field} == null`; case "notnull": return `${field} != null`; case "in": case "notin": { const [prefix, suffix] = shouldNegate(operatorTL) ? ["!(", ")"] : ["", ""]; const valueAsArray = toArray(value); return `${prefix}${field} in [${valueAsArray.map((val) => valueIsField || shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : `"${escapeDoubleQuotes(val, escapeQuotes)}"`).join(", ")}]${suffix}`; } case "between": case "notbetween": { const valueAsArray = toArray(value); if (valueAsArray.length >= 2 && !nullOrUndefinedOrEmpty(valueAsArray[0]) && !nullOrUndefinedOrEmpty(valueAsArray[1])) { const [first, second] = valueAsArray; const shouldParseNumbers = !(parseNumbers === false); const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : Number.NaN; const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : Number.NaN; let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : `"${escapeDoubleQuotes(first, escapeQuotes)}"` : firstNum; let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : `"${escapeDoubleQuotes(second, escapeQuotes)}"` : secondNum; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { const tempNum = secondNum; secondValue = firstNum; firstValue = tempNum; } return operatorTL === "between" ? `(${field} >= ${firstValue} && ${field} <= ${secondValue})` : `(${field} < ${firstValue} || ${field} > ${secondValue})`; } else { return ""; } } } return ""; }; // src/utils/formatQuery/defaultRuleGroupProcessorMongoDBQuery.ts var mongoDbFallback = { $and: [{ $expr: true }] }; var defaultRuleGroupProcessorMongoDBQuery = (ruleGroup, options, meta) => { const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? mongoDbFallback : false; } const combinator = `$${lc(rg.combinator)}`; let hasChildRules = false; const expressions = rg.rules.map((rule) => { if (isRuleGroup(rule)) { const processedRuleGroup = processRuleGroup(rule); if (processedRuleGroup) { hasChildRules = true; return processedRuleGroup; } return false; } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return false; } const fieldData = getOption(fields, rule.field); return ruleProcessor(rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), fieldData }, meta); }).filter(Boolean); return expressions.length > 0 ? expressions.length === 1 && !hasChildRules ? expressions[0] : { [combinator]: expressions } : mongoDbFallback; }; return processRuleGroup(convertFromIC(ruleGroup), true); }; // src/utils/formatQuery/defaultRuleProcessorMongoDBQuery.ts var processNumber = function(value, fallback) { let parseNumbers = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false; return shouldRenderAsNumber(value, parseNumbers || typeof value === "bigint") ? Number(parseNumber(value, { parseNumbers: "strict" })) : fallback; }; var defaultRuleProcessorMongoDBQuery = function(rule) { let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const { field, operator, value, valueSource } = rule; const { parseNumbers, preserveValueOrder, context } = options; const valueIsField = valueSource === "field"; const { avoidFieldsAsKeys } = context ?? {}; const matchEval = processMatchMode(rule); if (matchEval === false) { return; } else if (matchEval) { const { mode, threshold } = matchEval; const totalCount = { $size: { $ifNull: [`$${field}`, []] } }; const subQueryNoAggCtx = defaultRuleGroupProcessorMongoDBQuery(transformQuery(value, { ruleProcessor: (r) => ({ ...r, field: r.field ? `${field}.${r.field}` : field }) }), { ...options, // We have to override `ruleProcessor` in case original `format` is "mongodb" ruleProcessor: defaultRuleProcessorMongoDBQuery, context: { ...options.context, avoidFieldsAsKeys: false } }); const subQueryWithAggCtx = defaultRuleGroupProcessorMongoDBQuery(transformQuery(value, { ruleProcessor: (r) => ({ ...r, field: r.field ? `$item.${r.field}` : "$item" }) }), { ...options, // We have to override `ruleProcessor` in case original `format` is "mongodb" ruleProcessor: defaultRuleProcessorMongoDBQuery, context: { ...options.context, avoidFieldsAsKeys: true } }); const filteredCount = { $size: { $ifNull: [{ $filter: { input: `$${field}`, as: "item", cond: { $and: [subQueryWithAggCtx] } } }, []] } }; switch (mode) { case "all": return { $expr: { $eq: [filteredCount, totalCount] } }; case "none": return { $nor: [subQueryNoAggCtx] }; case "some": return subQueryNoAggCtx; case "atleast": case "atmost": case "exactly": { const op = mode === "atleast" ? mongoOperators[">="] : mode === "atmost" ? mongoOperators["<="] : mongoOperators["="]; if (threshold > 0 && threshold < 1) { return { $expr: { [op]: [filteredCount, { $multiply: [totalCount, threshold] }] } }; } return { $expr: { [op]: [filteredCount, threshold] } }; } } } if (operator === "=" && !valueIsField) { return avoidFieldsAsKeys ? { $eq: [`$${field}`, processNumber(value, value, parseNumbers)] } : { [field]: processNumber(value, value, parseNumbers) }; } const operatorLC = lc(operator); switch (operatorLC) { case "<": case "<=": case "=": case "!=": case ">": case ">=": { const mongoOperator = mongoOperators[operatorLC]; return valueIsField ? { [mongoOperator]: [`$${field}`, `$${value}`] } : avoidFieldsAsKeys ? { $and: [{ $ne: [`$${field}`, null] }, { [mongoOperator]: [`$${field}`, processNumber(value, value, parseNumbers)] }] } : { [field]: { [mongoOperator]: processNumber(value, value, parseNumbers) } }; } case "contains": return valueIsField ? { $where: `this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: value } } : { [field]: { $regex: value } }; case "beginswith": return valueIsField ? { $where: `this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: `^${value}` } } : { [field]: { $regex: `^${value}` } }; case "endswith": return valueIsField ? { $where: `this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: `${value}$` } } : { [field]: { $regex: `${value}$` } }; case "doesnotcontain": return valueIsField ? { $where: `!this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: value } } } : { [field]: { $not: { $regex: value } } }; case "doesnotbeginwith": return valueIsField ? { $where: `!this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: `^${value}` } } } : { [field]: { $not: { $regex: `^${value}` } } }; case "doesnotendwith": return valueIsField ? { $where: `!this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: `${value}$` } } } : { [field]: { $not: { $regex: `${value}$` } } }; case "null": return avoidFieldsAsKeys ? { $eq: [`$${field}`, null] } : { [field]: null }; case "notnull": return avoidFieldsAsKeys ? { $ne: [`$${field}`, null] } : { [field]: { $ne: null } }; case "in": case "notin": { const valueAsArray = toArray(value); return valueIsField ? { $where: `${operatorLC === "notin" ? "!" : ""}[${valueAsArray.map((val) => `this.${val}`).join(",")}].includes(this.${field})` } : avoidFieldsAsKeys ? operatorLC === "notin" ? { $not: { [mongoOperators.in]: [`$${field}`, valueAsArray.map((val) => processNumber(val, val, parseNumbers))] } } : { [mongoOperators[operatorLC]]: [`$${field}`, valueAsArray.map((val) => processNumber(val, val, parseNumbers))] } : { [field]: { [mongoOperators[operatorLC]]: valueAsArray.map((val) => processNumber(val, val, parseNumbers)) } }; } case "between": case "notbetween": { const valueAsArray = toArray(value); if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) { const [first, second] = valueAsArray; const firstNum = processNumber(first, Number.NaN, true); const secondNum = processNumber(second, Number.NaN, true); let firstValue = valueIsField ? first : Number.isNaN(firstNum) ? first : firstNum; let secondValue = valueIsField ? second : Number.isNaN(secondNum) ? second : secondNum; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { const tempNum = secondNum; secondValue = firstNum; firstValue = tempNum; } if (operatorLC === "between") { return valueIsField ? { $gte: [`$${field}`, `$${firstValue}`], $lte: [`$${field}`, `$${secondValue}`] } : avoidFieldsAsKeys ? { $and: [{ $gte: [`$${field}`, firstValue] }, { $lte: [`$${field}`, secondValue] }] } : { [field]: { $gte: firstValue, $lte: secondValue } }; } else { return valueIsField ? { $or: [{ $lt: [`$${field}`, `$${firstValue}`] }, { $gt: [`$${field}`, `$${secondValue}`] }] } : avoidFieldsAsKeys ? { $or: [{ $lt: [`$${field}`, firstValue] }, { $gt: [`$${field}`, secondValue] }] } : { $or: [{ [field]: { $lt: firstValue } }, { [field]: { $gt: secondValue } }] }; } } else { return ""; } } } return ""; }; // src/utils/formatQuery/defaultRuleProcessorMongoDB.ts var defaultRuleProcessorMongoDB = (rule, options) => { const queryObj = defaultRuleProcessorMongoDBQuery(rule, options); return queryObj ? JSON.stringify(queryObj) : ""; }; // src/utils/formatQuery/defaultRuleGroupProcessorSpEL.ts var defaultRuleGroupProcessorSpEL = (ruleGroup, options) => { const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? fallbackExpression : ""; } const expression = rg.rules.map((rule) => { if (typeof rule === "string") { return rule; } if (isRuleGroup(rule)) { return processRuleGroup(rule); } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return ""; } const fieldData = getOption(fields, rule.field); return ruleProcessor(rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), escapeQuotes: (rule.valueSource ?? "value") === "value", fieldData }); }).filter(Boolean).join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " "); const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""]; return expression ? `${prefix}${expression}${suffix}` : fallbackExpression; }; return processRuleGroup(ruleGroup, true); }; // src/utils/formatQuery/defaultRuleProcessorSpEL.ts var shouldNegate2 = (op) => op.startsWith("not") || op.startsWith("doesnot"); var wrapInNegation = (clause, negate2) => negate2 ? `!(${clause})` : `${clause}`; var escapeSingleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? v : v.replaceAll(`'`, `\\'`); var defaultRuleProcessorSpEL = function(rule) { let opts = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}; const { field, operator, value, valueSource } = rule; const { escapeQuotes, parseNumbers, preserveValueOrder } = opts; const valueIsField = valueSource === "field"; const operatorTL = lc(operator === "=" ? "==" : operator); const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers); const matchEval = processMatchMode(rule); if (matchEval === false) { return ""; } else if (matchEval) { const { mode, threshold } = matchEval; const nestedArrayFilter = defaultRuleGroupProcessorSpEL(transformQuery(rule.value, { ruleProcessor: (r) => ({ ...r, field: `${r.field || "#this"}` }) }), opts); const totalCount = `${field}.size()`; const filteredCount = `${field}.?[${nestedArrayFilter}].size()`; switch (mode) { case "all": return `${filteredCount} == ${totalCount}`; case "none": return `${filteredCount} == 0`; case "some": return `${filteredCount} >= 1`; case "atleast": case "atmost": case "exactly": { const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=="; if (threshold > 0 && threshold < 1) { return `${filteredCount} ${op} (${totalCount} * ${threshold})`; } return `${filteredCount} ${op} ${threshold}`; } } } switch (operatorTL) { case "<": case "<=": case "==": case "!=": case ">": case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`; case "contains": case "doesnotcontain": return wrapInNegation(`${field} matches ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`, shouldNegate2(operatorTL)); case "beginswith": case "doesnotbeginwith": { const valueTL = valueIsField ? `'^'.concat(${trimIfString(value)})` : `'${typeof value === "string" && !value.startsWith("^") || useBareValue ? "^" : ""}${escapeSingleQuotes(value, escapeQuotes)}'`; return wrapInNegation(`${field} matches ${valueTL}`, shouldNegate2(operatorTL)); } case "endswith": case "doesnotendwith": { const valueTL = valueIs