@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.
- package/LICENSE +21 -0
- package/README.en.md +363 -0
- package/README.md +362 -0
- package/dist/index.cjs +844 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +494 -0
- package/dist/index.d.ts +494 -0
- package/dist/index.js +837 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
package/dist/index.d.cts
ADDED
|
@@ -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 };
|