import type { FilterSpec } from './filter_spec.js';
/**
 * Codegen for the typed client — the AdonisJS-idiomatic port of the NestJS
 * `nestjs-filter` codegen. Where the NestJS package ships a standalone
 * `reflect-metadata` bin that reads decorator metadata off entity classes and
 * emits `filterQuery: () => filterQueryTyped<Fields, TypeMap>()` members into a
 * generated `api.ts`, the Adonis port has no decorators: the filter declaration
 * is already a plain runtime {@link FilterSpec} produced by `defineFilter(...)`.
 *
 * So generation is a **pure function of the spec** — string in, string out, no
 * AST walk, no metadata reflection, no framework. The ace command
 * (`make:filter-client`) is the thin IO wrapper that loads the app's declared
 * specs and writes the emitted module to disk. Keeping the generator pure keeps
 * it trivially testable (snapshot/string assertions) and framework-free, exactly
 * like the rest of this core.
 *
 * The emitted module targets `@adonis-agora/filter-client`'s `filterQueryTyped<Fields,
 * FieldTypes>()` factory — the same client the NestJS codegen emitted against —
 * so the two ecosystems share one browser runtime.
 */
import type { FilterFieldKind } from './types.js';
export type { FilterFieldKind };
/**
 * Optional per-field type information. A {@link FilterSpec} carries the
 * allow-list but not column value types (Lucid models aren't reflected here), so
 * the caller supplies types to unlock the client's type-aware operator narrowing.
 * Without it the emitted client is still field-name-safe, just operator-permissive.
 */
export interface FilterFieldTypeInfo {
    /** Classified value kind. Ignored when {@link typeRef} or {@link enumValues} is set. */
    kind?: FilterFieldKind;
    /** Enum literal members — emitted as a union (`"A" | "B"` or `1 | 2`). Wins over {@link kind}. */
    enumValues?: readonly (string | number)[];
    /** A named TS type (e.g. `'Role'`) — emitted verbatim. Wins over everything. */
    typeRef?: string;
    /** Nullable column — appends `| null` to the emitted type. */
    nullable?: boolean;
}
/** Options for {@link generateFilterClient}. */
export interface GenerateFilterClientOptions {
    /**
     * Base name for the emitted identifiers — e.g. `'people'` yields
     * `PeopleFilterFields`, `PeopleFilterFieldTypes`, `peopleFilterMeta` and
     * `peopleFilterQuery()`. Non-identifier characters are stripped.
     */
    name: string;
    /** Per-field value types keyed by (dotted) field path. Unlocks operator narrowing. */
    fieldTypes?: Record<string, FilterFieldTypeInfo>;
    /** Import specifier for `filterQueryTyped`. Default `'@adonis-agora/filter-client'`. */
    clientModule?: string;
    /** Cap relation-path depth of the emitted field union. Default {@link FilterSpec.maxDepth}. */
    maxDepth?: number;
    /** Emit the "generated — do not edit" banner. Default `true`. */
    banner?: boolean;
}
/** Enumerate the filterable field paths a spec admits, capped at `maxDepth` relation hops. */
export declare function filterableFieldPaths(spec: FilterSpec, maxDepth?: number): string[];
/** Enumerate the sortable field paths a spec admits, capped at `maxDepth` relation hops. */
export declare function sortableFieldPaths(spec: FilterSpec, maxDepth?: number): string[];
/**
 * Generate a typed filter client module for one {@link FilterSpec} — a pure
 * string transform. The emitted module exports, for a spec named `people`:
 *
 * - `type PeopleFilterFields` — the union of filterable field paths (the security
 *   boundary, as concrete string literals);
 * - `interface PeopleFilterFieldTypes` — the per-field value-type map (only when
 *   `fieldTypes` is supplied), which drives the client's operator/value narrowing;
 * - `const peopleFilterMeta` — runtime metadata (filterable/sortable/searchable
 *   fields, whitelisted relations, per-field kinds, and the default sort / page
 *   size / max size + cursor keyset the runner paginates by);
 * - `function peopleFilterQuery()` — a `filterQueryTyped<Fields, FieldTypes>()`
 *   factory returning a type-safe builder scoped to this spec.
 */
export declare function generateFilterClient(spec: FilterSpec, options: GenerateFilterClientOptions): string;
/**
 * One entry in a {@link FilterClientManifest}: a declared spec plus the optional
 * type info / codegen overrides the emitter needs. This is what an app exports
 * from `config/filter.ts` (as `filters`) for the `make:filter-client` command to
 * consume — the specs are already `defineFilter(...)` results, so no reflection.
 */
export interface FilterClientEntry {
    /** The `defineFilter(...)` result to generate a client for. */
    spec: FilterSpec;
    /** Per-field value types (see {@link GenerateFilterClientOptions.fieldTypes}). */
    fieldTypes?: Record<string, FilterFieldTypeInfo>;
    /** Override the client import specifier for this entry. */
    clientModule?: string;
    /** Override the relation-path depth cap for this entry. */
    maxDepth?: number;
}
/** A map of client name → declared spec, keyed by the emitted client's base name. */
export type FilterClientManifest = Record<string, FilterClientEntry>;
/** A single generated client: its name, its AdonisJS-conventional file name, and its code. */
export interface GeneratedFilterClient {
    name: string;
    /** `snake_case`d file name, e.g. `blog_post_filter_client.ts`. */
    filename: string;
    code: string;
}
/**
 * Expand a whole {@link FilterClientManifest} into per-spec generated modules —
 * the pure core the `make:filter-client` ace command writes to disk. Kept pure
 * (manifest in, modules out) so the command stays a thin IO wrapper and the
 * expansion is itself testable without a running app.
 */
export declare function generateFilterClients(manifest: FilterClientManifest): GeneratedFilterClient[];
//# sourceMappingURL=generate_client.d.ts.map