@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 lines • 369 kB
Source Map (JSON)
{"version":3,"file":"react-querybuilder_core.mjs","names":["defaultPlaceholderFieldName: typeof defaultPlaceholderName","defaultPlaceholderFieldLabel: typeof defaultPlaceholderLabel","defaultPlaceholderFieldGroupLabel: typeof defaultPlaceholderLabel","defaultPlaceholderOperatorName: typeof defaultPlaceholderName","defaultPlaceholderOperatorLabel: typeof defaultPlaceholderLabel","defaultPlaceholderOperatorGroupLabel: typeof defaultPlaceholderLabel","defaultPlaceholderValueName: typeof defaultPlaceholderName","defaultPlaceholderValueLabel: typeof defaultPlaceholderLabel","defaultPlaceholderValueGroupLabel: typeof defaultPlaceholderLabel","defaultTranslations: BaseTranslationsFull","defaultOperatorLabelMap: Record<DefaultOperatorName, string>","defaultCombinatorLabelMap: Record<DefaultCombinatorNameExtended, string>","defaultOperators: DefaultOperators","defaultOperatorNegationMap: Record<DefaultOperatorName, DefaultOperatorName>","defaultCombinators: DefaultCombinators","defaultCombinatorsExtended: DefaultCombinatorsExtended","defaultMatchModes: DefaultMatchModes","defaultControlClassnames: Classnames","rootPath: Path","queryBuilderFlagDefaults: Required<QueryBuilderFlags>","v","numericRegex: RegExp","numericQuantityRegex","rules: (RuleGroupTypeIC | RuleType | string)[]","defaultValidator: QueryValidator","result: ValidationMap","reasons: any[]","idObj: { name?: string; value?: string }","newArray: T[]","newArray: OptionGroup<ToFullOption<T>>[]","optionsForThisGroup: WithUnknownIndex<ToFullOption<T>>[]","optionList: O[] | OptionGroup<O>[]","optionsMap: Partial<FullOptionRecord<FullOption>>","valAsNum: number | bigint","celCombinatorMap: {\n and: '&&';\n or: '||';\n}","jsonLogicAdditionalOperators: Record<\n 'startsWith' | 'endsWith',\n (a: string, b: string) => boolean\n>","result: string[]","defaultNLTranslations: NLTranslations","defaultRuleGroupProcessorCEL: RuleGroupProcessor<string>","shouldNegate","defaultRuleProcessorCEL: RuleProcessor","defaultRuleGroupProcessorMongoDBQuery: RuleGroupProcessor","expressions: Record<string, unknown>[]","processNumber","defaultRuleProcessorMongoDBQuery: RuleProcessor","defaultRuleProcessorMongoDB: RuleProcessor","defaultRuleGroupProcessorSpEL: RuleGroupProcessor<string>","shouldNegate","negate","defaultRuleProcessorSpEL: RuleProcessor","escapeStringValueQuotes","defaultValueProcessorByRule: ValueProcessorByRule","defaultRuleProcessorDrizzle: RuleProcessor","defaultRuleGroupProcessorDrizzle: RuleGroupProcessor<\n (columns: Record<string, Column> | Table, drizzleOperators: Operators) => SQL | undefined\n>","ruleGroupSQL: SQL | undefined","defaultRuleGroupProcessorElasticSearch: RuleGroupProcessor<Record<string, unknown>>","defaultRuleGroupProcessorJSONata: RuleGroupProcessor<string>","defaultRuleGroupProcessorJsonLogic: RuleGroupProcessor<RQBJsonLogic>","jsonRuleGroup: RQBJsonLogic","defaultRuleGroupProcessorLDAP: RuleGroupProcessor<string>","rules: string[]","defaultRuleGroupProcessorMongoDB: RuleGroupProcessor<string>","expressions: string[]","defaultRuleGroupProcessorNL: RuleGroupProcessor<string>","defaultRuleGroupProcessorParameterized: RuleGroupProcessor<\n ParameterizedSQL | ParameterizedNamedSQL\n>","params: any[]","paramsNamed: Record<string, any>","fieldParams: Map<string, Set<string>>","defaultRuleGroupProcessorPrisma: RuleGroupProcessor<\n Record<string, unknown> | undefined\n>","expressions: Record<string, unknown>[]","defaultRuleGroupProcessorSequelize: RuleGroupProcessor<WhereOptions | undefined>","expressions: Record<string, unknown>[]","defaultRuleGroupProcessorSQL: RuleGroupProcessor<string>","negateIfNotOp","textFunctionMap: Partial<Record<Lowercase<DefaultOperatorName>, string>>","defaultRuleProcessorElasticSearch: RuleProcessor","negate","defaultRuleProcessorJSONata: RuleProcessor","defaultRuleProcessorJsonLogic: RuleProcessor","fieldObject: JsonLogicVar","negate","defaultRuleProcessorLDAP: RuleProcessor","defaultValueProcessorNL: ValueProcessorByRule","defaultExportOperatorMap: ExportOperatorMap","defaultOperatorProcessorNL: RuleProcessor","defaultRuleProcessorNL: RuleProcessor","defaultOperatorProcessorSQL: RuleProcessor","defaultRuleProcessorSQL: RuleProcessor","defaultRuleProcessorParameterized: RuleProcessor","params: any[]","paramsNamed: Record<string, any>","inParams: string[]","defaultRuleProcessorPrisma: RuleProcessor","defaultRuleProcessorSequelize: RuleProcessor","sqlDialectPresets: Record<SQLPreset, FormatQueryOptions>","formatQueryOptionPresets: Record<string, FormatQueryOptions>","defaultOperatorProcessor: RuleProcessor","defaultFallbackExpressions: Partial<Record<ExportFormat, string>>","optObj: MostFormatQueryOptions","valueProcessor: ValueProcessorByRule","getOperators: FormatQueryOptions['getOperators']","validationMap: ValidationMap","validatorMap: Record<string, RuleValidator>","validationResult: boolean | ValidationResult | undefined","fieldValidator: RuleValidator | undefined","finalOptions: FormatQueryFinalOptions","defaultValueProcessor: ValueProcessorLegacy","defaultMongoDBValueProcessor: ValueProcessorLegacy","defaultCELValueProcessor: ValueProcessorLegacy","defaultSpELValueProcessor: ValueProcessorLegacy","defaultValueProcessorCELByRule: RuleProcessor","defaultValueProcessorMongoDBByRule: RuleProcessor","defaultValueProcessorSpELByRule: RuleProcessor","target: FindPathReturnType","t: RuleGroupTypeAny | RuleType | string","commonAncestorPath: Path","target: RuleType | RuleGroupTypeAny","generateAccessibleDescription: ADG","dummyFD","matchModes: boolean | MatchMode[] | FlexibleOption<MatchMode>[]","defaultValueSourcesArray: ValueSourceFullOptions","valueSourcesNEW:\n | false\n | ValueSources\n | ValueSourceFlexibleOptions\n | ((operator: string) => ValueSources | ValueSourceFlexibleOptions)"],"sources":["../src/defaults.ts","../src/utils/arrayUtils.ts","../src/utils/clsx.ts","../src/utils/misc.ts","../src/utils/isRuleGroup.ts","../src/utils/convertQuery.ts","../src/utils/defaultValidator.ts","../src/utils/objectUtils.ts","../src/utils/optGroupUtils.ts","../src/utils/filterFieldsByComparator.ts","../src/utils/parseNumber.ts","../src/utils/transformQuery.ts","../src/utils/isRuleOrGroupValid.ts","../src/utils/getParseNumberMethod.ts","../src/utils/formatQuery/utils.ts","../src/utils/formatQuery/defaultRuleGroupProcessorCEL.ts","../src/utils/formatQuery/defaultRuleProcessorCEL.ts","../src/utils/formatQuery/defaultRuleGroupProcessorMongoDBQuery.ts","../src/utils/formatQuery/defaultRuleProcessorMongoDBQuery.ts","../src/utils/formatQuery/defaultRuleProcessorMongoDB.ts","../src/utils/formatQuery/defaultRuleGroupProcessorSpEL.ts","../src/utils/formatQuery/defaultRuleProcessorSpEL.ts","../src/utils/formatQuery/defaultValueProcessorByRule.ts","../src/utils/formatQuery/defaultRuleProcessorDrizzle.ts","../src/utils/formatQuery/defaultRuleGroupProcessorDrizzle.ts","../src/utils/formatQuery/defaultRuleGroupProcessorElasticSearch.ts","../src/utils/formatQuery/defaultRuleGroupProcessorJSONata.ts","../src/utils/formatQuery/defaultRuleGroupProcessorJsonLogic.ts","../src/utils/formatQuery/defaultRuleGroupProcessorLDAP.ts","../src/utils/formatQuery/defaultRuleGroupProcessorMongoDB.ts","../src/utils/formatQuery/defaultRuleGroupProcessorNL.ts","../src/utils/formatQuery/defaultRuleGroupProcessorParameterized.ts","../src/utils/formatQuery/defaultRuleGroupProcessorPrisma.ts","../src/utils/formatQuery/defaultRuleGroupProcessorSequelize.ts","../src/utils/formatQuery/defaultRuleGroupProcessorSQL.ts","../src/utils/formatQuery/defaultRuleProcessorElasticSearch.ts","../src/utils/formatQuery/defaultRuleProcessorJSONata.ts","../src/utils/formatQuery/defaultRuleProcessorJsonLogic.ts","../src/utils/formatQuery/defaultRuleProcessorLDAP.ts","../src/utils/formatQuery/defaultValueProcessorNL.ts","../src/utils/formatQuery/defaultRuleProcessorNL.ts","../src/utils/formatQuery/defaultRuleProcessorSQL.ts","../src/utils/formatQuery/defaultRuleProcessorParameterized.ts","../src/utils/formatQuery/defaultRuleProcessorPrisma.ts","../src/utils/formatQuery/defaultRuleProcessorSequelize.ts","../src/utils/formatQuery/formatQuery.ts","../src/utils/formatQuery/index.ts","../src/utils/pathUtils.ts","../src/utils/generateAccessibleDescription.ts","../src/utils/generateID.ts","../src/utils/getMatchModesUtil.ts","../src/utils/getValidationClassNames.ts","../src/utils/getValueSourcesUtil.ts","../src/utils/mergeAnyTranslations.ts","../src/utils/mergeClassnames.ts","../src/utils/preferProp.ts","../src/utils/prepareQueryObjects.ts","../src/utils/regenerateIDs.ts","../src/utils/queryTools.ts"],"sourcesContent":["import type {\n BaseTranslationsFull,\n Classnames,\n DefaultCombinatorName,\n DefaultCombinatorNameExtended,\n DefaultOperatorName,\n MatchMode,\n Path,\n QueryBuilderFlags,\n StringUnionToFullOptionArray,\n} from './types';\n\n// DO NOT ALTER OR REMOVE REGION NAMES. Some of them are used\n// to generate code snippets in the documentation.\n\n/**\n * @group Defaults\n */\nexport const defaultPlaceholderName = '~';\n/**\n * @group Defaults\n */\nexport const defaultPlaceholderLabel = '------';\n/**\n * Default `name` for placeholder option in the `fields` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderFieldName: typeof defaultPlaceholderName = defaultPlaceholderName;\n/**\n * Default `label` for placeholder option in the `fields` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderFieldLabel: typeof defaultPlaceholderLabel = defaultPlaceholderLabel;\n/**\n * Default `label` for placeholder option group in the `fields` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderFieldGroupLabel: typeof defaultPlaceholderLabel =\n defaultPlaceholderLabel;\n/**\n * Default `name` for placeholder option in the `operators` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderOperatorName: typeof defaultPlaceholderName = defaultPlaceholderName;\n/**\n * Default `label` for placeholder option in the `operators` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderOperatorLabel: typeof defaultPlaceholderLabel =\n defaultPlaceholderLabel;\n/**\n * Default `label` for placeholder option group in the `operators` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderOperatorGroupLabel: typeof defaultPlaceholderLabel =\n defaultPlaceholderLabel;\n/**\n * Default `name` for placeholder option in the `values` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderValueName: typeof defaultPlaceholderName = defaultPlaceholderName;\n/**\n * Default `label` for placeholder option in the `values` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderValueLabel: typeof defaultPlaceholderLabel = defaultPlaceholderLabel;\n/**\n * Default `label` for placeholder option group in the `values` array.\n *\n * @group Defaults\n */\nexport const defaultPlaceholderValueGroupLabel: typeof defaultPlaceholderLabel =\n defaultPlaceholderLabel;\n\n/**\n * Default configuration of translatable strings.\n *\n * @group Defaults\n */\n// #region docs-translations\nexport const defaultTranslations: BaseTranslationsFull = {\n fields: {\n title: 'Field',\n placeholderName: defaultPlaceholderFieldName,\n placeholderLabel: defaultPlaceholderFieldLabel,\n placeholderGroupLabel: defaultPlaceholderFieldGroupLabel,\n } as const,\n operators: {\n title: 'Operator',\n placeholderName: defaultPlaceholderOperatorName,\n placeholderLabel: defaultPlaceholderOperatorLabel,\n placeholderGroupLabel: defaultPlaceholderOperatorGroupLabel,\n } as const,\n values: {\n title: 'Values',\n placeholderName: defaultPlaceholderValueName,\n placeholderLabel: defaultPlaceholderValueLabel,\n placeholderGroupLabel: defaultPlaceholderValueGroupLabel,\n } as const,\n matchMode: { title: 'Match mode' } as const,\n matchThreshold: { title: 'Match threshold' } as const,\n value: { title: 'Value' } as const,\n removeRule: { label: '⨯', title: 'Remove rule' } as const,\n removeGroup: { label: '⨯', title: 'Remove group' } as const,\n addRule: { label: '+ Rule', title: 'Add rule' } as const,\n addGroup: { label: '+ Group', title: 'Add group' } as const,\n combinators: { title: 'Combinator' } as const,\n notToggle: { label: 'Not', title: 'Invert this group' } as const,\n cloneRule: { label: '⧉', title: 'Clone rule' } as const,\n cloneRuleGroup: { label: '⧉', title: 'Clone group' } as const,\n shiftActionUp: { label: '˄', title: 'Shift up' } as const,\n shiftActionDown: { label: '˅', title: 'Shift down' } as const,\n dragHandle: { label: '⁞⁞', title: 'Drag handle' } as const,\n lockRule: { label: '🔓', title: 'Lock rule' } as const,\n lockGroup: { label: '🔓', title: 'Lock group' } as const,\n lockRuleDisabled: { label: '🔒', title: 'Unlock rule' } as const,\n lockGroupDisabled: { label: '🔒', title: 'Unlock group' } as const,\n muteRule: { label: '🔊', title: 'Mute rule' } as const,\n muteGroup: { label: '🔊', title: 'Mute group' } as const,\n unmuteRule: { label: '🔇', title: 'Unmute rule' } as const,\n unmuteGroup: { label: '🔇', title: 'Unmute group' } as const,\n valueSourceSelector: { title: 'Value source' } as const,\n} satisfies BaseTranslationsFull;\n// #endregion\n\n/**\n * Default character used to `.join` and `.split` arrays.\n *\n * @group Defaults\n */\nexport const defaultJoinChar = ',';\n\nexport type DefaultOperators = StringUnionToFullOptionArray<DefaultOperatorName>;\n\nexport const defaultOperatorLabelMap: Record<DefaultOperatorName, string> = {\n '=': '=',\n '!=': '!=',\n '<': '<',\n '>': '>',\n '<=': '<=',\n '>=': '>=',\n contains: 'contains',\n beginsWith: 'begins with',\n endsWith: 'ends with',\n doesNotContain: 'does not contain',\n doesNotBeginWith: 'does not begin with',\n doesNotEndWith: 'does not end with',\n null: 'is null',\n notNull: 'is not null',\n in: 'in',\n notIn: 'not in',\n between: 'between',\n notBetween: 'not between',\n};\n\nexport const defaultCombinatorLabelMap: Record<DefaultCombinatorNameExtended, string> = {\n and: 'AND',\n or: 'OR',\n xor: 'XOR',\n};\n\n/**\n * Default operator list.\n *\n * @group Defaults\n */\n// #region docs-operators\nexport const defaultOperators: DefaultOperators = [\n { name: '=', value: '=', label: '=' },\n { name: '!=', value: '!=', label: '!=' },\n { name: '<', value: '<', label: '<' },\n { name: '>', value: '>', label: '>' },\n { name: '<=', value: '<=', label: '<=' },\n { name: '>=', value: '>=', label: '>=' },\n { name: 'contains', value: 'contains', label: 'contains' },\n { name: 'beginsWith', value: 'beginsWith', label: 'begins with' },\n { name: 'endsWith', value: 'endsWith', label: 'ends with' },\n { name: 'doesNotContain', value: 'doesNotContain', label: 'does not contain' },\n { name: 'doesNotBeginWith', value: 'doesNotBeginWith', label: 'does not begin with' },\n { name: 'doesNotEndWith', value: 'doesNotEndWith', label: 'does not end with' },\n { name: 'null', value: 'null', label: 'is null' },\n { name: 'notNull', value: 'notNull', label: 'is not null' },\n { name: 'in', value: 'in', label: 'in' },\n { name: 'notIn', value: 'notIn', label: 'not in' },\n { name: 'between', value: 'between', label: 'between' },\n { name: 'notBetween', value: 'notBetween', label: 'not between' },\n];\n// #endregion\n\n/**\n * Map of default operators to their respective opposite/negating operators.\n *\n * @group Defaults\n */\nexport const defaultOperatorNegationMap: Record<DefaultOperatorName, DefaultOperatorName> = {\n '=': '!=',\n '!=': '=',\n '<': '>=',\n '<=': '>',\n '>': '<=',\n '>=': '<',\n beginsWith: 'doesNotBeginWith',\n doesNotBeginWith: 'beginsWith',\n endsWith: 'doesNotEndWith',\n doesNotEndWith: 'endsWith',\n contains: 'doesNotContain',\n doesNotContain: 'contains',\n between: 'notBetween',\n notBetween: 'between',\n in: 'notIn',\n notIn: 'in',\n notNull: 'null',\n null: 'notNull',\n} satisfies Record<DefaultOperatorName, DefaultOperatorName>;\n\nexport type DefaultCombinators = StringUnionToFullOptionArray<DefaultCombinatorName>;\n\n/**\n * Default combinator list.\n *\n * @group Defaults\n */\n// #region docs-combinators\nexport const defaultCombinators: DefaultCombinators = [\n { name: 'and', value: 'and', label: 'AND' } as const,\n { name: 'or', value: 'or', label: 'OR' } as const,\n];\n// #endregion\n\nexport type DefaultCombinatorsExtended =\n StringUnionToFullOptionArray<DefaultCombinatorNameExtended>;\n\n/**\n * Default combinator list, with `XOR` added.\n *\n * @group Defaults\n */\nexport const defaultCombinatorsExtended: DefaultCombinatorsExtended = [\n ...defaultCombinators,\n { name: 'xor', value: 'xor', label: 'XOR' } as const,\n];\n\nexport type DefaultMatchModes = StringUnionToFullOptionArray<MatchMode>;\n\n/**\n * Default match modes.\n *\n * @group Defaults\n */\n// #region docs-matchmodes\nexport const defaultMatchModes: DefaultMatchModes = [\n { name: 'all', value: 'all', label: 'all' },\n { name: 'some', value: 'some', label: 'some' },\n { name: 'none', value: 'none', label: 'none' },\n { name: 'atLeast', value: 'atLeast', label: 'at least' },\n { name: 'atMost', value: 'atMost', label: 'at most' },\n { name: 'exactly', value: 'exactly', label: 'exactly' },\n];\n// #endregion\n\n/**\n * Standard classnames applied to each component.\n *\n * @group Defaults\n */\n// #region docs-standardclassnames\nexport const standardClassnames = {\n queryBuilder: 'queryBuilder',\n ruleGroup: 'ruleGroup',\n header: 'ruleGroup-header',\n body: 'ruleGroup-body',\n combinators: 'ruleGroup-combinators',\n addRule: 'ruleGroup-addRule',\n addGroup: 'ruleGroup-addGroup',\n cloneRule: 'rule-cloneRule',\n cloneGroup: 'ruleGroup-cloneGroup',\n removeGroup: 'ruleGroup-remove',\n notToggle: 'ruleGroup-notToggle',\n rule: 'rule',\n fields: 'rule-fields',\n matchMode: 'rule-matchMode',\n matchThreshold: 'rule-matchThreshold',\n operators: 'rule-operators',\n value: 'rule-value',\n removeRule: 'rule-remove',\n betweenRules: 'betweenRules',\n valid: 'queryBuilder-valid',\n invalid: 'queryBuilder-invalid',\n shiftActions: 'shiftActions',\n dndDragging: 'dndDragging',\n dndOver: 'dndOver',\n dndCopy: 'dndCopy',\n dndGroup: 'dndGroup',\n dndDropNotAllowed: 'dndDropNotAllowed',\n dragHandle: 'queryBuilder-dragHandle',\n disabled: 'queryBuilder-disabled',\n muted: 'queryBuilder-muted',\n lockRule: 'rule-lock',\n lockGroup: 'ruleGroup-lock',\n muteRule: 'rule-mute',\n muteGroup: 'ruleGroup-mute',\n valueSource: 'rule-valueSource',\n valueListItem: 'rule-value-list-item',\n branches: 'queryBuilder-branches',\n justified: 'queryBuilder-justified',\n hasSubQuery: 'rule-hasSubQuery',\n} as const;\n// #endregion\n\n/**\n * Default classnames for each component.\n *\n * @group Defaults\n */\nexport const defaultControlClassnames: Classnames = {\n queryBuilder: '',\n ruleGroup: '',\n header: '',\n body: '',\n combinators: '',\n addRule: '',\n addGroup: '',\n cloneRule: '',\n cloneGroup: '',\n removeGroup: '',\n notToggle: '',\n rule: '',\n fields: '',\n matchMode: '',\n matchThreshold: '',\n operators: '',\n value: '',\n removeRule: '',\n shiftActions: '',\n dragHandle: '',\n lockRule: '',\n lockGroup: '',\n muteRule: '',\n muteGroup: '',\n muted: '',\n valueSource: '',\n actionElement: '',\n valueSelector: '',\n betweenRules: '',\n valid: '',\n invalid: '',\n dndDragging: '',\n dndOver: '',\n dndGroup: '',\n dndCopy: '',\n dndDropNotAllowed: '',\n disabled: '',\n valueListItem: '',\n branches: '',\n hasSubQuery: '',\n} satisfies Classnames;\n\n/**\n * Default reason codes for a group being invalid.\n *\n * @group Defaults\n */\nexport const groupInvalidReasons = {\n empty: 'empty',\n invalidCombinator: 'invalid combinator',\n invalidIndependentCombinators: 'invalid independent combinators',\n} as const;\n\n/**\n * Component identifiers for testing.\n *\n * @group Defaults\n */\nexport const TestID = {\n rule: 'rule',\n ruleGroup: 'rule-group',\n inlineCombinator: 'inline-combinator',\n addGroup: 'add-group',\n removeGroup: 'remove-group',\n cloneGroup: 'clone-group',\n cloneRule: 'clone-rule',\n addRule: 'add-rule',\n removeRule: 'remove-rule',\n combinators: 'combinators',\n fields: 'fields',\n operators: 'operators',\n valueEditor: 'value-editor',\n notToggle: 'not-toggle',\n shiftActions: 'shift-actions',\n dragHandle: 'drag-handle',\n lockRule: 'lock-rule',\n lockGroup: 'lock-group',\n muteRule: 'mute-rule',\n muteGroup: 'mute-group',\n valueSourceSelector: 'value-source-selector',\n matchModeEditor: 'match-mode-editor',\n} as const;\n\nexport const LogType = {\n parentPathDisabled: 'action aborted: parent path disabled',\n pathDisabled: 'action aborted: path is disabled',\n queryUpdate: 'query updated',\n onAddRuleFalse: 'onAddRule callback returned false',\n onAddGroupFalse: 'onAddGroup callback returned false',\n onGroupRuleFalse: 'onGroupRule callback returned false',\n onGroupGroupFalse: 'onGroupGroup callback returned false',\n onMoveRuleFalse: 'onMoveRule callback returned false',\n onMoveGroupFalse: 'onMoveGroup callback returned false',\n onRemoveFalse: 'onRemove callback returned false',\n add: 'rule or group added',\n remove: 'rule or group removed',\n update: 'rule or group updated',\n move: 'rule or group moved',\n group: 'rule or group grouped with another',\n} as const;\n\n/**\n * The {@link Path} of the root group.\n *\n * @group Defaults\n */\nexport const rootPath: Path = [] satisfies Path;\n\n/**\n * Default values for all `boolean`\n * {@link react-querybuilder!QueryBuilder QueryBuilder} options.\n *\n * @group Defaults\n */\nexport const queryBuilderFlagDefaults: Required<QueryBuilderFlags> = {\n addRuleToNewGroups: false,\n autoSelectField: true,\n autoSelectOperator: true,\n autoSelectValue: false,\n debugMode: false,\n enableDragAndDrop: false,\n enableMountQueryChange: true,\n listsAsArrays: false,\n resetOnFieldChange: true,\n resetOnOperatorChange: false,\n showCloneButtons: false,\n showCombinatorsBetweenRules: false,\n showLockButtons: false,\n showMuteButtons: false,\n showNotToggle: false,\n showShiftActions: false,\n suppressStandardClassnames: false,\n};\n","import { defaultJoinChar } from '../defaults';\n\n/**\n * Splits a string by a given character (see {@link defaultJoinChar}). Escaped characters\n * (characters preceded by a backslash) will not apply to the split, and the backslash will\n * be removed in the array element. Inverse of {@link joinWith}.\n *\n * @example\n * splitBy('this\\\\,\\\\,that,,the other,,,\\\\,')\n * // or\n * splitBy('this\\\\,\\\\,that,,the other,,,\\\\,', ',')\n * // would return\n * ['this,,that', '', 'the other', '', '', ',']\n */\nexport const splitBy = (str?: string, splitChar: string = defaultJoinChar): string[] =>\n typeof str === 'string'\n ? str\n .split(`\\\\${splitChar}`)\n .map(c => c.split(splitChar))\n .reduce((prev, curr, idx) => {\n if (idx === 0) {\n return curr;\n }\n return [...prev.slice(0, -1), `${prev.at(-1)}${splitChar}${curr[0]}`, ...curr.slice(1)];\n }, [])\n : [];\n\n/**\n * Joins an array of strings using the given character (see {@link defaultJoinChar}). When\n * the given character appears in an array element, a backslash will be added just before it\n * to distinguish it from the join character. Effectively the inverse of {@link splitBy}.\n *\n * TIP: The join character can actually be a string of any length. Only the first character\n * will be searched for in the array elements and preceded by a backslash.\n *\n * @example\n * joinWith(['this,,that', '', 'the other', '', '', ','], ', ')\n * // would return\n * 'this\\\\,\\\\,that, , the other, , , \\\\,'\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const joinWith = (strArr: any[], joinChar: string = defaultJoinChar): string =>\n strArr.map(str => `${str ?? ''}`.replaceAll(joinChar[0], `\\\\${joinChar[0]}`)).join(joinChar);\n\n/**\n * Trims the value if it is a string. Otherwise returns the value as is.\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const trimIfString = (val: any): any => (typeof val === 'string' ? val.trim() : val);\n\n/**\n * Splits a string by comma then trims each element. Arrays are returned as is except\n * any string elements are trimmed.\n */\nexport const toArray = (\n // oxlint-disable-next-line typescript/no-explicit-any\n v: any,\n { retainEmptyStrings }: { retainEmptyStrings?: boolean } = {}\n // oxlint-disable-next-line typescript/no-explicit-any\n): any[] =>\n Array.isArray(v)\n ? v.map(v => trimIfString(v))\n : typeof v === 'string'\n ? splitBy(v, defaultJoinChar)\n .filter(retainEmptyStrings ? () => true : s => !/^\\s*$/.test(s))\n .map(s => s.trim())\n : typeof v === 'number'\n ? [v]\n : [];\n\n/**\n * Determines if an array is free of `null`/`undefined`.\n */\nexport const nullFreeArray = <T>(arr: T[]): arr is Exclude<T, null>[] =>\n arr.every(el => el === false || (el ?? false) !== false);\n","// Adapted from https://github.com/lukeed/clsx/tree/925494cf31bcd97d3337aacd34e659e80cae7fe2\n\n// oxlint-disable-next-line typescript/no-explicit-any\ntype ClassDictionary = Record<string, any>;\ntype ClassValue =\n | ClassArray\n | ClassDictionary\n | string\n | number\n | bigint\n | null\n | boolean\n | undefined;\ntype ClassArray = ClassValue[];\n\n// istanbul ignore next\n// oxlint-disable-next-line typescript/no-explicit-any\nfunction toVal(mix: any) {\n let k;\n let y;\n let str = '';\n\n if (typeof mix === 'string' || typeof mix === 'number') {\n str += mix;\n } else if (typeof mix === 'object') {\n if (Array.isArray(mix)) {\n const len = mix.length;\n for (k = 0; k < len; k++) {\n if (mix[k] && (y = toVal(mix[k]))) {\n // oxlint-disable-next-line no-unused-expressions\n str && (str += ' ');\n str += y;\n }\n }\n } else {\n for (y in mix) {\n if (mix[y]) {\n // oxlint-disable-next-line no-unused-expressions\n str && (str += ' ');\n str += y;\n }\n }\n }\n }\n\n return str;\n}\n\n/**\n * Vendored/adapted version of the `clsx` package.\n *\n * **NOTE:** Prefer the official package from npm outside the context of React Query Builder.\n */\n// istanbul ignore next\nexport function clsx(...args: ClassValue[]): string {\n let i = 0;\n let tmp;\n let x;\n let str = '';\n const len = args.length;\n for (; i < len; i++) {\n if ((tmp = args[i]) && (x = toVal(tmp))) {\n // oxlint-disable-next-line no-unused-expressions\n str && (str += ' ');\n str += x;\n }\n }\n return str;\n}\n\nexport default clsx;\n","import { numericRegex as numericQuantityRegex } from 'numeric-quantity';\n\n/**\n * Converts a value to lowercase if it's a string, otherwise returns the value as is.\n */\n// istanbul ignore next\nexport const lc = <T>(v: T): T => (typeof v === 'string' ? (v.toLowerCase() as T) : v);\n\n/**\n * Regex matching numeric strings. Passes for positive/negative integers, decimals,\n * and E notation, with optional surrounding whitespace.\n */\nexport const numericRegex: RegExp = new RegExp(\n numericQuantityRegex.source.replace(/^\\^/, String.raw`^\\s*`).replace(/\\$$/, String.raw`\\s*$`)\n);\n\n/**\n * Determines if a variable is a plain old JavaScript object, aka POJO.\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const isPojo = (obj: any): obj is Record<string, any> =>\n obj === null || typeof obj !== 'object' ? false : Object.getPrototypeOf(obj) === Object.prototype;\n\n/**\n * Simple helper to determine whether a value is null, undefined, or an empty string.\n */\nexport const nullOrUndefinedOrEmpty = (value: unknown): value is null | undefined | '' =>\n value === null || value === undefined || value === '';\n","import type { RuleGroupType, RuleGroupTypeAny, RuleGroupTypeIC, RuleType } from '../types';\nimport { isPojo } from './misc';\n\n/**\n * Determines if an object is a {@link RuleType} (only checks for a `field` property).\n */\nexport const isRuleType = (s: unknown): s is RuleType =>\n isPojo(s) && 'field' in s && typeof s.field === 'string';\n\n/**\n * Determines if an object is a {@link RuleGroupType} or {@link RuleGroupTypeIC}.\n */\nexport const isRuleGroup = (rg: unknown): rg is RuleGroupTypeAny =>\n isPojo(rg) && Array.isArray(rg.rules);\n\n/**\n * Determines if an object is a {@link RuleGroupType}.\n */\nexport const isRuleGroupType = (rg: unknown): rg is RuleGroupType =>\n isRuleGroup(rg) && typeof rg.combinator === 'string';\n\n/**\n * Determines if an object is a {@link RuleGroupTypeIC}.\n */\nexport const isRuleGroupTypeIC = (rg: unknown): rg is RuleGroupTypeIC =>\n isRuleGroup(rg) && rg.combinator === undefined;\n","import { produce } from 'immer';\nimport type {\n RuleGroupArray,\n RuleGroupType,\n RuleGroupTypeAny,\n RuleGroupTypeIC,\n RuleType,\n} from '../types';\nimport { isRuleGroup, isRuleGroupType, isRuleGroupTypeIC } from './isRuleGroup';\nimport { lc } from './misc';\n\nconst combinatorLevels = ['or', 'xor', 'and'] as const;\n\nconst isSameString = (a: unknown, b: string) => lc(a) === b;\n\nconst generateRuleGroupICWithConsistentCombinators = (\n rg: RuleGroupTypeIC,\n baseCombinatorLevel: number = 0\n): RuleGroupTypeIC => {\n const baseCombinator = combinatorLevels[baseCombinatorLevel];\n\n // oxlint-disable-next-line typescript/no-explicit-any\n if (!rg.rules.includes(baseCombinator as any)) {\n // No instances of this combinator, so group based on the next\n // combinator level if at least two levels remain\n return baseCombinatorLevel < combinatorLevels.length - 2\n ? generateRuleGroupICWithConsistentCombinators(rg, baseCombinatorLevel + 1)\n : rg;\n }\n\n return produce(rg, draft => {\n let cursor = 0;\n\n // Group all chains of combinators in the rule array that are not the base combinator\n while (cursor < draft.rules.length - 2) {\n if (isSameString(draft.rules[cursor + 1], baseCombinator)) {\n cursor += 2;\n continue;\n }\n\n const nextBaseCombinatorIndex = draft.rules.findIndex(\n (r, i) => i > cursor && typeof r === 'string' && lc(r) === baseCombinator\n );\n\n if (nextBaseCombinatorIndex === -1) {\n // No more instances of this combinator, so group all remaining rules and exit the loop\n draft.rules.splice(\n cursor,\n draft.rules.length,\n generateRuleGroupICWithConsistentCombinators(\n // oxlint-disable-next-line typescript/no-explicit-any\n { rules: draft.rules.slice(cursor) as any },\n baseCombinatorLevel + 1\n )\n );\n break;\n } else {\n // Group all rules between the current cursor and the next instance of the base combinator\n draft.rules.splice(\n cursor,\n nextBaseCombinatorIndex - cursor,\n generateRuleGroupICWithConsistentCombinators(\n // oxlint-disable-next-line typescript/no-explicit-any\n { rules: draft.rules.slice(cursor, nextBaseCombinatorIndex) as any },\n baseCombinatorLevel + 1\n )\n );\n }\n }\n });\n};\n\n/**\n * Converts a {@link RuleGroupTypeIC} to {@link RuleGroupType}.\n *\n * This function is idempotent: {@link RuleGroupType} queries will be\n * returned as-is.\n *\n * @group Query Tools\n */\nexport const convertFromIC = <RG extends RuleGroupType = RuleGroupType>(\n rg: RuleGroupTypeAny\n): RG => {\n if (isRuleGroupType(rg)) {\n return rg as RG;\n }\n const processedRG = generateRuleGroupICWithConsistentCombinators(rg);\n const rulesAsMixedList = processedRG.rules.map(r =>\n typeof r === 'string' || !isRuleGroup(r) ? r : convertFromIC(r)\n );\n const combinator = rulesAsMixedList.length < 2 ? 'and' : (rulesAsMixedList[1] as string);\n const rules = rulesAsMixedList.filter(r => typeof r !== 'string') as RuleGroupArray;\n return { ...processedRG, combinator, rules } as RG;\n};\n\n/**\n * Converts a {@link RuleGroupType} to {@link RuleGroupTypeIC}.\n *\n * This function is idempotent: {@link RuleGroupTypeIC} queries will be\n * returned as-is.\n *\n * @group Query Tools\n */\nexport const convertToIC = <RGIC extends RuleGroupTypeIC = RuleGroupTypeIC>(\n rg: RuleGroupTypeAny\n): RGIC => {\n if (isRuleGroupTypeIC(rg)) {\n return rg as RGIC;\n }\n const { combinator, ...queryWithoutCombinator } = rg;\n const rules: (RuleGroupTypeIC | RuleType | string)[] = [];\n const { length } = rg.rules;\n for (const [idx, r] of rg.rules.entries()) {\n if (isRuleGroup(r)) {\n rules.push(convertToIC(r));\n } else {\n rules.push(r);\n }\n if (combinator && idx < length - 1) {\n rules.push(combinator);\n }\n }\n return { ...queryWithoutCombinator, rules } as RGIC;\n};\n\n/**\n * Converts a {@link RuleGroupType} to {@link RuleGroupTypeIC}. For a more explicit\n * operation, use {@link convertToIC}.\n *\n * @group Query Tools\n */\nfunction convertQuery(query: RuleGroupType): RuleGroupTypeIC;\n/**\n * Converts a {@link RuleGroupTypeIC} to {@link RuleGroupType}. For a more explicit\n * operation, use {@link convertFromIC}.\n *\n * @group Query Tools\n */\nfunction convertQuery(query: RuleGroupTypeIC): RuleGroupType;\nfunction convertQuery(query: RuleGroupType | RuleGroupTypeIC): RuleGroupType | RuleGroupTypeIC {\n return isRuleGroupTypeIC(query) ? convertFromIC(query) : convertToIC(query);\n}\n\nexport { convertQuery };\n","import { defaultCombinators, groupInvalidReasons } from '../defaults';\nimport type { QueryValidator, RuleGroupTypeAny, RuleType, ValidationMap } from '../types';\nimport { isRuleGroup, isRuleGroupType } from './isRuleGroup';\n\n/**\n * This is an example validation function you can pass to {@link react-querybuilder!QueryBuilder QueryBuilder} in the\n * `validator` prop. It assumes that you want to validate groups, and has a no-op\n * for validating rules which you can replace with your own implementation.\n */\nexport const defaultValidator: QueryValidator = query => {\n const result: ValidationMap = {};\n\n /**\n * Replace this with your custom rule validator.\n */\n const validateRule = (rule: RuleType) => {\n // Set `result[rule.id] = true` for a valid rule, or either\n // `{ valid: false, reasons: ['whatever', 'reasons', 'here'] }`\n // or simply `false` for an invalid rule.\n // istanbul ignore else\n // oxlint-disable-next-line no-unused-expressions\n if (rule.id) result[rule.id]; // = true;\n };\n\n const validateGroup = (rg: RuleGroupTypeAny) => {\n // oxlint-disable-next-line typescript/no-explicit-any\n const reasons: any[] = [];\n if (rg.rules.length === 0) {\n reasons.push(groupInvalidReasons.empty);\n } else if (!isRuleGroupType(rg)) {\n // Odd indexes should be valid combinators and even indexes should be rules or groups\n let invalidICs = false;\n for (let i = 0; i < rg.rules.length && !invalidICs; i++) {\n if (\n (i % 2 === 0 && typeof rg.rules[i] === 'string') ||\n (i % 2 === 1 && typeof rg.rules[i] !== 'string') ||\n (i % 2 === 1 &&\n typeof rg.rules[i] === 'string' &&\n !defaultCombinators.map(c => c.name as string).includes(rg.rules[i] as string))\n ) {\n invalidICs = true;\n }\n }\n if (invalidICs) {\n reasons.push(groupInvalidReasons.invalidIndependentCombinators);\n }\n }\n // Non-independent combinators should be valid, but only checked if there are multiple rules\n // since combinators don't really apply to groups with only one rule/group\n if (\n isRuleGroupType(rg) &&\n !defaultCombinators.map(c => c.name as string).includes(rg.combinator) &&\n rg.rules.length > 1\n ) {\n reasons.push(groupInvalidReasons.invalidCombinator);\n }\n /* istanbul ignore else */\n if (rg.id) {\n result[rg.id] = reasons.length > 0 ? { valid: false, reasons } : true;\n }\n for (const r of rg.rules) {\n if (typeof r === 'string') {\n // Validation for this case was done earlier\n } else if (isRuleGroup(r)) {\n validateGroup(r);\n } else {\n validateRule(r);\n }\n }\n };\n\n validateGroup(query);\n\n return result;\n // You can return the result object itself like above, or if you just\n // want the entire query to be marked invalid if _any_ rules/groups are\n // invalid, return a boolean like this:\n // return Object.values(result).map(rv => (typeof rv !== 'boolean')).includes(true);\n // That will return `true` if no errors were found.\n};\n","// All code in this file is adapted from:\n// npm: https://www.npmjs.com/package/ts-extras\n// src: https://github.com/sindresorhus/ts-extras\n\n/**\n * Original looked like this (not sure why template string is used):\n * ```\n * type ObjectKeys<T extends object> = `${Exclude<keyof T, symbol>}`;\n * ```\n */\ntype ObjectKeys<T extends object> = Exclude<keyof T, symbol>;\n\n/**\n * A strongly-typed version of `Object.keys()`.\n *\n * [Original source](https://github.com/sindresorhus/ts-extras/blob/44f57392c5f027268330771996c4fdf9260b22d6/source/object-keys.ts)\n */\nexport const objectKeys = Object.keys as <Type extends object>(\n value: Type\n) => Array<ObjectKeys<Type>>;\n\n/**\n * A strongly-typed version of `Object.entries()`.\n *\n * [Original source](https://github.com/sindresorhus/ts-extras/blob/44f57392c5f027268330771996c4fdf9260b22d6/source/object-entries.ts)\n */\nexport const objectEntries = Object.entries as <Type extends Record<PropertyKey, unknown>>(\n value: Type\n) => Array<[ObjectKeys<Type>, Type[ObjectKeys<Type>]]>;\n","import type { Draft } from 'immer';\nimport { produce } from 'immer';\nimport type { RequireAtLeastOne } from 'type-fest';\nimport { defaultPlaceholderLabel, defaultPlaceholderName } from '../defaults';\nimport type {\n BaseOption,\n BaseOptionMap,\n FlexibleOption,\n FlexibleOptionGroup,\n FlexibleOptionList,\n FlexibleOptionListProp,\n FullOption,\n FullOptionList,\n FullOptionMap,\n FullOptionRecord,\n GetOptionIdentifierType,\n Option,\n OptionGroup,\n Placeholder,\n ToFullOption,\n ValueOption,\n WithUnknownIndex,\n} from '../types';\nimport { isPojo } from './misc';\nimport { objectKeys } from './objectUtils';\n\nconst isOptionWithName = (opt: BaseOption): opt is Option =>\n isPojo(opt) && 'name' in opt && typeof opt.name === 'string';\nconst isOptionWithValue = (opt: BaseOption): opt is ValueOption =>\n isPojo(opt) && 'value' in opt && typeof opt.value === 'string';\n\n/**\n * Converts an {@link Option} or {@link ValueOption} (i.e., {@link BaseOption})\n * into a {@link FullOption}. Full options are left unchanged.\n *\n * @group Option Lists\n */\nexport function toFullOption<Opt extends BaseOption>(\n opt: Opt | string,\n baseProperties?: Record<string, unknown>,\n labelMap?: Record<string, unknown>\n): ToFullOption<Opt> {\n const recipe: (o: Opt | string) => ToFullOption<Opt> = produce(draft => {\n const idObj: { name?: string; value?: string } = {};\n let needsUpdating = !!baseProperties;\n\n if (typeof draft === 'string') {\n return {\n ...baseProperties,\n name: draft,\n value: draft,\n label: labelMap?.[draft] ?? draft,\n } as Draft<Opt>;\n }\n\n if (isOptionWithName(draft) && !isOptionWithValue(draft)) {\n idObj.value = draft.name;\n needsUpdating = true;\n } else if (!isOptionWithName(draft) && isOptionWithValue(draft)) {\n idObj.name = draft.value;\n needsUpdating = true;\n }\n\n if (needsUpdating) {\n return Object.assign({}, baseProperties, draft, idObj);\n }\n });\n return recipe(opt);\n}\n\n/**\n * Converts an {@link OptionList} or {@link FlexibleOptionList} into a {@link FullOptionList}.\n * Lists of full options are left unchanged.\n *\n * @group Option Lists\n */\nexport function toFullOptionList<Opt extends BaseOption>(\n optList: unknown[],\n baseProperties?: Record<string, unknown>,\n labelMap?: Record<string, unknown>\n): FullOptionList<Opt> {\n if (!Array.isArray(optList)) {\n return [] as unknown as FullOptionList<Opt>;\n }\n\n const recipe: (ol: FlexibleOptionList<Opt>) => FullOptionList<Opt> = produce(draft => {\n if (isFlexibleOptionGroupArray(draft)) {\n for (const optGroup of draft) {\n for (const [idx, opt] of optGroup.options.entries())\n optGroup.options[idx] = toFullOption(opt, baseProperties, labelMap);\n }\n } else {\n for (const [idx, opt] of (draft as Opt[]).entries())\n draft[idx] = toFullOption(opt, baseProperties, labelMap);\n }\n });\n\n return recipe(optList as FlexibleOptionList<Opt>);\n}\n\n/**\n * Converts a {@link FlexibleOptionList} into a {@link FullOptionList}.\n * Lists of full options are left unchanged.\n *\n * @group Option Lists\n */\nexport function toFullOptionMap<OptMap extends BaseOptionMap>(\n optMap: OptMap,\n baseProperties?: Record<string, unknown>\n): OptMap extends BaseOptionMap<infer V, infer K> ? Partial<Record<K, ToFullOption<V>>> : never {\n type FullOptMapType =\n OptMap extends BaseOptionMap<infer VT, infer KT>\n ? Partial<Record<KT, ToFullOption<VT>>>\n : never;\n\n return Object.fromEntries(\n (Object.entries(optMap) as [string, FlexibleOption][]).map(([k, v]) => [\n k,\n toFullOption(v, baseProperties),\n ])\n ) as FullOptMapType;\n}\n\n/**\n * @deprecated Renamed to {@link uniqByIdentifier}.\n *\n * @group Option Lists\n */\nexport const uniqByName = <\n T extends { name: string; value?: string } | { name?: string; value: string },\n>(\n originalArray: T[]\n): T[] => uniqByIdentifier(originalArray);\n\n/**\n * Generates a new array of objects with duplicates removed based\n * on the identifying property (`value` or `name`)\n *\n * @group Option Lists\n */\nexport const uniqByIdentifier = <\n T extends RequireAtLeastOne<{ name: string; value: string }, 'name' | 'value'>,\n>(\n originalArray: T[]\n): T[] => {\n const names = new Set<string>();\n const newArray: T[] = [];\n for (const el of originalArray) {\n if (!names.has((el.value ?? el.name)!)) {\n names.add((el.value ?? el.name)!);\n newArray.push(el);\n }\n }\n return originalArray.length === newArray.length ? originalArray : newArray;\n};\n\n/**\n * Determines if an {@link OptionList} is an {@link OptionGroup} array.\n *\n * @group Option Lists\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const isOptionGroupArray = (arr: any): arr is OptionGroup<BaseOption>[] =>\n Array.isArray(arr) &&\n arr.length > 0 &&\n isPojo(arr[0]) &&\n 'options' in arr[0] &&\n Array.isArray(arr[0].options);\n\n/**\n * Determines if an array is a flat array of {@link FlexibleOption}.\n *\n * @group Option Lists\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const isFlexibleOptionArray = (arr: any): arr is FlexibleOption[] => {\n let isFOA = false;\n if (Array.isArray(arr)) {\n for (const o of arr) {\n if (isOptionWithName(o) || isOptionWithValue(o)) {\n isFOA = true;\n } else {\n return false;\n }\n }\n }\n return isFOA;\n};\n\n/**\n * Determines if an array is a flat array of {@link FullOption}.\n *\n * @group Option Lists\n */\n// oxlint-disable-next-line typescript/no-explicit-any\nexport const isFullOptionArray = (arr: any): arr is FullOption[] => {\n let isFOA = false;\n if (Array.isArray(arr)) {\n for (const o of arr) {\n if (isOptionWithName(o) && isOptionWithValue(o)) {\n isFOA = true;\n } else {\n return false;\n }\n }\n }\n return isFOA;\n};\n\n/**\n * Determines if a {@link FlexibleOptionList} is a {@link FlexibleOptionGroup} array.\n *\n * @group Option Lists\n */\nexport const isFlexibleOptionGroupArray = (\n // oxlint-disable-next-line typescript/no-explicit-any\n arr: any,\n { allowEmpty = false }: { allowEmpty?: boolean } = {}\n): arr is FlexibleOptionGroup[] => {\n let isFOGA = false;\n if (Array.isArray(arr)) {\n for (const og of arr) {\n if (\n isPojo(og) &&\n 'options' in og &&\n (isFlexibleOptionArray(og.options) ||\n (allowEmpty && Array.isArray(og.options) && og.options.length === 0))\n ) {\n isFOGA = true;\n } else {\n return false;\n }\n }\n }\n return isFOGA;\n};\n\n/**\n * Determines if a {@link FlexibleOptionList} is a {@link OptionGroup} array of {@link FullOption}.\n *\n * @group Option Lists\n */\nexport const isFullOptionGroupArray = (\n // oxlint-disable-next-line typescript/no-explicit-any\n arr: any,\n { allowEmpty = false }: { allowEmpty?: boolean } = {}\n): arr is OptionGroup<FullOption>[] => {\n let isFOGA = false;\n if (Array.isArray(arr)) {\n for (const og of arr) {\n if (\n isPojo(og) &&\n 'options' in og &&\n (isFullOptionArray(og.options) ||\n (allowEmpty && Array.isArray(og.options) && og.options.length === 0))\n ) {\n isFOGA = true;\n } else {\n return false;\n }\n }\n }\n return isFOGA;\n};\n\n/**\n * Gets the option from an {@link OptionList} with the given `name`. Handles\n * {@link Option} arrays as well as {@link OptionGroup} arrays.\n *\n * @group Option Lists\n */\nexport function getOption<OptType extends FullOption>(\n arr: FullOptionList<OptType>,\n name: string\n): OptType | undefined;\nexport function getOption<OptType extends ValueOption>(\n arr: FlexibleOptionList<OptType>,\n name: string\n): OptType | undefined;\nexport function getOption<OptType extends Option>(\n arr: FlexibleOptionList<OptType>,\n name: string\n): OptType | undefined;\nexport function getOption<OptType extends BaseOption>(\n arr: FlexibleOptionList<OptType>,\n name: string\n): OptType | undefined {\n const options = isFlexibleOptionGroupArray(arr, { allowEmpty: true })\n ? arr.flatMap(og => og.options)\n : arr;\n return options.find(op => op.value === name || op.name === name) as OptType | undefined;\n}\n\n/**\n * Gets the first option from an {@link OptionList}.\n *\n * @group Option Lists\n */\nexport function getFirstOption<Opt extends FullOption>(\n arr?: OptionGroup<Opt>[] | Opt[]\n): GetOptionIdentifierType<Opt> | null;\nexport function getFirstOption<Opt extends ValueOption>(\n arr?: OptionGroup<Opt>[] | Opt[]\n): GetOptionIdentifierType<Opt> | null;\nexport function getFirstOption<Opt extends Option>(\n arr?: OptionGroup<Opt>[] | Opt[]\n): GetOptionIdentifierType<Opt> | null;\nexport function getFirstOption<Opt extends BaseOption>(\n arr?: FlexibleOptionGroup<Opt>[] | Opt[]\n): GetOptionIdentifierType<Opt> | null {\n if (!Array.isArray(arr) || arr.length === 0) {\n return null;\n } else if (isFlexibleOptionGroupArray(arr, { allowEmpty: true })) {\n for (const og of arr) {\n if (og.options.length > 0) {\n return (og.options[0].value ?? og.options[0].name) as GetOptionIdentifierType<Opt>;\n }\n }\n // istanbul ignore next\n return null;\n }\n\n return (arr[0].value ?? arr[0].name) as GetOptionIdentifierType<Opt>;\n}\n\n/**\n * Flattens {@link FlexibleOptionGroup} arrays into {@link BaseOption} arrays.\n * If the array is already flat, it is returned as is.\n *\n * @group Option Lists\n */\nexport const toFlatOptionArray = <T extends FullOption, OL extends FullOptionList<T>>(arr: OL) =>\n uniqByIdentifier(isOptionGroupArray(arr) ? arr.flatMap(og => og.options) : arr) as T[];\n\n/**\n * Generates a new {@link OptionGroup} array with duplicates\n * removed based on the identifying property (`value` or `name`).\n *\n * @group Option Lists\n */\nexport const uniqOptGroups = <T extends BaseOption>(\n originalArray: FlexibleOptionGroup<T>[]\n): OptionGroup<ToFullOption<T>>[] => {\n type K = T extends BaseOption<infer KT> ? KT : never;\n const labels = new Set<string>();\n const names = new Set<K>();\n const newArray: OptionGroup<ToFullOption<T>>[] = [];\n for (const el of originalArray) {\n if (!labels.has(el.label)) {\n labels.add(el.label);\n const optionsForThisGroup: WithUnknownIndex<ToFullOption<T>>[] = [];\n for (const opt of el.options) {\n if (!names.has((opt.value ?? opt.name) as K)) {\n names.add((opt.value ?? opt.name) as K);\n optionsForThisGroup.push(toFullOption(opt) as WithUnknownIndex<ToFullOption<T>>);\n }\n }\n newArray.push({ ...el, options: optionsForThisGroup });\n }\n }\n return newArray;\n};\n\n/**\n * Generates a new {@link Option} or {@link OptionGroup} array with duplicates\n * removed based on the identifier property (`value` or `name`).\n *\n * @group Option Lists\n */\nexport const uniqOptList = <T extends BaseOption>(\n originalArray: FlexibleOptionList<T>\n): WithUnknownIndex<BaseOption & FullOption>[] | OptionGroup<ToFullOption<T>>[] => {\n if (isFlexibleOptionGroupArray(originalArray)) {\n return uniqOptGroups(originalArray) as OptionGroup<ToFullOption<T>>[];\n }\n return uniqByIdentifier((originalArray as BaseOption[]).map(o => toFullOption(o)));\n};\n\nexport interface PreparedOptionList<O extends FullOption> {\n defaultOption: FullOption;\n optionList: O[] | OptionGroup<O>[];\n optionsMap: Partial<FullOptionRecord<FullOption>>;\n}\n\nexport interface PrepareOptionListParams<O extends FullOption> {\n placeholder?: Placeholder;\n optionList?: FlexibleOptionListProp<O> | BaseOptionMap<O>;\n baseOption?: Record<string, unknown>;\n labelMap?: Record<string, string>;\n autoSelectOption?: boolean;\n}\n\nexport const prepareOptionList =