import { type CursorParams, type ResolvedCursor } from './cursor.js';
import { type QueryBuilderLike } from './lucid_adapter.js';
import type { FilterConfig, FilterInput } from './types.js';
/** The resolved offset pagination to hand to Lucid's `query.paginate(page, size)`. */
export interface ResolvedPagination {
    page: number;
    size: number;
}
/**
 * Resolve aliases, structurally validate, and prune `input.filters`/`input.search`
 * against the allow-lists, applying the survivors to the builder. Shared by both
 * {@link applyFilter} (offset) and {@link applyCursor} (keyset) so the security
 * boundary is identical for either pagination style.
 */
export declare function applyFilterConditions(qb: QueryBuilderLike, input: FilterInput, config: FilterConfig): void;
/**
 * Resolve the alias-mapped, allow-listed distinct fields for the request. A
 * distinct field is a projected column, so it is gated by the SAME `allowed`
 * boundary a `where` field is — aliases resolve first, then the allow-list;
 * unknown fields are dropped (or rejected under `throwOnInvalid`).
 *
 * A field that clears the allow-list but is not projectable
 * ({@link isProjectable}) is dropped/rejected too, with its OWN message: it is
 * not an allow-list problem, so pointing the reader at `allowed` would send them
 * to edit the wrong thing.
 */
export declare function resolveSafeDistinct(fields: string[], config: FilterConfig): string[];
/**
 * Apply a parsed {@link FilterInput} to a Lucid query builder under a
 * {@link FilterConfig} policy, and return the resolved offset pagination.
 *
 * The allow-lists are the security boundary — fields not in `allowed`/`sortable`/
 * `searchable` are dropped (or rejected with `throwOnInvalid`). Pagination is
 * returned (not applied) so the caller drives Lucid's `query.paginate()`:
 *
 * ```ts
 * const { page, size } = applyFilter(Users.query(), input, { allowed: ['name', 'age'] })
 * const result = await Users.query()... // or: await query.paginate(page, size)
 * ```
 */
export declare function applyFilter(qb: QueryBuilderLike, input: FilterInput, config: FilterConfig): ResolvedPagination;
/** Per-call policy for {@link applyCursor} — a {@link FilterConfig} plus the keyset tiebreaker. */
export interface CursorConfig extends FilterConfig {
    /** Primary-key column appended to the keyset as a stable tiebreaker. Default `'id'`. */
    primaryKey?: string;
}
/**
 * Apply a parsed {@link FilterInput} plus {@link CursorParams} to a Lucid query
 * builder as a **keyset (cursor) page** under a {@link CursorConfig} policy.
 *
 * Filters/search go through the same allow-list boundary as {@link applyFilter}.
 * The effective (allow-listed) sort, plus the primary-key tiebreaker, forms the
 * keyset; a supplied `after`/`before` cursor becomes a row-value seek predicate,
 * and the builder is ordered + limited to `size + 1` (one extra row to detect a
 * further page). Feed the fetched rows and the returned {@link ResolvedCursor}
 * to {@link buildCursorPage} to assemble the page and its boundary cursors:
 *
 * ```ts
 * const resolved = applyCursor(Users.query(), input, { allowed: ['name'], primaryKey: 'id' })
 * const rows = await Users.query()...exec()
 * const page = buildCursorPage(rows, resolved)
 * ```
 */
export declare function applyCursor(qb: QueryBuilderLike, input: FilterInput & CursorParams, config: CursorConfig): ResolvedCursor;
//# sourceMappingURL=runner.d.ts.map