export type ExcelFilterOperator = 'contains' | 'notContains' | 'equals' | 'notEquals' | 'startsWith' | 'endsWith' | 'regex' | 'in' | 'notIn' | 'greaterThan' | 'lessThan' | 'between' | 'isBlank' | 'isNotBlank'; export type ExcelFilter = { id: string; operator: ExcelFilterOperator; value?: unknown; valueTo?: unknown; }; export type ExcelFilterOptions = { /** * BCP-47 locale tag (or fallback list) for accent- and case- * insensitive text comparison. When set, "café", "Café" and "CAFÉ" * all match "cafe" without strain. * * Implementation: NFD-decompose both sides, strip combining marks * (diacritics), then locale-aware lowercase. This is the standard * "lowercase + asciifolding" pipeline used by Elasticsearch, Postgres * (with `unaccent`), and Lucene. Cheap, deterministic, no Collator * round-trip. */ locale?: string | ReadonlyArray; }; /** NFD-decompose, strip combining marks (diacritics), then locale-aware * lowercase. The locale-aware lowercasing handles Turkish dotted-I / * dotless-i correctly when the consumer threads `"tr"` through. */ export declare function normalizeForFilter(s: string, locale?: string | ReadonlyArray): string; /** * Delimiter used to serialise the `in` / `notIn` value list into the single * `value` string that the filter model carries. * * This has to be single-line safe: the tool panel and the filter menu show the * serialised list in a plain ``, and the HTML input value * sanitiser STRIPS newlines - so a newline-joined list came back out of the DOM * as one run-together token. `splitInTokens` accepts newlines too, so values * serialised by older builds still parse. */ export declare const IN_TOKEN_SEP = ", "; /** * Split a serialised `in` / `notIn` value into its individual tokens. * * The grammar is CSV-shaped: comma OR newline separates, and a token may be * double-quoted to carry a separator literally (`""` escapes a quote inside * one). Quoting is what makes a comma separator safe - facet values commonly * contain commas of their own, because numeric bucket labels are built with * `toLocaleString` ("1,234 - 5,678") and date labels with a short month * ("Aug 17, 2026"). A quote only opens a token when it is the token's first * non-space character, so an unquoted `5" pipe` stays literal. */ export declare function splitInTokens(value: unknown): string[]; /** * Serialise a list of tokens back into the single `in` / `notIn` value, * quoting any token that carries a separator or a quote of its own. */ export declare function joinInTokens(tokens: ReadonlyArray): string; /** * The token still being typed at the end of an `in` / `notIn` value - the text * after the last separator. `'AAPL, MS'` -> `'MS'`; a value ending in a * separator (or empty) has no trailing token. * * Inputs that hold the whole token list use this to drive the value-suggestion * dropdown: the trailing fragment is the search query, not a committed token. */ export declare function trailingInToken(value: unknown): string; export { ALL_FILTER_OPERATORS, SET_OPERATOR_IDS, VALUELESS_OPERATOR_IDS, RANGE_OPERATOR_IDS, isSetOperator, isValuelessOperator, isRangeOperator, type FilterValueType, } from './filter-operator-catalogue.js'; /** A filter with its needle-side work already done. Call per row. */ export type CompiledExcelFilter = (cellValue: unknown) => boolean; /** * Compile a filter once, then test many rows against it. * * Everything that depends only on the FILTER - folding the needle, splitting * `in` tokens, building the regex, coercing range endpoints - happens here, * so the per-row closure does the minimum. Filtering 100k rows used to redo * all of it 100k times. * * `applyExcelFilter` is defined in terms of this, so there is exactly one * copy of the operator semantics. */ export declare function compileExcelFilter(filter: ExcelFilter, options?: ExcelFilterOptions): CompiledExcelFilter; export declare function applyExcelFilter(cellValue: unknown, filter: ExcelFilter, options?: ExcelFilterOptions): boolean;