const require_chunk = require('./chunk-U64pC571.js');
const require_isRuleGroup = require('./isRuleGroup-Cjk1Q2mj.js');
let numeric_quantity = require("numeric-quantity");
let immer = require("immer");

//#region src/defaults.ts
/**
* @group Defaults
*/
const defaultPlaceholderName = "~";
/**
* Default `name` for placeholder option in the `fields` array.
*
* @group Defaults
*/
const defaultPlaceholderFieldName = defaultPlaceholderName;
/**
* Default `name` for placeholder option in the `operators` array.
*
* @group Defaults
*/
const defaultPlaceholderOperatorName = defaultPlaceholderName;
/**
* Default character used to `.join` and `.split` arrays.
*
* @group Defaults
*/
const defaultJoinChar = ",";
/**
* 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"
}];

//#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 ?? ""}`.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] : [];

//#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 = (0, numeric_quantity.numericQuantity)(val, {
		allowTrailingInvalid: parseNumbers === "enhanced",
		bigIntOnOverflow,
		romanNumerals: false,
		round: false
	});
	return typeof valAsNum === "bigint" || !Number.isNaN(valAsNum) ? valAsNum : val;
};

//#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;

//#endregion
//#region src/utils/optGroupUtils.ts
const isOptionWithName = (opt) => require_isRuleGroup.isPojo(opt) && "name" in opt && typeof opt.name === "string";
const isOptionWithValue = (opt) => require_isRuleGroup.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 (0, immer.produce)((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);
	})(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 (0, immer.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);
}
/**
* 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) if (!names.has(el.value ?? el.name)) {
		names.add(el.value ?? 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 && require_isRuleGroup.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 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 (require_isRuleGroup.isPojo(og) && "options" in og && (isFlexibleOptionArray(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);
}
/**
* 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);

//#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 (require_isRuleGroup.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) => ({
	...rg,
	rules: rg.rules.map((r) => {
		if (typeof r === "string") return r;
		if (require_isRuleGroup.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")) {
			// istanbul ignore else
			if (valAsArray.length > 1) return {
				...r,
				value: valAsArray
			};
			else if (valAsArray.length === 1) return {
				...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" && require_isRuleGroup.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 ?? ["", ""];
/**
* 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) => keyToTest.includes(`_${c}`) && keyToTest.match(/_/g)?.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 = []) => conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ?? "" : Object.entries(translations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? Object.entries(defaultNLTranslations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? defaultNLTranslations[key] ?? "";
/**
* Transforms
* - `match: { mode: "atLeast", threshold: 1 }` to `match: { mode: "some" }`
* - `match: { mode: "atMost", threshold: 0 }` to `match: { mode: "none" }`.
*
* Returns:
* - Processed `{ mode, threshold }` object for valid subqueries
* - `null` if match mode is not applicable for the rule
* - `false` if match mode is valid, but either
*   1. `threshold` is required and invalid, or
*   2. `value` is not a valid rule group.
*/
const processMatchMode = (rule) => {
	const { mode, threshold } = rule.match ?? {};
	if (!mode) return null;
	if (!require_isRuleGroup.isRuleGroup(rule.value)) return false;
	const matchModeLC = require_isRuleGroup.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) => require_isRuleGroup.isPojo(value) && Object.keys(value).length === 1 && typeof value.$bigint === "string" ? BigInt(value.$bigint) : value;

//#endregion
Object.defineProperty(exports, 'bigIntJsonParseReviver', {
  enumerable: true,
  get: function () {
    return bigIntJsonParseReviver;
  }
});
Object.defineProperty(exports, 'bigIntJsonStringifyReplacer', {
  enumerable: true,
  get: function () {
    return bigIntJsonStringifyReplacer;
  }
});
Object.defineProperty(exports, 'celCombinatorMap', {
  enumerable: true,
  get: function () {
    return celCombinatorMap;
  }
});
Object.defineProperty(exports, 'defaultNLTranslations', {
  enumerable: true,
  get: function () {
    return defaultNLTranslations;
  }
});
Object.defineProperty(exports, 'defaultOperatorNegationMap', {
  enumerable: true,
  get: function () {
    return defaultOperatorNegationMap;
  }
});
Object.defineProperty(exports, 'defaultPlaceholderFieldName', {
  enumerable: true,
  get: function () {
    return defaultPlaceholderFieldName;
  }
});
Object.defineProperty(exports, 'defaultPlaceholderOperatorName', {
  enumerable: true,
  get: function () {
    return defaultPlaceholderOperatorName;
  }
});
Object.defineProperty(exports, 'getNLTranslataion', {
  enumerable: true,
  get: function () {
    return getNLTranslataion;
  }
});
Object.defineProperty(exports, 'getOption', {
  enumerable: true,
  get: function () {
    return getOption;
  }
});
Object.defineProperty(exports, 'getParseNumberMethod', {
  enumerable: true,
  get: function () {
    return getParseNumberMethod;
  }
});
Object.defineProperty(exports, 'getQuoteFieldNamesWithArray', {
  enumerable: true,
  get: function () {
    return getQuoteFieldNamesWithArray;
  }
});
Object.defineProperty(exports, 'getQuotedFieldName', {
  enumerable: true,
  get: function () {
    return getQuotedFieldName;
  }
});
Object.defineProperty(exports, 'isFlexibleOptionArray', {
  enumerable: true,
  get: function () {
    return isFlexibleOptionArray;
  }
});
Object.defineProperty(exports, 'isFlexibleOptionGroupArray', {
  enumerable: true,
  get: function () {
    return isFlexibleOptionGroupArray;
  }
});
Object.defineProperty(exports, 'isValidValue', {
  enumerable: true,
  get: function () {
    return isValidValue;
  }
});
Object.defineProperty(exports, 'isValueProcessorLegacy', {
  enumerable: true,
  get: function () {
    return isValueProcessorLegacy;
  }
});
Object.defineProperty(exports, 'joinWith', {
  enumerable: true,
  get: function () {
    return joinWith;
  }
});
Object.defineProperty(exports, 'jsonLogicAdditionalOperators', {
  enumerable: true,
  get: function () {
    return jsonLogicAdditionalOperators;
  }
});
Object.defineProperty(exports, 'mapSQLOperator', {
  enumerable: true,
  get: function () {
    return mapSQLOperator;
  }
});
Object.defineProperty(exports, 'mongoOperators', {
  enumerable: true,
  get: function () {
    return mongoOperators;
  }
});
Object.defineProperty(exports, 'normalizeConstituentWordOrder', {
  enumerable: true,
  get: function () {
    return normalizeConstituentWordOrder;
  }
});
Object.defineProperty(exports, 'numerifyValues', {
  enumerable: true,
  get: function () {
    return numerifyValues;
  }
});
Object.defineProperty(exports, 'objectKeys', {
  enumerable: true,
  get: function () {
    return objectKeys;
  }
});
Object.defineProperty(exports, 'parseNumber', {
  enumerable: true,
  get: function () {
    return parseNumber;
  }
});
Object.defineProperty(exports, 'prismaOperators', {
  enumerable: true,
  get: function () {
    return prismaOperators;
  }
});
Object.defineProperty(exports, 'processMatchMode', {
  enumerable: true,
  get: function () {
    return processMatchMode;
  }
});
Object.defineProperty(exports, 'shouldRenderAsNumber', {
  enumerable: true,
  get: function () {
    return shouldRenderAsNumber;
  }
});
Object.defineProperty(exports, 'toArray', {
  enumerable: true,
  get: function () {
    return toArray;
  }
});
Object.defineProperty(exports, 'toFlatOptionArray', {
  enumerable: true,
  get: function () {
    return toFlatOptionArray;
  }
});
Object.defineProperty(exports, 'toFullOption', {
  enumerable: true,
  get: function () {
    return toFullOption;
  }
});
Object.defineProperty(exports, 'toFullOptionList', {
  enumerable: true,
  get: function () {
    return toFullOptionList;
  }
});
Object.defineProperty(exports, 'trimIfString', {
  enumerable: true,
  get: function () {
    return trimIfString;
  }
});
//# sourceMappingURL=utils-CR1ToTMW.js.map