UNPKG

@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
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