import { ModelType } from '../lib/engine'; /** * Typed filter inputs for generated list queries. * * Generated list queries used to inherit an opaque `filters` blob (`GraphQLJSONObject`) from the * template's `CorePagingInput`, which reached Prisma's `where` clause verbatim. Because the blob * was untyped, GraphQL could not validate it, so a caller controlled the full Prisma filter * grammar — and Prisma's `where` is built from the *database* model, not the GraphQL model. Every * column was therefore filterable whether or not it was queryable, including credential columns * removed from the GraphQL layer with `@graphqlOmit`. Presence/absence of results is an oracle, * so those columns could be read back one character at a time. * * These generated inputs close that off: a caller can only filter on columns we emit, using * operators the column's type supports. `@graphqlOmit` fields never reach this module — the CRUD * generator strips them from `model.fields` before we see them — so an omitted column is * unfilterable by construction rather than by a second exclusion list that could drift. * * Operator and field names deliberately mirror Prisma's own filter grammar. The generated data * access service normalizes the compatibility shape for to-one relations into Prisma's relation * filter form before merging it into `where`; every other emitted object is already a direct * Prisma subset. * * Logical operators and relation traversal share one depth budget. Each level points only at the * next generated level, so callers can compose useful filters without regaining an unbounded, * self-referencing input type. * * One deliberate omission remains: scalar list fields (`String[]`) are not filterable. Prisma * models those with * `has`/`hasEvery`/`hasSome` rather than the operators here, and nothing downstream filters on * them today. */ /** Filter nesting levels to emit. Level `maxDepth` carries scalars only, which terminates recursion. */ export declare const DEFAULT_FILTER_DEPTH = 3; /** * Filter input for a model with no filterable column of its own. * * Such a model still needs *something* to override the inherited blob with. An explicit `@Field` * override is the only mechanism that actually removes an inherited field from a code-first * schema — `@HideField()` is a no-op at runtime (it exists to steer the CLI plugin), and leaving * the field alone keeps `filters: JSONObject` on that model, which is the whole vulnerability. * GraphQL rejects an input type with zero fields, so this carries one inert placeholder. */ export declare const UNFILTERABLE_INPUT_NAME = "UnfilterableInput"; export interface FilterInputsResult { /** Source for the generated filter input classes; empty only when there are no models. */ source: string; /** * Filter input class name per model, for the `filters` override on its list input. Every model * gets an entry — models with nothing filterable map to {@link UNFILTERABLE_INPUT_NAME} — so no * generated list input is left inheriting the untyped blob. */ filterInputNames: Record; } /** * Builds the filter input classes for the given models. * * Filter nesting is capped by generating a distinct type per level rather than a single * self-referencing type: `UserFilterInput` points at `PostFilterInput2` for a relation, or at * `UserFilterInput2` for a logical operator. Level 3 carries scalars only. A recursive input type * would let a caller nest filters arbitrarily deep at query time, which typing alone would not * prevent. * * @param models Models to emit filters for, already stripped of `@skipCrud` and `@graphqlOmit`. * @param maxDepth Filter nesting levels to emit; values below 1 fall back to the default. */ export declare function generateFilterInputs(models: readonly ModelType[], maxDepth?: number): FilterInputsResult;