@querykitjs/prisma-pg 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,494 @@
1
+ import { Types } from '@prisma/client/runtime/library';
2
+ import * as _querykitjs_core from '@querykitjs/core';
3
+ import { SkippedCondition } from '@querykitjs/core';
4
+ export { f } from '@querykitjs/core';
5
+
6
+ /**
7
+ * Model metadata — the Prisma counterpart of drizzle's `columns.ts` and
8
+ * mongoose's `fields.ts`.
9
+ *
10
+ * Prisma's `where` is keyed by **model field name** (camelCase), never by the DB
11
+ * column — but a client may well send `created_at`, which drizzle-pg accepts. So
12
+ * each field's `@map`ped column is recorded as an alias and `resolveField`
13
+ * translates it back, keeping the two Postgres adapters interchangeable.
14
+ *
15
+ * Getting the field list without importing the generated client is the other
16
+ * half of the job: this package must typecheck with no generated client present,
17
+ * so metadata is read off the client **instance** at runtime, through a fallback
18
+ * chain.
19
+ */
20
+ /** One model field, normalized across every metadata source. */
21
+ interface FieldMeta {
22
+ name: string;
23
+ /** Prisma type name: `"Int"`, `"String"`, `"DateTime"`, an enum or model name, … */
24
+ type: string;
25
+ kind: "scalar" | "enum" | "object" | "unsupported";
26
+ isId: boolean;
27
+ /** `@updatedAt` — Prisma bumps these itself, so the adapter must not. */
28
+ isUpdatedAt: boolean;
29
+ isList: boolean;
30
+ /** The `@map`ped database column, when it differs from the field name. */
31
+ dbName?: string;
32
+ }
33
+ /** Everything the repository needs to know about one model. */
34
+ interface ModelMeta {
35
+ /** Prisma model name (`"User"`) — used as the diagnostics `source`. */
36
+ name: string;
37
+ fields: Map<string, FieldMeta>;
38
+ /** `@map`ped DB column → Prisma field name, so `created_at` also resolves. */
39
+ byDbName: Map<string, string>;
40
+ /** The `@id` field name; undefined for a composite `@@id`. */
41
+ idField: string | undefined;
42
+ /** Fields that uniquely address a row — one `@id`, or every `@@id` field. */
43
+ primaryKey: string[];
44
+ /** Prisma's name for a composite key (`@@id(name:)`), else the joined fields. */
45
+ primaryKeyName: string | undefined;
46
+ hasDeletedAt: boolean;
47
+ /** Present only when the field exists **and** is not `@updatedAt`-managed. */
48
+ stampUpdatedAt: string | undefined;
49
+ hasCreatedAt: boolean;
50
+ }
51
+
52
+ /**
53
+ * QueryKit Prisma adapter — public types. The DSL/filter/meta types come from
54
+ * `@querykitjs/core` (shared with the drizzle-pg and mongoose adapters); they are
55
+ * only re-specialized here over a Prisma **model delegate** (`TDelegate`), with
56
+ * the raw escape-hatch bound to that model's native `WhereInput`.
57
+ *
58
+ * The public surface mirrors `@querykitjs/drizzle-pg` and `@querykitjs/mongoose`
59
+ * so the same frontend request contract works regardless of backend: same option
60
+ * names, same semantics, one shared source in core.
61
+ *
62
+ * ⚠️ This file must never import from `@prisma/client` itself — only from
63
+ * `@prisma/client/runtime/library`, which exists whether or not a client has been
64
+ * generated. That is what lets the package typecheck and build in CI with no
65
+ * `prisma generate` step, while still inferring exact row types in a consumer's
66
+ * project. `Types.Public.*` are Prisma's own public type utilities; the generated
67
+ * client re-exports the very same ones as `Prisma.Result` / `Prisma.Args`.
68
+ */
69
+
70
+ /** Structural minimum of a Prisma model delegate (`prisma.user`). */
71
+ interface AnyDelegate {
72
+ findMany(args?: any): any;
73
+ findFirst(args?: any): any;
74
+ count(args?: any): any;
75
+ }
76
+ /** Any Prisma client (or interactive-transaction client). */
77
+ type AnyClient = Record<string, any>;
78
+ /**
79
+ * The model keys of a Prisma client — `"user" | "post"`. Prisma names its
80
+ * delegates in camelCase, so model `LegalEntity` is `prisma.legalEntity`.
81
+ */
82
+ type ModelKey<TClient> = {
83
+ [K in keyof TClient & string]: K extends `$${string}` | `_${string}` ? never : TClient[K] extends AnyDelegate ? K : never;
84
+ }[keyof TClient & string];
85
+ /** The delegate behind a model key. */
86
+ type DelegateOf<TClient, TKey extends ModelKey<TClient>> = TClient[TKey];
87
+ type ArgsOf<TDelegate, TOperation extends Types.Public.Operation> = Types.Public.Args<TDelegate, TOperation>;
88
+ /** One row with the given Prisma args applied (`select` / `include`). */
89
+ type RowWith<TDelegate, TArgs> = Types.Public.Result<TDelegate, TArgs, "findFirstOrThrow">;
90
+ /** Full row shape of a model (what reads return with no `columns`/`with`). */
91
+ type Row<TDelegate> = RowWith<TDelegate, Record<string, never>>;
92
+ /** Insertable shape — the model's `create` data. */
93
+ type Insert<TDelegate> = ArgsOf<TDelegate, "create">["data"];
94
+ /**
95
+ * Raw Prisma predicate — the filter escape hatch (`TRaw`), i.e. this model's
96
+ * `Prisma.<Model>WhereInput`.
97
+ */
98
+ type RawWhere<TDelegate> = NonNullable<ArgsOf<TDelegate, "findMany">["where"]>;
99
+ /** This model's `Prisma.<Model>Select`. */
100
+ type SelectInput<TDelegate> = NonNullable<ArgsOf<TDelegate, "findMany">["select"]>;
101
+ /** This model's `Prisma.<Model>Include`. */
102
+ type IncludeInput<TDelegate> = NonNullable<ArgsOf<TDelegate, "findMany">["include"]>;
103
+ /** Value accepted anywhere an id is expected. */
104
+ type Id = string | number;
105
+ /** Union of a model's scalar field keys. */
106
+ type FieldKey<TDelegate> = keyof Row<TDelegate> & string;
107
+ /**
108
+ * A field key that also accepts any `string`. Filter/sort keys arrive from the
109
+ * wire as plain strings and the adapter skips the ones it cannot resolve, so a
110
+ * validated payload can go straight into a repository call with no `as` cast.
111
+ * Autocomplete still suggests the model's real fields.
112
+ */
113
+ type LooseFieldKey<TDelegate> = _querykitjs_core.LooseKey<FieldKey<TDelegate>>;
114
+ type FilterOperator = _querykitjs_core.FilterOperator;
115
+ type FilterScalar = _querykitjs_core.FilterScalar;
116
+ type FilterValue = _querykitjs_core.FilterValue;
117
+ type FieldCondition<TDelegate = AnyDelegate> = _querykitjs_core.FieldCondition<LooseFieldKey<TDelegate>>;
118
+ type AndGroup<TDelegate = AnyDelegate> = _querykitjs_core.AndGroup<LooseFieldKey<TDelegate>, RawWhere<TDelegate>>;
119
+ type OrGroup<TDelegate = AnyDelegate> = _querykitjs_core.OrGroup<LooseFieldKey<TDelegate>, RawWhere<TDelegate>>;
120
+ type NotGroup<TDelegate = AnyDelegate> = _querykitjs_core.NotGroup<LooseFieldKey<TDelegate>, RawWhere<TDelegate>>;
121
+ /**
122
+ * Any node in a filter tree: a field comparison, a logical group, or a raw
123
+ * Prisma `where` object (escape hatch for predicates the DSL can't express).
124
+ */
125
+ type FilterNode<TDelegate = AnyDelegate> = _querykitjs_core.FilterNode<LooseFieldKey<TDelegate>, RawWhere<TDelegate>>;
126
+ /**
127
+ * Public filter input. Either a filter tree/node, or a flat array of conditions
128
+ * (treated as implicit AND — backward compatible with db-service).
129
+ */
130
+ type Filter<TDelegate = AnyDelegate> = _querykitjs_core.Filter<LooseFieldKey<TDelegate>, RawWhere<TDelegate>>;
131
+ type SortDirection = _querykitjs_core.SortDirection;
132
+ type SortItem<TDelegate = AnyDelegate> = _querykitjs_core.SortItem<LooseFieldKey<TDelegate>>;
133
+ /**
134
+ * Sort — **always an array** of `{ key, direction }` (multi-field). This is the
135
+ * exact shape the querykit frontend sends (`@querykitjs/web` decodes its
136
+ * `sortType=-createdAt,id` URL string into it).
137
+ */
138
+ type Sort<TDelegate = AnyDelegate> = SortItem<TDelegate>[];
139
+ /** Inclusion-only column selection — `{ id: true, email: true }`. */
140
+ type ColumnSelection<TDelegate = AnyDelegate> = Partial<Record<FieldKey<TDelegate>, boolean>>;
141
+ /** Relations to load — forwarded to Prisma's `include` (or nested `select`). */
142
+ type WithRelations = Record<string, unknown>;
143
+ interface QueryParams<TDelegate = AnyDelegate> {
144
+ filter?: Filter<TDelegate>;
145
+ sort?: Sort<TDelegate>;
146
+ columns?: ColumnSelection<TDelegate>;
147
+ with?: WithRelations;
148
+ /** Include soft-deleted rows (models with a `deletedAt` field). */
149
+ withDeleted?: boolean;
150
+ }
151
+ /** Equality scope applied to every operation of a scoped repository. */
152
+ type Scope<TDelegate = AnyDelegate> = _querykitjs_core.Scope<FieldKey<TDelegate>>;
153
+ interface ByIdParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
154
+ /** Field to match against the id. Defaults to `"id"` (→ the model's `@id`). */
155
+ idKey?: LooseFieldKey<TDelegate>;
156
+ }
157
+ /** Options for {@link Repository.upsert} / {@link Repository.upsertMany}. */
158
+ type UpsertOptions<TDelegate = AnyDelegate> = _querykitjs_core.UpsertOptions<FieldKey<TDelegate>, Insert<TDelegate>>;
159
+ /** Aggregate query specification (`count`/`sum`/`avg`/`min`/`max` + `groupBy`). */
160
+ type AggregateSpec<TDelegate = AnyDelegate> = _querykitjs_core.AggregateSpec<FieldKey<TDelegate>, Filter<TDelegate>>;
161
+ /** One aggregated result row (group fields + aggregate values). */
162
+ type AggregateRow = Record<string, unknown>;
163
+ /** Offset (classic page-based) pagination. */
164
+ interface OffsetParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
165
+ page?: number;
166
+ perPage?: number;
167
+ }
168
+ type OffsetMeta = _querykitjs_core.OffsetMeta;
169
+ interface OffsetResult<T> {
170
+ data: T[];
171
+ meta: OffsetMeta;
172
+ }
173
+ /** Offset-window pagination tuned for infinite scroll (limit + offset). */
174
+ interface InfiniteParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
175
+ limit?: number;
176
+ offset?: number;
177
+ }
178
+ type InfiniteMeta = _querykitjs_core.InfiniteMeta;
179
+ interface InfiniteResult<T> {
180
+ data: T[];
181
+ meta: InfiniteMeta;
182
+ }
183
+ /** Keyset (cursor) pagination — stable under inserts, ideal for feeds. */
184
+ interface CursorParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
185
+ limit?: number;
186
+ /** Opaque cursor token from a previous result's meta. */
187
+ cursor?: string | null;
188
+ /** Field the cursor walks over. Must be unique & sortable. Defaults to `"id"`. */
189
+ cursorKey?: LooseFieldKey<TDelegate>;
190
+ /** Stable order of the dataset. Defaults to `"asc"`. */
191
+ order?: SortDirection;
192
+ /** Navigation relative to the cursor. Defaults to `"forward"`. */
193
+ direction?: "forward" | "backward";
194
+ }
195
+ type CursorMeta = _querykitjs_core.CursorMeta;
196
+ interface CursorResult<T> {
197
+ data: T[];
198
+ meta: CursorMeta;
199
+ }
200
+ type TrueKeys<T> = {
201
+ [K in keyof T]: T[K] extends true ? K : never;
202
+ }[keyof T];
203
+ /** Relation keys that are actually loaded — `true` or a nested config object. */
204
+ type LoadedKeys<T> = {
205
+ [K in keyof T]-?: T[K] extends false | undefined | null ? never : K;
206
+ }[keyof T];
207
+ type HasColumns<T> = T extends object ? ([TrueKeys<T>] extends [never] ? false : true) : false;
208
+ type HasWith<T> = T extends object ? ([LoadedKeys<T>] extends [never] ? false : true) : false;
209
+ /**
210
+ * The result row for a given `columns` selection and loaded `with` relations.
211
+ * An empty/all-false `columns` yields the full row, matching the runtime
212
+ * (inclusion-only projection) and the other two adapters.
213
+ */
214
+ type ResultFor<TDelegate, TColumns, TWith> = HasColumns<TColumns> extends true ? HasWith<TWith> extends true ? RowWith<TDelegate, {
215
+ select: TColumns & TWith;
216
+ }> : RowWith<TDelegate, {
217
+ select: TColumns;
218
+ }> : HasWith<TWith> extends true ? RowWith<TDelegate, {
219
+ include: TWith;
220
+ }> : Row<TDelegate>;
221
+ /** Params whose `columns`/`with` are typed to the passed selection (drives inference). */
222
+ type Params<TBase, TColumns, TWith> = Omit<TBase, "columns" | "with"> & {
223
+ columns?: TColumns;
224
+ with?: TWith;
225
+ };
226
+ /**
227
+ * Model-scoped repository. Read methods infer their return type from the
228
+ * `columns` and `with` arguments, mirroring `@querykitjs/drizzle-pg`.
229
+ */
230
+ interface Repository<TDelegate = AnyDelegate> {
231
+ /** The underlying Prisma model delegate — escape hatch for custom queries. */
232
+ readonly delegate: TDelegate;
233
+ /**
234
+ * Returns **all** rows matching the filter (no pagination).
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * const active = await usersRepository.findAll({
239
+ * filter: [{ key: "status", operation: "=", value: "active" }],
240
+ * sort: [{ key: "createdAt", direction: "desc" }],
241
+ * });
242
+ * ```
243
+ */
244
+ findAll<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDelegate>, TColumns, TWith>): Promise<ResultFor<TDelegate, TColumns, TWith>[]>;
245
+ /** Returns the **first** matching row (or `undefined`). */
246
+ findOne<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDelegate>, TColumns, TWith>): Promise<ResultFor<TDelegate, TColumns, TWith> | undefined>;
247
+ /** Returns one row by id (or `undefined`). Use `params.idKey` for another field. */
248
+ findById<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(id: Id, params?: Params<ByIdParams<TDelegate>, TColumns, TWith>): Promise<ResultFor<TDelegate, TColumns, TWith> | undefined>;
249
+ /**
250
+ * **Offset (page-based) pagination** — with `total_items`/`total_pages`.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * const { data, meta } = await usersRepository.findList({ page: 2, perPage: 20 });
255
+ * ```
256
+ */
257
+ findList<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<OffsetParams<TDelegate>, TColumns, TWith>): Promise<OffsetResult<ResultFor<TDelegate, TColumns, TWith>>>;
258
+ /** **Infinite-scroll pagination** — `limit`+`offset`, no `COUNT`. */
259
+ findInfinite<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<InfiniteParams<TDelegate>, TColumns, TWith>): Promise<InfiniteResult<ResultFor<TDelegate, TColumns, TWith>>>;
260
+ /** **Cursor (keyset) pagination** — stable under inserts, ideal for feeds. */
261
+ findCursor<const TColumns extends ColumnSelection<TDelegate> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<CursorParams<TDelegate>, TColumns, TWith>): Promise<CursorResult<ResultFor<TDelegate, TColumns, TWith>>>;
262
+ /** Number of rows matching the filter. */
263
+ count(filter?: Filter<TDelegate>): Promise<number>;
264
+ /** Whether any row matches the filter (`count > 0`). */
265
+ exists(filter?: Filter<TDelegate>): Promise<boolean>;
266
+ /** Create one row and return it. */
267
+ create(values: Insert<TDelegate>): Promise<Row<TDelegate>>;
268
+ /** Create many rows, returning them in input order. */
269
+ createMany(values: Insert<TDelegate>[]): Promise<Row<TDelegate>[]>;
270
+ /** Insert, or update on `target` conflict (Prisma `upsert`). */
271
+ upsert(values: Insert<TDelegate>, options: UpsertOptions<TDelegate>): Promise<Row<TDelegate>>;
272
+ /** Bulk upsert (chunked); returns rows in input order. */
273
+ upsertMany(values: Insert<TDelegate>[], options: UpsertOptions<TDelegate>): Promise<Row<TDelegate>[]>;
274
+ /** Update one row by id; returns the updated row (or `undefined`). */
275
+ updateById(id: Id, patch: Partial<Insert<TDelegate>>, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
276
+ /** Update every row matching the filter; returns the updated rows. */
277
+ updateWhere(filter: Filter<TDelegate>, patch: Partial<Insert<TDelegate>>): Promise<Row<TDelegate>[]>;
278
+ /** Hard-delete one row by id; returns the deleted row (or `undefined`). */
279
+ deleteById(id: Id, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
280
+ /** Hard-delete every row matching the filter; returns the deleted rows. */
281
+ deleteWhere(filter: Filter<TDelegate>): Promise<Row<TDelegate>[]>;
282
+ /** Soft-delete (set `deletedAt`); requires a `deletedAt` field. */
283
+ softDelete(id: Id, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
284
+ /** Restore a soft-deleted row (`deletedAt = null`). */
285
+ restore(id: Id, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
286
+ /** Aggregate: `count`/`sum`/`avg`/`min`/`max` with optional `groupBy`. */
287
+ aggregate(spec: AggregateSpec<TDelegate>): Promise<AggregateRow[]>;
288
+ /**
289
+ * **Scoped repository** — adds a constant equality filter to every read/write
290
+ * and defaults it on inserts (scope wins). For RBAC / multi-tenancy.
291
+ */
292
+ scoped(scope: Scope<TDelegate>): Repository<TDelegate>;
293
+ }
294
+ /** Extends a base repository with custom, model-specific methods. */
295
+ type RepositoryExtender<TDelegate, TExt extends Record<string, unknown>> = (base: Repository<TDelegate>) => TExt;
296
+ /** {@link createRegistry} options — pagination defaults and bounds. */
297
+ interface RegistryOptions {
298
+ /** `findList` default page size (core `DEFAULT_PER_PAGE` = 20). */
299
+ defaultPerPage?: number;
300
+ /** `findInfinite`/`findCursor` default limit (core `DEFAULT_LIMIT` = 20). */
301
+ defaultLimit?: number;
302
+ /**
303
+ * Upper bound for `perPage` (core `DEFAULT_MAX_PER_PAGE` = 200). Applied even
304
+ * when validation was bypassed — defense in depth. Disable with `Infinity`.
305
+ */
306
+ maxPerPage?: number;
307
+ /** Upper bound for `limit` (`findInfinite`/`findCursor`, core `DEFAULT_MAX_LIMIT` = 200). */
308
+ maxLimit?: number;
309
+ /**
310
+ * **Strict mode.** An unknown filter/sort key, or a value the operator cannot
311
+ * use, is no longer dropped silently — a `QueryKitError` is thrown (the backend
312
+ * maps it to a 400). Defaults to `false`, matching the other adapters.
313
+ *
314
+ * Why it matters: a filter is meant to **narrow** a result set, so a mistyped
315
+ * key that vanishes silently makes the endpoint return **more** data than
316
+ * intended, with nothing in the log to show it.
317
+ */
318
+ strict?: boolean;
319
+ /**
320
+ * Called for every dropped condition (also when `strict` is off). Migration
321
+ * path: watch with the hook first, flip `strict: true` once the log is clean.
322
+ */
323
+ onSkippedCondition?: (info: _querykitjs_core.SkippedCondition) => void;
324
+ }
325
+ /**
326
+ * Options for `registry.repository(model, options)`.
327
+ *
328
+ * Philosophy: the guard lives in the **repository**, not the route — `scope`
329
+ * (RBAC) already worked that way and projection joins it. Same names and same
330
+ * semantics as the drizzle-pg and mongoose adapters.
331
+ *
332
+ * ⚠️ `forcedColumns`/`allowedColumns` affect **read** methods only
333
+ * (`findAll`/`findOne`/`findById`/`findList`/`findInfinite`/`findCursor` +
334
+ * `aggregate`). Write methods return the full `Row` by type contract — trimming
335
+ * it at runtime would make the type a lie. Re-read with `findById`, or map it
336
+ * yourself, before handing a write result to a client.
337
+ */
338
+ interface RepositoryOptions<TDelegate = AnyDelegate> {
339
+ /** Constant equality filter added to every read/write (RBAC / multi-tenancy). */
340
+ scope?: Scope<TDelegate>;
341
+ /**
342
+ * Forced projection — the client's `columns` is **ignored entirely**. Keeps a
343
+ * password out of a `users` response without a `{ ...params, columns }` trick
344
+ * in every route. Must select at least one field, else building throws.
345
+ */
346
+ forcedColumns?: ColumnSelection<TDelegate>;
347
+ /**
348
+ * Allowlist — the client's `columns` is intersected with it. An empty
349
+ * intersection yields the allowlist itself, **never** the full row. An empty
350
+ * array throws (a projection selecting nothing means "everything").
351
+ */
352
+ allowedColumns?: readonly FieldKey<TDelegate>[];
353
+ /**
354
+ * Explicit field metadata. Only needed when this adapter cannot read it off
355
+ * the Prisma client itself (it tries `_runtimeDataModel`, then the public
356
+ * `delegate.fields`) — e.g. behind an unusual client wrapper.
357
+ */
358
+ fields?: readonly FieldMeta[];
359
+ }
360
+ /** DB registry: created once, yields model repositories. */
361
+ interface Registry<TClient extends AnyClient = AnyClient> {
362
+ /** The Prisma client the registry was created with. */
363
+ readonly client: TClient;
364
+ /**
365
+ * Repository for a model — addressed either by the model handle
366
+ * (`prisma.user`, like the drizzle-pg and mongoose registries take a table or
367
+ * a Model) or by its Prisma delegate key (`"user"`, camelCase).
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * export const usersRepository = registry.repository(prisma.user);
372
+ * // …or, equivalently:
373
+ * export const usersRepository = registry.repository("user");
374
+ * ```
375
+ */
376
+ repository<TKey extends ModelKey<TClient>>(model: TKey): Repository<DelegateOf<TClient, TKey>>;
377
+ /** …extended with custom methods (2nd-arg extender fn — same as the other adapters). */
378
+ repository<TKey extends ModelKey<TClient>, TExt extends Record<string, unknown>>(model: TKey, extend: RepositoryExtender<DelegateOf<TClient, TKey>, TExt>): Repository<DelegateOf<TClient, TKey>> & TExt;
379
+ /** …with per-repository options (`scope`, `forcedColumns`, `allowedColumns`). */
380
+ repository<TKey extends ModelKey<TClient>>(model: TKey, options: RepositoryOptions<DelegateOf<TClient, TKey>>): Repository<DelegateOf<TClient, TKey>>;
381
+ /** …with both options and custom methods. */
382
+ repository<TKey extends ModelKey<TClient>, TExt extends Record<string, unknown>>(model: TKey, options: RepositoryOptions<DelegateOf<TClient, TKey>>, extend: RepositoryExtender<DelegateOf<TClient, TKey>, TExt>): Repository<DelegateOf<TClient, TKey>> & TExt;
383
+ repository<TDelegate extends AnyDelegate>(model: TDelegate): Repository<TDelegate>;
384
+ repository<TDelegate extends AnyDelegate, TExt extends Record<string, unknown>>(model: TDelegate, extend: RepositoryExtender<TDelegate, TExt>): Repository<TDelegate> & TExt;
385
+ repository<TDelegate extends AnyDelegate>(model: TDelegate, options: RepositoryOptions<TDelegate>): Repository<TDelegate>;
386
+ repository<TDelegate extends AnyDelegate, TExt extends Record<string, unknown>>(model: TDelegate, options: RepositoryOptions<TDelegate>, extend: RepositoryExtender<TDelegate, TExt>): Repository<TDelegate> & TExt;
387
+ /**
388
+ * Runs `fn` inside a Prisma interactive transaction. Repositories used inside
389
+ * it automatically run on that transaction client (ambient context).
390
+ *
391
+ * ⚠️ Prisma has no savepoints, so a **nested** call reuses the ambient
392
+ * transaction instead of opening a new one (same as the mongoose adapter;
393
+ * drizzle-pg creates a real savepoint there).
394
+ */
395
+ transaction<T>(fn: () => Promise<T>): Promise<T>;
396
+ }
397
+
398
+ /**
399
+ * Create the DB **registry** once. It holds the Prisma client (for transactions)
400
+ * and yields a {@link Repository} per model. Transactions flow ambiently via
401
+ * AsyncLocalStorage — repositories used inside `registry.transaction(...)`
402
+ * automatically run on that transaction client, with no `tx` threading.
403
+ *
404
+ * Framework-agnostic by design: this package knows nothing about HTTP, so the
405
+ * same registry serves Express, Hono, NestJS or a background worker. Parse and
406
+ * validate the request in your app (`@querykitjs/zod` or
407
+ * `@querykitjs/class-validator`), then hand the params straight to a repository.
408
+ *
409
+ * @param prisma - your `PrismaClient` instance.
410
+ * @param options - pagination defaults/bounds and diagnostics; defaults come
411
+ * from `@querykitjs/core` (`DEFAULT_PER_PAGE`/`DEFAULT_LIMIT` = 20).
412
+ * @example
413
+ * ```ts
414
+ * // registry.ts (once)
415
+ * export const registry = createRegistry(prisma);
416
+ *
417
+ * // users.repository.ts — with custom methods
418
+ * export const usersRepository = registry.repository("user", base => ({
419
+ * findByEmail: (email: string) => base.findOne({ filter: [{ key: "email", value: email }] }),
420
+ * }));
421
+ * ```
422
+ */
423
+ declare function createRegistry<TClient extends AnyClient>(prisma: TClient, options?: RegistryOptions): Registry<TClient>;
424
+
425
+ /**
426
+ * A Prisma client or interactive-transaction client. The adapter only ever needs
427
+ * model delegates off it, so it is kept structural — this file never imports
428
+ * `@prisma/client` (see the package README: the generated client is a peer).
429
+ */
430
+ type AnyExecutor = Record<string, unknown>;
431
+
432
+ /** Shared, registry-level runtime injected into every repository. */
433
+ interface RepoRuntime {
434
+ /** Base (non-transactional) Prisma client. */
435
+ baseClient: AnyExecutor;
436
+ /** Current executor — the active transaction client if any, else `baseClient`. */
437
+ getExecutor: () => AnyExecutor;
438
+ /** Default page size for `findList` when `perPage` is omitted. */
439
+ defaultPerPage: number;
440
+ /** Default `limit` for `findInfinite`/`findCursor` when omitted. */
441
+ defaultLimit: number;
442
+ /** Upper bound for `perPage`, applied even when validation was bypassed. */
443
+ maxPerPage: number;
444
+ /** Upper bound for `limit` (`findInfinite`/`findCursor`). */
445
+ maxLimit: number;
446
+ /** Throw `QueryKitError` instead of dropping an unresolvable condition. */
447
+ strict: boolean;
448
+ /** Called for every dropped condition (also when `strict` is off). */
449
+ onSkippedCondition?: (info: SkippedCondition) => void;
450
+ }
451
+ /**
452
+ * Build a model-scoped repository. `runtime` (client + ambient transaction
453
+ * context) comes from the registry, so this file never touches the app's
454
+ * database handle directly.
455
+ */
456
+ declare function buildRepository<TDelegate>(runtime: RepoRuntime, modelKey: string, config?: RepositoryOptions<TDelegate>): Repository<TDelegate>;
457
+
458
+ /**
459
+ * Typed, ergonomic filter builders for a model (field-name autocomplete). A thin
460
+ * wrapper over `@querykitjs/core`'s builder — the key type is the model's field
461
+ * key, the raw escape hatch is that model's native `WhereInput`.
462
+ *
463
+ * @example
464
+ * ```ts
465
+ * const uf = createFilters<typeof prisma.user>();
466
+ * await usersRepository.findAll({ filter: uf.and(uf.eq("status", "active"), uf.gte("age", 18)) });
467
+ * ```
468
+ */
469
+ declare function createFilters<TDelegate>(): {
470
+ eq: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
471
+ ne: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
472
+ gt: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
473
+ gte: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
474
+ lt: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
475
+ lte: (key: FieldKey<TDelegate>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
476
+ contains: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
477
+ startsWith: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
478
+ endsWith: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
479
+ like: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
480
+ ilike: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
481
+ notLike: (key: FieldKey<TDelegate>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
482
+ in: (key: FieldKey<TDelegate>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
483
+ notIn: (key: FieldKey<TDelegate>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
484
+ between: (key: FieldKey<TDelegate>, min: _querykitjs_core.FilterScalar | Date, max: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
485
+ notBetween: (key: FieldKey<TDelegate>, min: _querykitjs_core.FilterScalar | Date, max: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
486
+ range: (key: FieldKey<TDelegate>, from: _querykitjs_core.FilterScalar | Date, to: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>[];
487
+ isNull: (key: FieldKey<TDelegate>) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
488
+ isNotNull: (key: FieldKey<TDelegate>) => _querykitjs_core.FieldCondition<FieldKey<TDelegate>>;
489
+ and: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDelegate>, RawWhere<TDelegate>>[]) => _querykitjs_core.AndGroup<FieldKey<TDelegate>, RawWhere<TDelegate>>;
490
+ or: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDelegate>, RawWhere<TDelegate>>[]) => _querykitjs_core.OrGroup<FieldKey<TDelegate>, RawWhere<TDelegate>>;
491
+ not: (node: _querykitjs_core.FilterNode<FieldKey<TDelegate>, RawWhere<TDelegate>>) => _querykitjs_core.NotGroup<FieldKey<TDelegate>, RawWhere<TDelegate>>;
492
+ };
493
+
494
+ export { type AggregateRow, type AggregateSpec, type AndGroup, type AnyClient, type AnyDelegate, type ByIdParams, type ColumnSelection, type CursorMeta, type CursorParams, type CursorResult, type DelegateOf, type FieldCondition, type FieldKey, type FieldMeta, type Filter, type FilterNode, type FilterOperator, type FilterScalar, type FilterValue, type Id, type IncludeInput, type InfiniteMeta, type InfiniteParams, type InfiniteResult, type Insert, type LooseFieldKey, type ModelKey, type ModelMeta, type NotGroup, type OffsetMeta, type OffsetParams, type OffsetResult, type OrGroup, type QueryParams, type RawWhere, type Registry, type RegistryOptions, type RepoRuntime, type Repository, type RepositoryExtender, type RepositoryOptions, type ResultFor, type Row, type Scope, type SelectInput, type Sort, type SortDirection, type SortItem, type UpsertOptions, type WithRelations, buildRepository, createFilters, createRegistry };