@querykitjs/prisma-pg 1.0.1 → 2.0.1

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.
package/dist/index.d.cts CHANGED
@@ -1,53 +1,16 @@
1
- import { Types } from '@prisma/client/runtime/library';
2
1
  import * as _querykitjs_core from '@querykitjs/core';
3
- import { SkippedCondition } from '@querykitjs/core';
2
+ import { AllRows, MutationCount, MutationRows, Relations, SkippedCondition, QueryLimits } from '@querykitjs/core';
4
3
  export { f } from '@querykitjs/core';
4
+ import { Types } from '@prisma/client/runtime/client';
5
5
 
6
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.
7
+ * Prisma type utilities the public types are built from. Not re-exported by
8
+ * `types/index.ts`: they are implementation helpers, not part of the API.
19
9
  */
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
- }
10
+
11
+ type ArgsOf<TDelegate, TOperation extends Types.Public.Operation> = Types.Public.Args<TDelegate, TOperation>;
12
+ /** One row with the given Prisma args applied (`select` / `include`). */
13
+ type RowWith<TDelegate, TArgs> = Types.Public.Result<TDelegate, TArgs, "findFirstOrThrow">;
51
14
 
52
15
  /**
53
16
  * QueryKit Prisma adapter — public types. The DSL/filter/meta types come from
@@ -60,8 +23,9 @@ interface ModelMeta {
60
23
  * names, same semantics, one shared source in core.
61
24
  *
62
25
  * ⚠️ 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
26
+ * `@prisma/client/runtime/client`, which exists whether or not a client has been
27
+ * generated (Prisma 6.19 and 7 both ship it; 7 has no `runtime/library`). That
28
+ * is what lets the package typecheck and build in CI with no
65
29
  * `prisma generate` step, while still inferring exact row types in a consumer's
66
30
  * project. `Types.Public.*` are Prisma's own public type utilities; the generated
67
31
  * client re-exports the very same ones as `Prisma.Result` / `Prisma.Args`.
@@ -84,9 +48,6 @@ type ModelKey<TClient> = {
84
48
  }[keyof TClient & string];
85
49
  /** The delegate behind a model key. */
86
50
  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
51
  /** Full row shape of a model (what reads return with no `columns`/`with`). */
91
52
  type Row<TDelegate> = RowWith<TDelegate, Record<string, never>>;
92
53
  /** Insertable shape — the model's `create` data. */
@@ -160,10 +121,13 @@ type UpsertOptions<TDelegate = AnyDelegate> = _querykitjs_core.UpsertOptions<Fie
160
121
  type AggregateSpec<TDelegate = AnyDelegate> = _querykitjs_core.AggregateSpec<FieldKey<TDelegate>, Filter<TDelegate>>;
161
122
  /** One aggregated result row (group fields + aggregate values). */
162
123
  type AggregateRow = Record<string, unknown>;
124
+
163
125
  /** Offset (classic page-based) pagination. */
164
126
  interface OffsetParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
165
127
  page?: number;
166
128
  perPage?: number;
129
+ /** `false` — no count query: `perPage + 1` rows are read, `total_items` / `total_pages` are `null`. Default `true`. */
130
+ withTotal?: boolean;
167
131
  }
168
132
  type OffsetMeta = _querykitjs_core.OffsetMeta;
169
133
  interface OffsetResult<T> {
@@ -197,6 +161,7 @@ interface CursorResult<T> {
197
161
  data: T[];
198
162
  meta: CursorMeta;
199
163
  }
164
+
200
165
  type TrueKeys<T> = {
201
166
  [K in keyof T]: T[K] extends true ? K : never;
202
167
  }[keyof T];
@@ -273,12 +238,32 @@ interface Repository<TDelegate = AnyDelegate> {
273
238
  upsertMany(values: Insert<TDelegate>[], options: UpsertOptions<TDelegate>): Promise<Row<TDelegate>[]>;
274
239
  /** Update one row by id; returns the updated row (or `undefined`). */
275
240
  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>[]>;
241
+ /**
242
+ * Update every row matching the filter in one atomic `updateMany` → `{ count }`.
243
+ * No filter only with `{ all: true }` (the scope still applies); an empty or fully skipped
244
+ * filter is `QueryKitError` `empty-filter`. The filter passes the request policy like a read.
245
+ */
246
+ updateWhere(filter: Filter<TDelegate> | AllRows, patch: Partial<Insert<TDelegate>>, options?: {
247
+ returning?: false;
248
+ }): Promise<MutationCount>;
249
+ /** With `returning: { limit }`: `{ count, rows }` from one atomic `updateManyAndReturn`, at most `limit` rows. */
250
+ updateWhere(filter: Filter<TDelegate> | AllRows, patch: Partial<Insert<TDelegate>>, options: {
251
+ returning: {
252
+ limit: number;
253
+ };
254
+ }): Promise<MutationRows<Row<TDelegate>>>;
278
255
  /** Hard-delete one row by id; returns the deleted row (or `undefined`). */
279
256
  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>[]>;
257
+ /** Hard-delete every row matching the filter in one `deleteMany` → `{ count }` (same rules as `updateWhere`). */
258
+ deleteWhere(filter: Filter<TDelegate> | AllRows, options?: {
259
+ returning?: false;
260
+ }): Promise<MutationCount>;
261
+ /** With `returning: { limit }`: `{ count, rows }` — the rows are read just before the delete (not atomic). */
262
+ deleteWhere(filter: Filter<TDelegate> | AllRows, options: {
263
+ returning: {
264
+ limit: number;
265
+ };
266
+ }): Promise<MutationRows<Row<TDelegate>>>;
282
267
  /** Soft-delete (set `deletedAt`); requires a `deletedAt` field. */
283
268
  softDelete(id: Id, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
284
269
  /** Restore a soft-deleted row (`deletedAt = null`). */
@@ -293,6 +278,69 @@ interface Repository<TDelegate = AnyDelegate> {
293
278
  }
294
279
  /** Extends a base repository with custom, model-specific methods. */
295
280
  type RepositoryExtender<TDelegate, TExt extends Record<string, unknown>> = (base: Repository<TDelegate>) => TExt;
281
+
282
+ /**
283
+ * Model metadata — the Prisma counterpart of drizzle's `columns.ts` and
284
+ * mongoose's `fields.ts`.
285
+ *
286
+ * Prisma's `where` is keyed by **model field name** (camelCase), never by the DB
287
+ * column — but a client may well send `created_at`, which drizzle-pg accepts. So
288
+ * each field's `@map`ped column is recorded as an alias and `resolveField`
289
+ * translates it back, keeping the two Postgres adapters interchangeable.
290
+ *
291
+ * Getting the field list without importing the generated client is the other
292
+ * half of the job: this package must typecheck with no generated client present,
293
+ * so metadata is read off the client **instance** at runtime, through a fallback
294
+ * chain.
295
+ */
296
+ /** One model field, normalized across every metadata source. */
297
+ interface FieldMeta {
298
+ name: string;
299
+ /** Prisma type name: `"Int"`, `"String"`, `"DateTime"`, an enum or model name, … */
300
+ type: string;
301
+ kind: "scalar" | "enum" | "object" | "unsupported";
302
+ isId: boolean;
303
+ /** `@updatedAt` — Prisma bumps these itself, so the adapter must not. */
304
+ isUpdatedAt: boolean;
305
+ isList: boolean;
306
+ /** The `@map`ped database column, when it differs from the field name. */
307
+ dbName?: string;
308
+ /** `false` for an optional (nullable) field — Prisma accepts `nulls` in `orderBy` only there. */
309
+ isRequired?: boolean;
310
+ /** The database type (`"Uuid"` for `@db.Uuid`) — from the runtime model when it has one, or the `fields` override. */
311
+ nativeType?: string;
312
+ /** An enum field's values (from the runtime data model), for the cast. */
313
+ enumValues?: readonly string[];
314
+ }
315
+ /** Everything the repository needs to know about one model. */
316
+ interface ModelMeta {
317
+ /** Prisma model name (`"User"`) — used as the diagnostics `source`. */
318
+ name: string;
319
+ fields: Map<string, FieldMeta>;
320
+ /** `@map`ped DB column → Prisma field name, so `created_at` also resolves. */
321
+ byDbName: Map<string, string>;
322
+ /** The `@id` field name; undefined for a composite `@@id`. */
323
+ idField: string | undefined;
324
+ /** Fields that uniquely address a row — one `@id`, or every `@@id` field. */
325
+ primaryKey: string[];
326
+ /** Prisma's name for a composite key (`@@id(name:)`), else the joined fields. */
327
+ primaryKeyName: string | undefined;
328
+ hasDeletedAt: boolean;
329
+ /** Present only when the field exists **and** is not `@updatedAt`-managed. */
330
+ stampUpdatedAt: string | undefined;
331
+ hasCreatedAt: boolean;
332
+ }
333
+ /**
334
+ * Read a model's metadata off a live Prisma client, trying, in order:
335
+ * `_runtimeDataModel` → `delegate.fields` → the caller-supplied `override`.
336
+ *
337
+ * Throws when none of them work — the same reasoning as drizzle's "table not in
338
+ * schema" error: a repository built on unknown metadata would silently skip
339
+ * *every* filter key and return the whole table.
340
+ */
341
+ /** A `fields` override entry: a full field, or a patch of a discovered one by `name` (e.g. `{ name: "age", isRequired: false }`). */
342
+ type FieldOverride = Pick<FieldMeta, "name"> & Partial<FieldMeta>;
343
+
296
344
  /** {@link createRegistry} options — pagination defaults and bounds. */
297
345
  interface RegistryOptions {
298
346
  /** `findList` default page size (core `DEFAULT_PER_PAGE` = 20). */
@@ -321,6 +369,27 @@ interface RegistryOptions {
321
369
  * path: watch with the hook first, flip `strict: true` once the log is clean.
322
370
  */
323
371
  onSkippedCondition?: (info: _querykitjs_core.SkippedCondition) => void;
372
+ /**
373
+ * Server-side request limits (core `DEFAULT_QUERY_LIMITS`: depth 5, conditions 30,
374
+ * array 200, string 256, offset 10 000, sort 3) — enforced even when validation was
375
+ * bypassed. Past one ⇒ `QueryKitError` (`reason: "limit-exceeded"`). A repository's
376
+ * own `limits` win.
377
+ */
378
+ limits?: Partial<_querykitjs_core.QueryLimits>;
379
+ /**
380
+ * Signs cursor tokens (HMAC-SHA256). With it a token that was not
381
+ * issued by this server — or was changed — is `invalid-value`. Without it tokens
382
+ * are unsigned but still checked (scalar values, v2 shape).
383
+ */
384
+ cursorSecret?: string;
385
+ /**
386
+ * The createdAt / deletedAt field names (default `createdAt`, `deletedAt`): the default
387
+ * sort and the default `sortable` use `createdAt`, soft delete uses `deletedAt`.
388
+ */
389
+ timestamps?: {
390
+ createdAt?: string;
391
+ deletedAt?: string;
392
+ };
324
393
  }
325
394
  /**
326
395
  * Options for `registry.repository(model, options)`.
@@ -336,6 +405,12 @@ interface RegistryOptions {
336
405
  * yourself, before handing a write result to a client.
337
406
  */
338
407
  interface RepositoryOptions<TDelegate = AnyDelegate> {
408
+ /**
409
+ * Relations a client may open with `with` (R21): relation → `{ columns, filterable?, nested? }`.
410
+ * `columns` is required (a list or `"*"`); an unlisted relation, column, `where` field or nested
411
+ * `include` is `QueryKitError` `forbidden-key`, `site: "with"`. Without it `with` opens nothing.
412
+ */
413
+ relations?: Relations;
339
414
  /** Constant equality filter added to every read/write (RBAC / multi-tenancy). */
340
415
  scope?: Scope<TDelegate>;
341
416
  /**
@@ -355,7 +430,24 @@ interface RepositoryOptions<TDelegate = AnyDelegate> {
355
430
  * the Prisma client itself (it tries `_runtimeDataModel`, then the public
356
431
  * `delegate.fields`) — e.g. behind an unusual client wrapper.
357
432
  */
358
- fields?: readonly FieldMeta[];
433
+ fields?: readonly FieldOverride[];
434
+ /**
435
+ * Fields a client may filter by, and the operators for each (`"*"` — all). A
436
+ * relation field is `"relation.field"` (`"posts.title": ["="]`). A field not
437
+ * listed ⇒ `reason: "forbidden-key"`, an operator not listed ⇒ `"forbidden-operator"`
438
+ * — a `QueryKitError` whatever `strict` says, before Prisma is called.
439
+ * **When not given** — `allowedColumns` (else the `forcedColumns` keys, else only
440
+ * the `@id` field) with `"="` and `"in"` only. `aggregate` fields come from this list too.
441
+ */
442
+ filterable?: _querykitjs_core.Filterable<FieldKey<TDelegate> | `${string}.${string}`>;
443
+ /**
444
+ * Fields a client may sort (and use as `cursorKey`) by. When not given — the
445
+ * `@id` field and `createdAt` (when the model has it). Anything else ⇒
446
+ * `reason: "forbidden-key"`.
447
+ */
448
+ sortable?: readonly FieldKey<TDelegate>[];
449
+ /** Request limits for this repository — over the registry's `limits`. */
450
+ limits?: Partial<_querykitjs_core.QueryLimits>;
359
451
  }
360
452
  /** DB registry: created once, yields model repositories. */
361
453
  interface Registry<TClient extends AnyClient = AnyClient> {
@@ -447,10 +539,20 @@ interface RepoRuntime {
447
539
  strict: boolean;
448
540
  /** Called for every dropped condition (also when `strict` is off). */
449
541
  onSkippedCondition?: (info: SkippedCondition) => void;
542
+ /** Registry-level request limits; a repository's own `limits` win. */
543
+ limits?: Partial<QueryLimits>;
544
+ /** Signs cursor tokens. */
545
+ cursorSecret?: string;
546
+ /** createdAt / deletedAt field names; default `createdAt`, `deletedAt`. */
547
+ timestamps?: {
548
+ createdAt?: string;
549
+ deletedAt?: string;
550
+ };
450
551
  }
552
+
451
553
  /**
452
554
  * Build a model-scoped repository. `runtime` (client + ambient transaction
453
- * context) comes from the registry, so this file never touches the app's
555
+ * context) comes from the registry, so this module never touches the app's
454
556
  * database handle directly.
455
557
  */
456
558
  declare function buildRepository<TDelegate>(runtime: RepoRuntime, modelKey: string, config?: RepositoryOptions<TDelegate>): Repository<TDelegate>;
@@ -491,4 +593,5 @@ declare function createFilters<TDelegate>(): {
491
593
  not: (node: _querykitjs_core.FilterNode<FieldKey<TDelegate>, RawWhere<TDelegate>>) => _querykitjs_core.NotGroup<FieldKey<TDelegate>, RawWhere<TDelegate>>;
492
594
  };
493
595
 
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 };
596
+ export { buildRepository, createFilters, createRegistry };
597
+ export type { AggregateRow, AggregateSpec, AndGroup, AnyClient, AnyDelegate, ByIdParams, ColumnSelection, CursorMeta, CursorParams, CursorResult, DelegateOf, FieldCondition, FieldKey, FieldMeta, Filter, FilterNode, FilterOperator, FilterScalar, FilterValue, Id, IncludeInput, InfiniteMeta, InfiniteParams, InfiniteResult, Insert, LooseFieldKey, ModelKey, ModelMeta, NotGroup, OffsetMeta, OffsetParams, OffsetResult, OrGroup, QueryParams, RawWhere, Registry, RegistryOptions, RepoRuntime, Repository, RepositoryExtender, RepositoryOptions, ResultFor, Row, Scope, SelectInput, Sort, SortDirection, SortItem, UpsertOptions, WithRelations };
package/dist/index.d.ts CHANGED
@@ -1,53 +1,16 @@
1
- import { Types } from '@prisma/client/runtime/library';
2
1
  import * as _querykitjs_core from '@querykitjs/core';
3
- import { SkippedCondition } from '@querykitjs/core';
2
+ import { AllRows, MutationCount, MutationRows, Relations, SkippedCondition, QueryLimits } from '@querykitjs/core';
4
3
  export { f } from '@querykitjs/core';
4
+ import { Types } from '@prisma/client/runtime/client';
5
5
 
6
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.
7
+ * Prisma type utilities the public types are built from. Not re-exported by
8
+ * `types/index.ts`: they are implementation helpers, not part of the API.
19
9
  */
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
- }
10
+
11
+ type ArgsOf<TDelegate, TOperation extends Types.Public.Operation> = Types.Public.Args<TDelegate, TOperation>;
12
+ /** One row with the given Prisma args applied (`select` / `include`). */
13
+ type RowWith<TDelegate, TArgs> = Types.Public.Result<TDelegate, TArgs, "findFirstOrThrow">;
51
14
 
52
15
  /**
53
16
  * QueryKit Prisma adapter — public types. The DSL/filter/meta types come from
@@ -60,8 +23,9 @@ interface ModelMeta {
60
23
  * names, same semantics, one shared source in core.
61
24
  *
62
25
  * ⚠️ 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
26
+ * `@prisma/client/runtime/client`, which exists whether or not a client has been
27
+ * generated (Prisma 6.19 and 7 both ship it; 7 has no `runtime/library`). That
28
+ * is what lets the package typecheck and build in CI with no
65
29
  * `prisma generate` step, while still inferring exact row types in a consumer's
66
30
  * project. `Types.Public.*` are Prisma's own public type utilities; the generated
67
31
  * client re-exports the very same ones as `Prisma.Result` / `Prisma.Args`.
@@ -84,9 +48,6 @@ type ModelKey<TClient> = {
84
48
  }[keyof TClient & string];
85
49
  /** The delegate behind a model key. */
86
50
  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
51
  /** Full row shape of a model (what reads return with no `columns`/`with`). */
91
52
  type Row<TDelegate> = RowWith<TDelegate, Record<string, never>>;
92
53
  /** Insertable shape — the model's `create` data. */
@@ -160,10 +121,13 @@ type UpsertOptions<TDelegate = AnyDelegate> = _querykitjs_core.UpsertOptions<Fie
160
121
  type AggregateSpec<TDelegate = AnyDelegate> = _querykitjs_core.AggregateSpec<FieldKey<TDelegate>, Filter<TDelegate>>;
161
122
  /** One aggregated result row (group fields + aggregate values). */
162
123
  type AggregateRow = Record<string, unknown>;
124
+
163
125
  /** Offset (classic page-based) pagination. */
164
126
  interface OffsetParams<TDelegate = AnyDelegate> extends QueryParams<TDelegate> {
165
127
  page?: number;
166
128
  perPage?: number;
129
+ /** `false` — no count query: `perPage + 1` rows are read, `total_items` / `total_pages` are `null`. Default `true`. */
130
+ withTotal?: boolean;
167
131
  }
168
132
  type OffsetMeta = _querykitjs_core.OffsetMeta;
169
133
  interface OffsetResult<T> {
@@ -197,6 +161,7 @@ interface CursorResult<T> {
197
161
  data: T[];
198
162
  meta: CursorMeta;
199
163
  }
164
+
200
165
  type TrueKeys<T> = {
201
166
  [K in keyof T]: T[K] extends true ? K : never;
202
167
  }[keyof T];
@@ -273,12 +238,32 @@ interface Repository<TDelegate = AnyDelegate> {
273
238
  upsertMany(values: Insert<TDelegate>[], options: UpsertOptions<TDelegate>): Promise<Row<TDelegate>[]>;
274
239
  /** Update one row by id; returns the updated row (or `undefined`). */
275
240
  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>[]>;
241
+ /**
242
+ * Update every row matching the filter in one atomic `updateMany` → `{ count }`.
243
+ * No filter only with `{ all: true }` (the scope still applies); an empty or fully skipped
244
+ * filter is `QueryKitError` `empty-filter`. The filter passes the request policy like a read.
245
+ */
246
+ updateWhere(filter: Filter<TDelegate> | AllRows, patch: Partial<Insert<TDelegate>>, options?: {
247
+ returning?: false;
248
+ }): Promise<MutationCount>;
249
+ /** With `returning: { limit }`: `{ count, rows }` from one atomic `updateManyAndReturn`, at most `limit` rows. */
250
+ updateWhere(filter: Filter<TDelegate> | AllRows, patch: Partial<Insert<TDelegate>>, options: {
251
+ returning: {
252
+ limit: number;
253
+ };
254
+ }): Promise<MutationRows<Row<TDelegate>>>;
278
255
  /** Hard-delete one row by id; returns the deleted row (or `undefined`). */
279
256
  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>[]>;
257
+ /** Hard-delete every row matching the filter in one `deleteMany` → `{ count }` (same rules as `updateWhere`). */
258
+ deleteWhere(filter: Filter<TDelegate> | AllRows, options?: {
259
+ returning?: false;
260
+ }): Promise<MutationCount>;
261
+ /** With `returning: { limit }`: `{ count, rows }` — the rows are read just before the delete (not atomic). */
262
+ deleteWhere(filter: Filter<TDelegate> | AllRows, options: {
263
+ returning: {
264
+ limit: number;
265
+ };
266
+ }): Promise<MutationRows<Row<TDelegate>>>;
282
267
  /** Soft-delete (set `deletedAt`); requires a `deletedAt` field. */
283
268
  softDelete(id: Id, idKey?: FieldKey<TDelegate>): Promise<Row<TDelegate> | undefined>;
284
269
  /** Restore a soft-deleted row (`deletedAt = null`). */
@@ -293,6 +278,69 @@ interface Repository<TDelegate = AnyDelegate> {
293
278
  }
294
279
  /** Extends a base repository with custom, model-specific methods. */
295
280
  type RepositoryExtender<TDelegate, TExt extends Record<string, unknown>> = (base: Repository<TDelegate>) => TExt;
281
+
282
+ /**
283
+ * Model metadata — the Prisma counterpart of drizzle's `columns.ts` and
284
+ * mongoose's `fields.ts`.
285
+ *
286
+ * Prisma's `where` is keyed by **model field name** (camelCase), never by the DB
287
+ * column — but a client may well send `created_at`, which drizzle-pg accepts. So
288
+ * each field's `@map`ped column is recorded as an alias and `resolveField`
289
+ * translates it back, keeping the two Postgres adapters interchangeable.
290
+ *
291
+ * Getting the field list without importing the generated client is the other
292
+ * half of the job: this package must typecheck with no generated client present,
293
+ * so metadata is read off the client **instance** at runtime, through a fallback
294
+ * chain.
295
+ */
296
+ /** One model field, normalized across every metadata source. */
297
+ interface FieldMeta {
298
+ name: string;
299
+ /** Prisma type name: `"Int"`, `"String"`, `"DateTime"`, an enum or model name, … */
300
+ type: string;
301
+ kind: "scalar" | "enum" | "object" | "unsupported";
302
+ isId: boolean;
303
+ /** `@updatedAt` — Prisma bumps these itself, so the adapter must not. */
304
+ isUpdatedAt: boolean;
305
+ isList: boolean;
306
+ /** The `@map`ped database column, when it differs from the field name. */
307
+ dbName?: string;
308
+ /** `false` for an optional (nullable) field — Prisma accepts `nulls` in `orderBy` only there. */
309
+ isRequired?: boolean;
310
+ /** The database type (`"Uuid"` for `@db.Uuid`) — from the runtime model when it has one, or the `fields` override. */
311
+ nativeType?: string;
312
+ /** An enum field's values (from the runtime data model), for the cast. */
313
+ enumValues?: readonly string[];
314
+ }
315
+ /** Everything the repository needs to know about one model. */
316
+ interface ModelMeta {
317
+ /** Prisma model name (`"User"`) — used as the diagnostics `source`. */
318
+ name: string;
319
+ fields: Map<string, FieldMeta>;
320
+ /** `@map`ped DB column → Prisma field name, so `created_at` also resolves. */
321
+ byDbName: Map<string, string>;
322
+ /** The `@id` field name; undefined for a composite `@@id`. */
323
+ idField: string | undefined;
324
+ /** Fields that uniquely address a row — one `@id`, or every `@@id` field. */
325
+ primaryKey: string[];
326
+ /** Prisma's name for a composite key (`@@id(name:)`), else the joined fields. */
327
+ primaryKeyName: string | undefined;
328
+ hasDeletedAt: boolean;
329
+ /** Present only when the field exists **and** is not `@updatedAt`-managed. */
330
+ stampUpdatedAt: string | undefined;
331
+ hasCreatedAt: boolean;
332
+ }
333
+ /**
334
+ * Read a model's metadata off a live Prisma client, trying, in order:
335
+ * `_runtimeDataModel` → `delegate.fields` → the caller-supplied `override`.
336
+ *
337
+ * Throws when none of them work — the same reasoning as drizzle's "table not in
338
+ * schema" error: a repository built on unknown metadata would silently skip
339
+ * *every* filter key and return the whole table.
340
+ */
341
+ /** A `fields` override entry: a full field, or a patch of a discovered one by `name` (e.g. `{ name: "age", isRequired: false }`). */
342
+ type FieldOverride = Pick<FieldMeta, "name"> & Partial<FieldMeta>;
343
+
296
344
  /** {@link createRegistry} options — pagination defaults and bounds. */
297
345
  interface RegistryOptions {
298
346
  /** `findList` default page size (core `DEFAULT_PER_PAGE` = 20). */
@@ -321,6 +369,27 @@ interface RegistryOptions {
321
369
  * path: watch with the hook first, flip `strict: true` once the log is clean.
322
370
  */
323
371
  onSkippedCondition?: (info: _querykitjs_core.SkippedCondition) => void;
372
+ /**
373
+ * Server-side request limits (core `DEFAULT_QUERY_LIMITS`: depth 5, conditions 30,
374
+ * array 200, string 256, offset 10 000, sort 3) — enforced even when validation was
375
+ * bypassed. Past one ⇒ `QueryKitError` (`reason: "limit-exceeded"`). A repository's
376
+ * own `limits` win.
377
+ */
378
+ limits?: Partial<_querykitjs_core.QueryLimits>;
379
+ /**
380
+ * Signs cursor tokens (HMAC-SHA256). With it a token that was not
381
+ * issued by this server — or was changed — is `invalid-value`. Without it tokens
382
+ * are unsigned but still checked (scalar values, v2 shape).
383
+ */
384
+ cursorSecret?: string;
385
+ /**
386
+ * The createdAt / deletedAt field names (default `createdAt`, `deletedAt`): the default
387
+ * sort and the default `sortable` use `createdAt`, soft delete uses `deletedAt`.
388
+ */
389
+ timestamps?: {
390
+ createdAt?: string;
391
+ deletedAt?: string;
392
+ };
324
393
  }
325
394
  /**
326
395
  * Options for `registry.repository(model, options)`.
@@ -336,6 +405,12 @@ interface RegistryOptions {
336
405
  * yourself, before handing a write result to a client.
337
406
  */
338
407
  interface RepositoryOptions<TDelegate = AnyDelegate> {
408
+ /**
409
+ * Relations a client may open with `with` (R21): relation → `{ columns, filterable?, nested? }`.
410
+ * `columns` is required (a list or `"*"`); an unlisted relation, column, `where` field or nested
411
+ * `include` is `QueryKitError` `forbidden-key`, `site: "with"`. Without it `with` opens nothing.
412
+ */
413
+ relations?: Relations;
339
414
  /** Constant equality filter added to every read/write (RBAC / multi-tenancy). */
340
415
  scope?: Scope<TDelegate>;
341
416
  /**
@@ -355,7 +430,24 @@ interface RepositoryOptions<TDelegate = AnyDelegate> {
355
430
  * the Prisma client itself (it tries `_runtimeDataModel`, then the public
356
431
  * `delegate.fields`) — e.g. behind an unusual client wrapper.
357
432
  */
358
- fields?: readonly FieldMeta[];
433
+ fields?: readonly FieldOverride[];
434
+ /**
435
+ * Fields a client may filter by, and the operators for each (`"*"` — all). A
436
+ * relation field is `"relation.field"` (`"posts.title": ["="]`). A field not
437
+ * listed ⇒ `reason: "forbidden-key"`, an operator not listed ⇒ `"forbidden-operator"`
438
+ * — a `QueryKitError` whatever `strict` says, before Prisma is called.
439
+ * **When not given** — `allowedColumns` (else the `forcedColumns` keys, else only
440
+ * the `@id` field) with `"="` and `"in"` only. `aggregate` fields come from this list too.
441
+ */
442
+ filterable?: _querykitjs_core.Filterable<FieldKey<TDelegate> | `${string}.${string}`>;
443
+ /**
444
+ * Fields a client may sort (and use as `cursorKey`) by. When not given — the
445
+ * `@id` field and `createdAt` (when the model has it). Anything else ⇒
446
+ * `reason: "forbidden-key"`.
447
+ */
448
+ sortable?: readonly FieldKey<TDelegate>[];
449
+ /** Request limits for this repository — over the registry's `limits`. */
450
+ limits?: Partial<_querykitjs_core.QueryLimits>;
359
451
  }
360
452
  /** DB registry: created once, yields model repositories. */
361
453
  interface Registry<TClient extends AnyClient = AnyClient> {
@@ -447,10 +539,20 @@ interface RepoRuntime {
447
539
  strict: boolean;
448
540
  /** Called for every dropped condition (also when `strict` is off). */
449
541
  onSkippedCondition?: (info: SkippedCondition) => void;
542
+ /** Registry-level request limits; a repository's own `limits` win. */
543
+ limits?: Partial<QueryLimits>;
544
+ /** Signs cursor tokens. */
545
+ cursorSecret?: string;
546
+ /** createdAt / deletedAt field names; default `createdAt`, `deletedAt`. */
547
+ timestamps?: {
548
+ createdAt?: string;
549
+ deletedAt?: string;
550
+ };
450
551
  }
552
+
451
553
  /**
452
554
  * Build a model-scoped repository. `runtime` (client + ambient transaction
453
- * context) comes from the registry, so this file never touches the app's
555
+ * context) comes from the registry, so this module never touches the app's
454
556
  * database handle directly.
455
557
  */
456
558
  declare function buildRepository<TDelegate>(runtime: RepoRuntime, modelKey: string, config?: RepositoryOptions<TDelegate>): Repository<TDelegate>;
@@ -491,4 +593,5 @@ declare function createFilters<TDelegate>(): {
491
593
  not: (node: _querykitjs_core.FilterNode<FieldKey<TDelegate>, RawWhere<TDelegate>>) => _querykitjs_core.NotGroup<FieldKey<TDelegate>, RawWhere<TDelegate>>;
492
594
  };
493
595
 
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 };
596
+ export { buildRepository, createFilters, createRegistry };
597
+ export type { AggregateRow, AggregateSpec, AndGroup, AnyClient, AnyDelegate, ByIdParams, ColumnSelection, CursorMeta, CursorParams, CursorResult, DelegateOf, FieldCondition, FieldKey, FieldMeta, Filter, FilterNode, FilterOperator, FilterScalar, FilterValue, Id, IncludeInput, InfiniteMeta, InfiniteParams, InfiniteResult, Insert, LooseFieldKey, ModelKey, ModelMeta, NotGroup, OffsetMeta, OffsetParams, OffsetResult, OrGroup, QueryParams, RawWhere, Registry, RegistryOptions, RepoRuntime, Repository, RepositoryExtender, RepositoryOptions, ResultFor, Row, Scope, SelectInput, Sort, SortDirection, SortItem, UpsertOptions, WithRelations };