@react-querybuilder/core
Version:
React Query Builder component for constructing queries and filters, with utilities for executing them in various database and evaluation contexts
1,519 lines (1,496 loc) • 189 kB
JavaScript
import { produce } from "immer";
import { numericQuantity, numericRegex as numericRegex$1 } from "numeric-quantity";
//#region src/defaults.ts
/**
* @group Defaults
*/
const defaultPlaceholderName = "~";
/**
* @group Defaults
*/
const defaultPlaceholderLabel = "------";
/**
* Default `name` for placeholder option in the `fields` array.
*
* @group Defaults
*/
const defaultPlaceholderFieldName = defaultPlaceholderName;
/**
* Default `label` for placeholder option in the `fields` array.
*
* @group Defaults
*/
const defaultPlaceholderFieldLabel = defaultPlaceholderLabel;
/**
* Default `label` for placeholder option group in the `fields` array.
*
* @group Defaults
*/
const defaultPlaceholderFieldGroupLabel = defaultPlaceholderLabel;
/**
* Default `name` for placeholder option in the `operators` array.
*
* @group Defaults
*/
const defaultPlaceholderOperatorName = defaultPlaceholderName;
/**
* Default `label` for placeholder option in the `operators` array.
*
* @group Defaults
*/
const defaultPlaceholderOperatorLabel = defaultPlaceholderLabel;
/**
* Default `label` for placeholder option group in the `operators` array.
*
* @group Defaults
*/
const defaultPlaceholderOperatorGroupLabel = defaultPlaceholderLabel;
/**
* Default `name` for placeholder option in the `values` array.
*
* @group Defaults
*/
const defaultPlaceholderValueName = defaultPlaceholderName;
/**
* Default `label` for placeholder option in the `values` array.
*
* @group Defaults
*/
const defaultPlaceholderValueLabel = defaultPlaceholderLabel;
/**
* Default `label` for placeholder option group in the `values` array.
*
* @group Defaults
*/
const defaultPlaceholderValueGroupLabel = defaultPlaceholderLabel;
/**
* Default configuration of translatable strings.
*
* @group Defaults
*/
const defaultTranslations = {
fields: {
title: "Field",
placeholderName: defaultPlaceholderFieldName,
placeholderLabel: defaultPlaceholderFieldLabel,
placeholderGroupLabel: defaultPlaceholderFieldGroupLabel
},
operators: {
title: "Operator",
placeholderName: defaultPlaceholderOperatorName,
placeholderLabel: defaultPlaceholderOperatorLabel,
placeholderGroupLabel: defaultPlaceholderOperatorGroupLabel
},
values: {
title: "Values",
placeholderName: defaultPlaceholderValueName,
placeholderLabel: defaultPlaceholderValueLabel,
placeholderGroupLabel: defaultPlaceholderValueGroupLabel
},
matchMode: { title: "Match mode" },
matchThreshold: { title: "Match threshold" },
value: { title: "Value" },
removeRule: {
label: "⨯",
title: "Remove rule"
},
removeGroup: {
label: "⨯",
title: "Remove group"
},
addRule: {
label: "+ Rule",
title: "Add rule"
},
addGroup: {
label: "+ Group",
title: "Add group"
},
combinators: { title: "Combinator" },
notToggle: {
label: "Not",
title: "Invert this group"
},
cloneRule: {
label: "⧉",
title: "Clone rule"
},
cloneRuleGroup: {
label: "⧉",
title: "Clone group"
},
shiftActionUp: {
label: "˄",
title: "Shift up"
},
shiftActionDown: {
label: "˅",
title: "Shift down"
},
dragHandle: {
label: "⁞⁞",
title: "Drag handle"
},
lockRule: {
label: "🔓",
title: "Lock rule"
},
lockGroup: {
label: "🔓",
title: "Lock group"
},
lockRuleDisabled: {
label: "🔒",
title: "Unlock rule"
},
lockGroupDisabled: {
label: "🔒",
title: "Unlock group"
},
muteRule: {
label: "🔊",
title: "Mute rule"
},
muteGroup: {
label: "🔊",
title: "Mute group"
},
unmuteRule: {
label: "🔇",
title: "Unmute rule"
},
unmuteGroup: {
label: "🔇",
title: "Unmute group"
},
valueSourceSelector: { title: "Value source" }
};
/**
* Default character used to `.join` and `.split` arrays.
*
* @group Defaults
*/
const defaultJoinChar = ",";
const 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"
};
const defaultCombinatorLabelMap = {
and: "AND",
or: "OR",
xor: "XOR"
};
/**
* Default operator list.
*
* @group Defaults
*/
const 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"
}
];
/**
* Map of default operators to their respective opposite/negating operators.
*
* @group Defaults
*/
const 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"
};
/**
* Default combinator list.
*
* @group Defaults
*/
const defaultCombinators = [{
name: "and",
value: "and",
label: "AND"
}, {
name: "or",
value: "or",
label: "OR"
}];
/**
* Default combinator list, with `XOR` added.
*
* @group Defaults
*/
const defaultCombinatorsExtended = [...defaultCombinators, {
name: "xor",
value: "xor",
label: "XOR"
}];
/**
* Default match modes.
*
* @group Defaults
*/
const 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"
}
];
/**
* Standard classnames applied to each component.
*
* @group Defaults
*/
const 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",
dndDropNotAllowed: "dndDropNotAllowed",
dragHandle: "queryBuilder-dragHandle",
disabled: "queryBuilder-disabled",
muted: "queryBuilder-muted",
lockRule: "rule-lock",
lockGroup: "ruleGroup-lock",
muteRule: "rule-mute",
muteGroup: "ruleGroup-mute",
valueSource: "rule-valueSource",
valueListItem: "rule-value-list-item",
branches: "queryBuilder-branches",
justified: "queryBuilder-justified",
hasSubQuery: "rule-hasSubQuery"
};
/**
* Default classnames for each component.
*
* @group Defaults
*/
const defaultControlClassnames = {
queryBuilder: "",
ruleGroup: "",
header: "",
body: "",
combinators: "",
addRule: "",
addGroup: "",
cloneRule: "",
cloneGroup: "",
removeGroup: "",
notToggle: "",
rule: "",
fields: "",
matchMode: "",
matchThreshold: "",
operators: "",
value: "",
removeRule: "",
shiftActions: "",
dragHandle: "",
lockRule: "",
lockGroup: "",
muteRule: "",
muteGroup: "",
muted: "",
valueSource: "",
actionElement: "",
valueSelector: "",
betweenRules: "",
valid: "",
invalid: "",
dndDragging: "",
dndOver: "",
dndGroup: "",
dndCopy: "",
dndDropNotAllowed: "",
disabled: "",
valueListItem: "",
branches: "",
hasSubQuery: ""
};
/**
* Default reason codes for a group being invalid.
*
* @group Defaults
*/
const groupInvalidReasons = {
empty: "empty",
invalidCombinator: "invalid combinator",
invalidIndependentCombinators: "invalid independent combinators"
};
/**
* Component identifiers for testing.
*
* @group Defaults
*/
const 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",
muteRule: "mute-rule",
muteGroup: "mute-group",
valueSourceSelector: "value-source-selector",
matchModeEditor: "match-mode-editor"
};
const 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"
};
/**
* The {@link Path} of the root group.
*
* @group Defaults
*/
const rootPath = [];
/**
* Default values for all `boolean`
* {@link react-querybuilder!QueryBuilder QueryBuilder} options.
*
* @group Defaults
*/
const queryBuilderFlagDefaults = {
addRuleToNewGroups: false,
autoSelectField: true,
autoSelectOperator: true,
autoSelectValue: false,
debugMode: false,
enableDragAndDrop: false,
enableMountQueryChange: true,
listsAsArrays: false,
resetOnFieldChange: true,
resetOnOperatorChange: false,
showCloneButtons: false,
showCombinatorsBetweenRules: false,
showLockButtons: false,
showMuteButtons: false,
showNotToggle: false,
showShiftActions: false,
suppressStandardClassnames: false
};
//#endregion
//#region src/utils/arrayUtils.ts
/**
* Splits a string by a given character (see {@link defaultJoinChar}). Escaped characters
* (characters preceded by a backslash) will not apply to the split, and the backslash will
* be removed in the array element. Inverse of {@link joinWith}.
*
* @example
* splitBy('this\\,\\,that,,the other,,,\\,')
* // or
* splitBy('this\\,\\,that,,the other,,,\\,', ',')
* // would return
* ['this,,that', '', 'the other', '', '', ',']
*/
const splitBy = (str, splitChar = defaultJoinChar) => typeof str === "string" ? str.split(`\\${splitChar}`).map((c) => c.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)
];
}, []) : [];
/**
* Joins an array of strings using the given character (see {@link defaultJoinChar}). When
* the given character appears in an array element, a backslash will be added just before it
* to distinguish it from the join character. Effectively the inverse of {@link splitBy}.
*
* TIP: The join character can actually be a string of any length. Only the first character
* will be searched for in the array elements and preceded by a backslash.
*
* @example
* joinWith(['this,,that', '', 'the other', '', '', ','], ', ')
* // would return
* 'this\\,\\,that, , the other, , , \\,'
*/
const joinWith = (strArr, joinChar = defaultJoinChar) => strArr.map((str) => `${str !== null && str !== void 0 ? str : ""}`.replaceAll(joinChar[0], `\\${joinChar[0]}`)).join(joinChar);
/**
* Trims the value if it is a string. Otherwise returns the value as is.
*/
const trimIfString = (val) => typeof val === "string" ? val.trim() : val;
/**
* Splits a string by comma then trims each element. Arrays are returned as is except
* any string elements are trimmed.
*/
const toArray = (v, { retainEmptyStrings } = {}) => Array.isArray(v) ? v.map((v$1) => trimIfString(v$1)) : typeof v === "string" ? splitBy(v, defaultJoinChar).filter(retainEmptyStrings ? () => true : (s) => !/^\s*$/.test(s)).map((s) => s.trim()) : typeof v === "number" ? [v] : [];
/**
* Determines if an array is free of `null`/`undefined`.
*/
const nullFreeArray = (arr) => arr.every((el) => el === false || (el !== null && el !== void 0 ? el : false) !== false);
//#endregion
//#region src/utils/clsx.ts
// istanbul ignore next
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;
}
/**
* Vendored/adapted version of the `clsx` package.
*
* **NOTE:** Prefer the official package from npm outside the context of React Query Builder.
*/
// istanbul ignore next
function clsx(...args) {
let i = 0;
let tmp;
let x;
let str = "";
const len = args.length;
for (; i < len; i++) if ((tmp = args[i]) && (x = toVal(tmp))) {
str && (str += " ");
str += x;
}
return str;
}
//#endregion
//#region src/utils/misc.ts
/**
* Converts a value to lowercase if it's a string, otherwise returns the value as is.
*/
// istanbul ignore next
const lc = (v) => typeof v === "string" ? v.toLowerCase() : v;
/**
* Regex matching numeric strings. Passes for positive/negative integers, decimals,
* and E notation, with optional surrounding whitespace.
*/
const numericRegex = new RegExp(numericRegex$1.source.replace(/^\^/, String.raw`^\s*`).replace(/\$$/, String.raw`\s*$`));
/**
* Determines if a variable is a plain old JavaScript object, aka POJO.
*/
const isPojo = (obj) => obj === null || typeof obj !== "object" ? false : Object.getPrototypeOf(obj) === Object.prototype;
/**
* Simple helper to determine whether a value is null, undefined, or an empty string.
*/
const nullOrUndefinedOrEmpty = (value) => value === null || value === void 0 || value === "";
//#endregion
//#region src/utils/isRuleGroup.ts
/**
* Determines if an object is a {@link RuleType} (only checks for a `field` property).
*/
const isRuleType = (s) => isPojo(s) && "field" in s && typeof s.field === "string";
/**
* Determines if an object is a {@link RuleGroupType} or {@link RuleGroupTypeIC}.
*/
const isRuleGroup = (rg) => isPojo(rg) && Array.isArray(rg.rules);
/**
* Determines if an object is a {@link RuleGroupType}.
*/
const isRuleGroupType = (rg) => isRuleGroup(rg) && typeof rg.combinator === "string";
/**
* Determines if an object is a {@link RuleGroupTypeIC}.
*/
const isRuleGroupTypeIC = (rg) => isRuleGroup(rg) && rg.combinator === void 0;
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/typeof.js
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
return typeof o$1;
} : function(o$1) {
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
}, _typeof(o);
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/toPrimitive.js
function toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/toPropertyKey.js
function toPropertyKey(t) {
var i = toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/defineProperty.js
function _defineProperty(e, r, t) {
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/objectSpread2.js
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function(r$1) {
return Object.getOwnPropertyDescriptor(e, r$1).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function(r$1) {
_defineProperty(e, r$1, t[r$1]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r$1) {
Object.defineProperty(e, r$1, Object.getOwnPropertyDescriptor(t, r$1));
});
}
return e;
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/objectWithoutPropertiesLoose.js
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
if (e.includes(n)) continue;
t[n] = r[n];
}
return t;
}
//#endregion
//#region \0@oxc-project+runtime@0.95.0/helpers/objectWithoutProperties.js
function _objectWithoutProperties(e, t) {
if (null == e) return {};
var o, r, i = _objectWithoutPropertiesLoose(e, t);
if (Object.getOwnPropertySymbols) {
var s = Object.getOwnPropertySymbols(e);
for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
}
return i;
}
//#endregion
//#region src/utils/convertQuery.ts
const _excluded = ["combinator"];
const combinatorLevels = [
"or",
"xor",
"and"
];
const isSameString = (a, b) => lc(a) === b;
const generateRuleGroupICWithConsistentCombinators = (rg, baseCombinatorLevel = 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({ rules: draft.rules.slice(cursor) }, baseCombinatorLevel + 1));
break;
} else draft.rules.splice(cursor, nextBaseCombinatorIndex - cursor, generateRuleGroupICWithConsistentCombinators({ rules: draft.rules.slice(cursor, nextBaseCombinatorIndex) }, baseCombinatorLevel + 1));
}
});
};
/**
* Converts a {@link RuleGroupTypeIC} to {@link RuleGroupType}.
*
* This function is idempotent: {@link RuleGroupType} queries will be
* returned as-is.
*
* @group Query Tools
*/
const 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 _objectSpread2(_objectSpread2({}, processedRG), {}, {
combinator,
rules
});
};
/**
* Converts a {@link RuleGroupType} to {@link RuleGroupTypeIC}.
*
* This function is idempotent: {@link RuleGroupTypeIC} queries will be
* returned as-is.
*
* @group Query Tools
*/
const convertToIC = (rg) => {
if (isRuleGroupTypeIC(rg)) return rg;
const { combinator } = rg, queryWithoutCombinator = _objectWithoutProperties(rg, _excluded);
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 _objectSpread2(_objectSpread2({}, queryWithoutCombinator), {}, { rules });
};
function convertQuery(query) {
return isRuleGroupTypeIC(query) ? convertFromIC(query) : convertToIC(query);
}
//#endregion
//#region src/utils/defaultValidator.ts
/**
* This is an example validation function you can pass to {@link react-querybuilder!QueryBuilder QueryBuilder} in the
* `validator` prop. It assumes that you want to validate groups, and has a no-op
* for validating rules which you can replace with your own implementation.
*/
const defaultValidator = (query) => {
const result = {};
/**
* Replace this with your custom rule validator.
*/
const validateRule = (rule) => {
// istanbul ignore else
if (rule.id) result[rule.id];
};
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((c) => c.name).includes(rg.rules[i])) invalidICs = true;
if (invalidICs) reasons.push(groupInvalidReasons.invalidIndependentCombinators);
}
if (isRuleGroupType(rg) && !defaultCombinators.map((c) => c.name).includes(rg.combinator) && rg.rules.length > 1) reasons.push(groupInvalidReasons.invalidCombinator);
/* istanbul ignore else */
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;
};
//#endregion
//#region src/utils/objectUtils.ts
/**
* A strongly-typed version of `Object.keys()`.
*
* [Original source](https://github.com/sindresorhus/ts-extras/blob/44f57392c5f027268330771996c4fdf9260b22d6/source/object-keys.ts)
*/
const objectKeys = Object.keys;
/**
* A strongly-typed version of `Object.entries()`.
*
* [Original source](https://github.com/sindresorhus/ts-extras/blob/44f57392c5f027268330771996c4fdf9260b22d6/source/object-entries.ts)
*/
const objectEntries = Object.entries;
//#endregion
//#region src/utils/optGroupUtils.ts
const isOptionWithName = (opt) => isPojo(opt) && "name" in opt && typeof opt.name === "string";
const isOptionWithValue = (opt) => isPojo(opt) && "value" in opt && typeof opt.value === "string";
/**
* Converts an {@link Option} or {@link ValueOption} (i.e., {@link BaseOption})
* into a {@link FullOption}. Full options are left unchanged.
*
* @group Option Lists
*/
function toFullOption(opt, baseProperties, labelMap) {
return produce((draft) => {
const idObj = {};
let needsUpdating = !!baseProperties;
if (typeof draft === "string") {
var _labelMap$draft;
return _objectSpread2(_objectSpread2({}, baseProperties), {}, {
name: draft,
value: draft,
label: (_labelMap$draft = labelMap === null || labelMap === void 0 ? void 0 : labelMap[draft]) !== null && _labelMap$draft !== void 0 ? _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);
})(opt);
}
/**
* Converts an {@link OptionList} or {@link FlexibleOptionList} into a {@link FullOptionList}.
* Lists of full options are left unchanged.
*
* @group Option Lists
*/
function toFullOptionList(optList, baseProperties, labelMap) {
if (!Array.isArray(optList)) return [];
return produce((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);
})(optList);
}
/**
* Converts a {@link FlexibleOptionList} into a {@link FullOptionList}.
* Lists of full options are left unchanged.
*
* @group Option Lists
*/
function toFullOptionMap(optMap, baseProperties) {
return Object.fromEntries(Object.entries(optMap).map(([k, v]) => [k, toFullOption(v, baseProperties)]));
}
/**
* @deprecated Renamed to {@link uniqByIdentifier}.
*
* @group Option Lists
*/
const uniqByName = (originalArray) => uniqByIdentifier(originalArray);
/**
* Generates a new array of objects with duplicates removed based
* on the identifying property (`value` or `name`)
*
* @group Option Lists
*/
const uniqByIdentifier = (originalArray) => {
const names = /* @__PURE__ */ new Set();
const newArray = [];
for (const el of originalArray) {
var _el$value;
if (!names.has((_el$value = el.value) !== null && _el$value !== void 0 ? _el$value : el.name)) {
var _el$value2;
names.add((_el$value2 = el.value) !== null && _el$value2 !== void 0 ? _el$value2 : el.name);
newArray.push(el);
}
}
return originalArray.length === newArray.length ? originalArray : newArray;
};
/**
* Determines if an {@link OptionList} is an {@link OptionGroup} array.
*
* @group Option Lists
*/
const isOptionGroupArray = (arr) => Array.isArray(arr) && arr.length > 0 && isPojo(arr[0]) && "options" in arr[0] && Array.isArray(arr[0].options);
/**
* Determines if an array is a flat array of {@link FlexibleOption}.
*
* @group Option Lists
*/
const 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;
};
/**
* Determines if an array is a flat array of {@link FullOption}.
*
* @group Option Lists
*/
const 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;
};
/**
* Determines if a {@link FlexibleOptionList} is a {@link FlexibleOptionGroup} array.
*
* @group Option Lists
*/
const isFlexibleOptionGroupArray = (arr, { allowEmpty = false } = {}) => {
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;
};
/**
* Determines if a {@link FlexibleOptionList} is a {@link OptionGroup} array of {@link FullOption}.
*
* @group Option Lists
*/
const isFullOptionGroupArray = (arr, { allowEmpty = false } = {}) => {
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;
};
function getOption(arr, name) {
return (isFlexibleOptionGroupArray(arr, { allowEmpty: true }) ? arr.flatMap((og) => og.options) : arr).find((op) => op.value === name || op.name === name);
}
function getFirstOption(arr) {
var _arr$0$value;
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) {
var _og$options$0$value;
return (_og$options$0$value = og.options[0].value) !== null && _og$options$0$value !== void 0 ? _og$options$0$value : og.options[0].name;
}
// istanbul ignore next
return null;
}
return (_arr$0$value = arr[0].value) !== null && _arr$0$value !== void 0 ? _arr$0$value : arr[0].name;
}
/**
* Flattens {@link FlexibleOptionGroup} arrays into {@link BaseOption} arrays.
* If the array is already flat, it is returned as is.
*
* @group Option Lists
*/
const toFlatOptionArray = (arr) => uniqByIdentifier(isOptionGroupArray(arr) ? arr.flatMap((og) => og.options) : arr);
/**
* Generates a new {@link OptionGroup} array with duplicates
* removed based on the identifying property (`value` or `name`).
*
* @group Option Lists
*/
const 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) {
var _opt$value;
if (!names.has((_opt$value = opt.value) !== null && _opt$value !== void 0 ? _opt$value : opt.name)) {
var _opt$value2;
names.add((_opt$value2 = opt.value) !== null && _opt$value2 !== void 0 ? _opt$value2 : opt.name);
optionsForThisGroup.push(toFullOption(opt));
}
}
newArray.push(_objectSpread2(_objectSpread2({}, el), {}, { options: optionsForThisGroup }));
}
return newArray;
};
/**
* Generates a new {@link Option} or {@link OptionGroup} array with duplicates
* removed based on the identifier property (`value` or `name`).
*
* @group Option Lists
*/
const uniqOptList = (originalArray) => {
if (isFlexibleOptionGroupArray(originalArray)) return uniqOptGroups(originalArray);
return uniqByIdentifier(originalArray.map((o) => toFullOption(o)));
};
const prepareOptionList = (props) => {
// istanbul ignore next
const { optionList: optionListPropOriginal, baseOption = {}, labelMap = {}, placeholder: { placeholderName = defaultPlaceholderName, placeholderLabel = defaultPlaceholderLabel, placeholderGroupLabel = defaultPlaceholderLabel } = {}, autoSelectOption = true } = props;
const defaultOption = {
id: placeholderName,
name: placeholderName,
value: placeholderName,
label: placeholderLabel
};
const optionsProp = optionListPropOriginal !== null && optionListPropOriginal !== void 0 ? optionListPropOriginal : [defaultOption];
let optionList = [];
const opts = Array.isArray(optionsProp) ? toFullOptionList(optionsProp, baseOption, labelMap) : objectKeys(toFullOptionMap(optionsProp, baseOption)).map((opt) => _objectSpread2(_objectSpread2({}, optionsProp[opt]), {}, {
name: opt,
value: opt
})).sort((a, b) => a.label.localeCompare(b.label));
if (isFlexibleOptionGroupArray(opts)) optionList = autoSelectOption ? uniqOptGroups(opts) : uniqOptGroups([{
label: placeholderGroupLabel,
options: [defaultOption]
}, ...opts]);
else optionList = autoSelectOption ? uniqByIdentifier(opts) : uniqByIdentifier([defaultOption, ...opts]);
let optionsMap = {};
if (!Array.isArray(optionsProp)) {
const op = toFullOptionMap(optionsProp, baseOption);
optionsMap = autoSelectOption ? op : _objectSpread2(_objectSpread2({}, op), {}, { [placeholderName]: defaultOption });
} else if (isFlexibleOptionGroupArray(optionList)) for (const og of optionList) for (const opt of og.options) {
var _opt$value3;
optionsMap[(_opt$value3 = opt.value) !== null && _opt$value3 !== void 0 ? _opt$value3 : opt.name] = toFullOption(opt, baseOption);
}
else for (const opt of optionList) {
var _opt$value4;
optionsMap[(_opt$value4 = opt.value) !== null && _opt$value4 !== void 0 ? _opt$value4 : opt.name] = toFullOption(opt, baseOption);
}
return {
defaultOption,
optionList,
optionsMap
};
};
//#endregion
//#region src/utils/filterFieldsByComparator.ts
const filterByComparator = (field, operator, fieldToCompare) => {
var _fullField$comparator, _fullField$comparator2;
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 = (_fullField$comparator2 = fullField.comparator) === null || _fullField$comparator2 === void 0 ? void 0 : _fullField$comparator2.call(fullField, fullFieldToCompare, operator)) !== null && _fullField$comparator !== void 0 ? _fullField$comparator : false;
};
/**
* For a given {@link FullField}, returns the `fields` list filtered for
* other fields that match by `comparator`. Only fields *other than the
* one in question* will ever be included, even if `comparator` is `null`
* or `undefined`. If `comparator` is a string, fields with the same value
* for that property will be included. If `comparator` is a function, each
* field will be passed to the function along with the `operator` and fields
* for which the function returns `true` will be included.
*
* @group Option Lists
*/
const filterFieldsByComparator = (field, fields, operator) => {
if (!field.comparator) {
const filterOutSameField = (f) => {
var _f$value, _field$value;
return ((_f$value = f.value) !== null && _f$value !== void 0 ? _f$value : f.name) !== ((_field$value = field.value) !== null && _field$value !== void 0 ? _field$value : field.name);
};
if (isFlexibleOptionGroupArray(fields)) return fields.map((og) => _objectSpread2(_objectSpread2({}, og), {}, { options: og.options.filter((v) => filterOutSameField(v)) }));
return fields.filter((v) => filterOutSameField(v));
}
if (isFlexibleOptionGroupArray(fields)) return fields.map((og) => _objectSpread2(_objectSpread2({}, og), {}, { options: og.options.filter((f) => filterByComparator(field, operator, f)) })).filter((og) => og.options.length > 0);
return fields.filter((f) => filterByComparator(field, operator, f));
};
//#endregion
//#region src/utils/parseNumber.ts
/**
* Converts a string to a number. Uses native `parseFloat` if `parseNumbers` is "native",
* otherwise uses [`numeric-quantity`](https://jakeboone02.github.io/numeric-quantity/).
* If that returns `NaN`, the string is returned unchanged. Numeric values are returned
* as-is regardless of the `parseNumbers` option.
*/
const parseNumber = (val, { parseNumbers, bigIntOnOverflow } = {}) => {
if (!parseNumbers || typeof val === "bigint" || typeof val === "number") return val;
if (parseNumbers === "native") return Number.parseFloat(val);
const valAsNum = numericQuantity(val, {
allowTrailingInvalid: parseNumbers === "enhanced",
bigIntOnOverflow,
romanNumerals: false,
round: false
});
return typeof valAsNum === "bigint" || !Number.isNaN(valAsNum) ? valAsNum : val;
};
//#endregion
//#region src/utils/transformQuery.ts
const remapProperties = (obj, propertyMap, deleteRemappedProperties) => produce(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, options = {}) {
const { ruleProcessor = (r) => r, ruleGroupProcessor = (rg) => rg, propertyMap = {}, combinatorMap = {}, operatorMap = {}, omitPath = false, deleteRemappedProperties = true } = options;
const processGroup = (rg) => {
var _combinatorMap$rg$com, _propertyMap$rules;
return _objectSpread2(_objectSpread2({}, ruleGroupProcessor(remapProperties(_objectSpread2(_objectSpread2({}, rg), isRuleGroupType(rg) ? { combinator: (_combinatorMap$rg$com = combinatorMap[rg.combinator]) !== null && _combinatorMap$rg$com !== void 0 ? _combinatorMap$rg$com : rg.combinator } : {}), propertyMap, deleteRemappedProperties))), propertyMap["rules"] === false ? null : { [(_propertyMap$rules = propertyMap["rules"]) !== null && _propertyMap$rules !== void 0 ? _propertyMap$rules : "rules"]: rg.rules.map((r, idx) => {
var _operatorMap$r$operat;
const pathObject = omitPath ? null : { path: [...rg.path, idx] };
if (typeof r === "string") {
var _combinatorMap$r;
return (_combinatorMap$r = combinatorMap[r]) !== null && _combinatorMap$r !== void 0 ? _combinatorMap$r : r;
} else if (isRuleGroup(r)) return processGroup(_objectSpread2(_objectSpread2({}, r), pathObject));
return ruleProcessor(remapProperties(_objectSpread2(_objectSpread2(_objectSpread2({}, r), pathObject), "operator" in r ? { operator: (_operatorMap$r$operat = operatorMap[r.operator]) !== null && _operatorMap$r$operat !== void 0 ? _operatorMap$r$operat : r.operator } : {}), propertyMap, deleteRemappedProperties));
}) });
};
return processGroup(_objectSpread2(_objectSpread2({}, query), omitPath ? null : { path: [] }));
}
//#endregion
//#region src/utils/isRuleOrGroupValid.ts
/**
* Determines if an object is useful as a validation result.
*/
const isValidationResult = (vr) => isPojo(vr) && typeof vr.valid === "boolean";
/**
* Determines if a rule or group is valid based on a validation result (if defined)
* or a validator function. Returns `true` if neither are defined and the `muted`
* property is not `true`.
*/
const isRuleOrGroupValid = (rg, validationResult, validator) => {
if (rg.muted) return false;
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;
// istanbul ignore else
if (isValidationResult(vr)) return vr.valid;
}
return true;
};
//#endregion
//#region src/utils/getParseNumberMethod.ts
const getParseNumberMethod = ({ parseNumbers, inputType }) => {
if (typeof parseNumbers === "string") {
const [method, level] = parseNumbers.split("-");
if (level === "limited") return inputType === "number" ? method : false;
return method;
}
return parseNumbers ? "strict" : false;
};
//#endregion
//#region src/utils/formatQuery/utils.ts
/**
* Maps a {@link DefaultOperatorName} to a SQL operator.
*
* @group Export
*/
const 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;
}
};
/**
* Maps a (lowercase) {@link DefaultOperatorName} to a MongoDB operator.
*
* @group Export
*/
const mongoOperators = {
"=": "$eq",
"!=": "$ne",
"<": "$lt",
"<=": "$lte",
">": "$gt",
">=": "$gte",
in: "$in",
notin: "$nin",
notIn: "$nin"
};
/**
* Maps a (lowercase) {@link DefaultOperatorName} to a Prisma ORM operator.
*
* @group Export
*/
const prismaOperators = {
"=": "equals",
"!=": "not",
"<": "lt",
"<=": "lte",
">": "gt",
">=": "gte",
in: "in",
notin: "notIn"
};
/**
* Maps a {@link DefaultCombinatorName} to a CEL combinator.
*
* @group Export
*/
const celCombinatorMap = {
and: "&&",
or: "||"
};
/**
* Register these operators with `jsonLogic` before applying the result
* of `formatQuery(query, 'jsonlogic')`.
*
* @example
* ```
* for (const [op, func] of Object.entries(jsonLogicAdditionalOperators)) {
* jsonLogic.add_operation(op, func);
* }
* jsonLogic.apply({ "startsWith": [{ "var": "firstName" }, "Stev"] }, data);
* ```
*
* @group Export
*/
const jsonLogicAdditionalOperators = {
startsWith: (a, b) => typeof a === "string" && a.startsWith(b),
endsWith: (a, b) => typeof a === "string" && a.endsWith(b)
};
/**
* Converts all `string`-type `value` properties of a query object into `number` where appropriate.
*
* Used by {@link formatQuery} for the `json*` formats when `parseNumbers` is `true`.
*
* @group Export
*/
const numerifyValues = (rg, options) => _objectSpread2(_objectSpread2({}, rg), {}, { 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 === null || fieldData === void 0 ? void 0 : fieldData.inputType
});
if (Array.isArray(r.value)) return _objectSpread2(_objectSpread2({}, 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")) {
// istanbul ignore else
if (valAsArray.length > 1) return _objectSpread2(_objectSpread2({}, r), {}, { value: valAsArray });
else if (valAsArray.length === 1) return _objectSpread2(_objectSpread2({}, r), {}, { value: valAsArray[0] });
}
return r;
}) });
/**
* Determines whether a value is _anything_ except an empty `string` or `NaN`.
*
* @group Export
*/
const isValidValue = (value) => typeof value === "string" && value.length > 0 || typeof value === "number" && !Number.isNaN(value) || typeof value !== "string" && typeof value !== "number";
/**
* Determines whether {@link formatQuery} should render the given value as a number.
* As long as `parseNumbers` is `true`, `number` and `bigint` values will return `true` and
* `string` values will return `true` if they test positive against {@link numericRegex}.
*
* @group Export
*/
const shouldRenderAsNumber = (value, parseNumbers) => !!parseNumbers && (typeof value === "number" || typeof value === "bigint" || typeof value === "string" && numericRegex.test(value));
/**
* Used by {@link formatQuery} to determine whether the given value processor is a
* "legacy" value processor by counting the number of arguments. Legacy value
* processors take 3 arguments (not counting any arguments with default values), while
* rule-based value processors take no more than 2 arguments.
*
* @group Export
*/
const isValueProcessorLegacy = (valueProcessor) => valueProcessor.length >= 3;
/**
* Converts the `quoteFieldNamesWith` option into an array of two strings.
* If the option is a string, the array elements are both that string.
*
* @default
* ['', '']
*
* @group Export
*/
const getQuoteFieldNamesWithArray = (quoteFieldNamesWith = ["", ""]) => Array.isArray(quoteFieldNamesWith) ? quoteFieldNamesWith : typeof quoteFieldNamesWith === "string" ? [quoteFieldNamesWith, quoteFieldNamesWith] : quoteFieldNamesWith !== null && quoteFieldNamesWith !== void 0 ? quoteFieldNamesWith : ["", ""];
/**
* Given a field name and relevant {@link ValueProcessorOptions}, returns the field name
* wrapped in the configured quote character(s).
*
* @group Export
*/
const getQuotedFieldName = (fieldName, { quoteFieldNamesWith, fieldIdentifierSeparator }) => {
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}`;
};
const defaultWordOrder = [
"S",
"V",
"O"
];
/**
* Given a [Constituent word order](https://en.wikipedia.org/wiki/Word_order#Constituent_word_orders)
* like "svo" or "sov", returns a permutation of `["S", "V", "O"]` based on the first occurrence of
* each letter in the input string (case insensitive). This widens the valid input from abbreviations
* like "svo" to more expressive strings like "subject-verb-object" or "sub ver obj". Any missing
* letters are appended in the default order "SVO" (e.g., "object" would yield `["O", "S", "V"]`).
*
* @group Export
*/
const 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;
};
/**
* Default translations used by {@link formatQuery} for "natural_language" format.
*
* @group Export
*/
const defaultNLTranslations = {
groupPrefix: "",
groupPrefix_not_xor: "either zero or more than one of",
groupPrefix_xor: "exactly one of",
groupSuffix: "is true",
groupSuffix_not: "is not true"
};
/**
* Note: This function assumes `conditions.length > 0`
*/
const translationMatchFilter = (key, keyToTest, conditions) => keyToTest.startsWith(key) && conditions.every((c) => {
var _keyToTest$match;
return keyToTest.includes(`_${c}`) && ((_keyToTest$match = keyToTest.match(/_/g)) === null || _keyToTest$match === void 0 ? void 0 : _keyToTest$match.length) === conditions.length;
});
/**
* Used by {@link formatQuery} to get a translation based on certain conditions
* for the "natural_language" format.
*
* @group Export
*/
const getNLTranslataion = (key, translations, conditions = []) => {
var _ref, _translations$key, _ref2, _ref3, _Object$entries$find$, _Object$entries$find, _Object$entries$find2;
return conditions.length === 0 ? (_ref = (_translations$key = translations[key]) !== null && _translations$key !== void 0 ? _translations$key : defaultNLTranslations[key]) !== null && _ref !== void 0 ? _ref : "" : (_ref2 = (_ref3 = (_Object$entries$find$ = (_Object$entries$find = Object.entries(translations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))) === null || _Object$entries$find === void 0 ? void 0 : _Object$entries$find[1]) !== null && _Object$entries$find$ !== void 0 ? _Object$entries$find$ : (_Object$entries$find2 = Object.entries(defaultNLTranslations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))) === null || _Object$entries$find2 === void 0 ? void 0 : _Object$entries$find2[1]) !== null && _ref3 !== void 0 ? _ref3 : defaultNLTranslations[key]) !== null && _ref2 !== void 0 ? _ref2 : "";
};
const processMatchMode = (rule) => {
var _rule$match;
const { mode, threshold } = (_rule$match = rule.match) !== null && _rule$match !== void 0 ? _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
};
}
};
/**
* "Replacer" method for JSON.stringify's second argument. Converts `bigint` values to
* objects with a `$bigint` property having a value of a string representation of
* the actual `bigint`-type value.
*
* Inverse of {@link bigIntJsonParseReviver}.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
*/
const bigIntJsonStringifyReplacer = (_key, value) => typeof value === "bigint" ? { $bigint: value.toString() } : value;
/**
* "Reviver" method for JSON.parse's second argument. Converts objects having a single
* `$bigint: string` property to an actual `bigint` value.
*
* Inverse of {@link bigIntJsonStringifyReplacer}.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
*/
const bigIntJsonParseReviver = (_key, value) => isPojo(value) && Object.keys(value).length === 1 && typeof value.$bigint === "string" ? BigInt(value.$bigint) : value;
//#endregion
//#region src/utils/formatQuery/defaultRuleGroupProcessorCEL.ts
/**
* Rule group processor used by {@link formatQuery} for "cel" format.
*
* @group Export
*/
const defaultRuleGroupProcessorCEL = (ruleGroup, options) => {
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
const processRuleGroup = (rg, outermost) => {
var _rg$id;
if (!isRuleOrGroupValid(rg, validationMap[(_rg$id = rg.id) !== null && _rg$id !== void 0 ? _rg$id : ""])) return outermost ? fallbackExpression : "";
const processedRules = [];
let precedingCombinator = "";
let firstRule = true;
for (const rule of rg.rules) {
var _rule$valueSource;
if (typeof rule === "string") {
precedingCombinator = celCombinatorMap[rule];
continue;
}
if (isRuleGroup(rule)) {
const processedGroup = processRuleGroup(rule);
if (processedGroup) {
if (!firstRule && precedingCombinator) {
processedRules.push(precedingCombinator);
precedingCombinator = "";
}
firstRule = false;
processedRules.push(processedGroup);
}
continue;
}
const [validationResult, fieldValidator] = validateRule(rule);
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) continue;
const fieldData = getOption(fields, rule.field);
const processedRule = ruleProcessor(rule, _objectSpread2(_objectSpre