@adonis-agora/filter
Version:
Server-side query filtering/sorting/pagination for AdonisJS — Spatie-style input, a Lucid adapter, and field allow-listing. Part of the Agora ecosystem.
65 lines • 3.45 kB
TypeScript
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