@querykitjs/mongoose 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,287 @@
1
+ import { Types, Model, Connection, ClientSession } from 'mongoose';
2
+ import * as _querykitjs_core from '@querykitjs/core';
3
+ export { f } from '@querykitjs/core';
4
+
5
+ /**
6
+ * QueryKit Mongoose adapter — public types. The DSL/filter/meta types come from
7
+ * `@querykitjs/core` (shared with the Postgres adapter); they are only
8
+ * re-specialized here over a Mongoose document type (`TDoc`), with the raw
9
+ * escape-hatch bound to a native Mongo `FilterQuery`.
10
+ *
11
+ * The public surface mirrors `@querykitjs/drizzle-pg` so the same frontend
12
+ * request contract works regardless of backend.
13
+ *
14
+ * NOTE: Stage 1 exposes only read methods (+ scoped). Writes/upsert/aggregate/
15
+ * transaction land in stage 2.
16
+ */
17
+
18
+ /** Plain (lean) document type — what reads return. */
19
+ type Row<TDoc> = TDoc;
20
+ /**
21
+ * Insertable shape — the document minus auto-managed fields (`_id`, timestamps),
22
+ * with the rest optional (Mongoose enforces `required`/defaults at runtime; that
23
+ * metadata isn't in the doc type, so unlike drizzle-pg we can't mark it here).
24
+ */
25
+ type Insert<TDoc> = Partial<Omit<TDoc, "_id" | "createdAt" | "updatedAt">>;
26
+ /** Value accepted anywhere an id is expected. */
27
+ type Id = string | number | Types.ObjectId;
28
+ /**
29
+ * Raw Mongo predicate fragment — the filter escape hatch (`TRaw`). A plain Mongo
30
+ * query object (kept simple on purpose; Mongoose's `QueryFilter<TDoc>` generic is
31
+ * too invariant to compose cleanly through the core DSL).
32
+ */
33
+ type RawFilter<_TDoc = unknown> = Record<string, unknown>;
34
+ /** Union of a document's field keys. */
35
+ type FieldKey<TDoc> = keyof TDoc & string;
36
+ type FilterOperator = _querykitjs_core.FilterOperator;
37
+ type FilterScalar = _querykitjs_core.FilterScalar;
38
+ type FilterValue = _querykitjs_core.FilterValue;
39
+ type FieldCondition<TDoc = Record<string, unknown>> = _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
40
+ type AndGroup<TDoc = Record<string, unknown>> = _querykitjs_core.AndGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
41
+ type OrGroup<TDoc = Record<string, unknown>> = _querykitjs_core.OrGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
42
+ type NotGroup<TDoc = Record<string, unknown>> = _querykitjs_core.NotGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
43
+ type FilterNode<TDoc = Record<string, unknown>> = _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<TDoc>>;
44
+ type Filter<TDoc = Record<string, unknown>> = _querykitjs_core.Filter<FieldKey<TDoc>, RawFilter<TDoc>>;
45
+ type SortDirection = _querykitjs_core.SortDirection;
46
+ /** One sort field by typed field key (`{ key, direction }`) — autocomplete. */
47
+ type SortItem<TDoc = Record<string, unknown>> = _querykitjs_core.SortItem<FieldKey<TDoc>>;
48
+ /**
49
+ * Sort — **always an array** of `{ key, direction }` (multi-field). This is the
50
+ * exact shape the querykit frontend sends (`@querykitjs/web` decodes its
51
+ * `sortType=-createdAt,id` URL string into it).
52
+ */
53
+ type Sort<TDoc = Record<string, unknown>> = SortItem<TDoc>[];
54
+ type ColumnSelection<TDoc = Record<string, unknown>> = Partial<Record<FieldKey<TDoc>, boolean>>;
55
+ /** Relations to populate (Mongoose `populate`). */
56
+ type WithRelations = Record<string, unknown>;
57
+ interface QueryParams<TDoc = Record<string, unknown>> {
58
+ filter?: Filter<TDoc>;
59
+ sort?: Sort<TDoc>;
60
+ columns?: ColumnSelection<TDoc>;
61
+ with?: WithRelations;
62
+ /** Include soft-deleted docs (schemas with a `deletedAt` path). */
63
+ withDeleted?: boolean;
64
+ }
65
+ /** Equality scope applied to every operation of a scoped repository. */
66
+ type Scope<TDoc = Record<string, unknown>> = Partial<Record<FieldKey<TDoc>, FilterScalar>>;
67
+ interface ByIdParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
68
+ /** Field to match against the id. Defaults to `"id"` (→ `_id`). */
69
+ idKey?: FieldKey<TDoc>;
70
+ }
71
+ /** Options for {@link Repository.upsert} / {@link Repository.upsertMany}. */
72
+ interface UpsertOptions<TDoc = Record<string, unknown>> {
73
+ /** Unique field(s) whose match triggers an update instead of an insert. */
74
+ target: FieldKey<TDoc> | FieldKey<TDoc>[];
75
+ /** Fields to update on conflict. Defaults to the inserted values minus `target`. */
76
+ set?: Partial<Insert<TDoc>>;
77
+ }
78
+ type AggKeys<TDoc> = FieldKey<TDoc> | FieldKey<TDoc>[];
79
+ /** Aggregate query specification (mirrors drizzle-pg). */
80
+ interface AggregateSpec<TDoc = Record<string, unknown>> {
81
+ filter?: Filter<TDoc>;
82
+ /** Group by these field(s); each appears in the output rows. */
83
+ groupBy?: AggKeys<TDoc>;
84
+ /** `$sum:1` → `count`. */
85
+ count?: boolean;
86
+ /** `$sum` → `sum_<field>`. */
87
+ sum?: AggKeys<TDoc>;
88
+ /** `$avg` → `avg_<field>`. */
89
+ avg?: AggKeys<TDoc>;
90
+ /** `$min` → `min_<field>`. */
91
+ min?: AggKeys<TDoc>;
92
+ /** `$max` → `max_<field>`. */
93
+ max?: AggKeys<TDoc>;
94
+ withDeleted?: boolean;
95
+ }
96
+ /** One aggregated result row (group fields + aggregate values). */
97
+ type AggregateRow = Record<string, unknown>;
98
+ interface OffsetParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
99
+ page?: number;
100
+ perPage?: number;
101
+ }
102
+ type OffsetMeta = _querykitjs_core.OffsetMeta;
103
+ interface OffsetResult<T> {
104
+ data: T[];
105
+ meta: OffsetMeta;
106
+ }
107
+ interface InfiniteParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
108
+ limit?: number;
109
+ offset?: number;
110
+ }
111
+ type InfiniteMeta = _querykitjs_core.InfiniteMeta;
112
+ interface InfiniteResult<T> {
113
+ data: T[];
114
+ meta: InfiniteMeta;
115
+ }
116
+ interface CursorParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
117
+ limit?: number;
118
+ cursor?: string | null;
119
+ /** Field the cursor walks over. Defaults to `"id"` (→ `_id`). */
120
+ cursorKey?: FieldKey<TDoc>;
121
+ order?: SortDirection;
122
+ direction?: "forward" | "backward";
123
+ }
124
+ type CursorMeta = _querykitjs_core.CursorMeta;
125
+ interface CursorResult<T> {
126
+ data: T[];
127
+ meta: CursorMeta;
128
+ }
129
+ /** Map of relation field name → the Mongoose model it refers to. */
130
+ type RelationMap = Record<string, Model<any>>;
131
+ type DocOf<M> = M extends Model<infer T> ? T : never;
132
+ /** Resolve a {@link RelationMap} to `{ field: <its document type> }`. */
133
+ type RelationDocs<TRel extends RelationMap> = {
134
+ [K in keyof TRel]: DocOf<TRel[K]>;
135
+ };
136
+ /** Options for `registry.repository(model, options)`. */
137
+ interface RepositoryOptions<TRel extends RelationMap = RelationMap> {
138
+ /** Declare populatable relations (field → model) so `with` types the result. */
139
+ relations?: TRel;
140
+ }
141
+ type TrueKeys<T> = {
142
+ [K in keyof T]: T[K] extends true ? K : never;
143
+ }[keyof T];
144
+ type NarrowColumns<TDoc, TColumns> = TColumns extends ColumnSelection<TDoc> ? ([TrueKeys<TColumns>] extends [never] ? TDoc : Pick<TDoc, TrueKeys<TColumns> & keyof TDoc>) : TDoc;
145
+ /** Relation fields selected in `with` (`true`) that exist in the repo's `TRel`. */
146
+ type PopulatedFields<TRel, TWith> = Pick<TRel, Extract<TrueKeys<TWith>, keyof TRel>>;
147
+ /** Replace populated relation fields (e.g. `author: ObjectId` → `author: IUser`). */
148
+ type ApplyWith<TRow, TRel, TWith> = TWith extends object ? [Extract<TrueKeys<TWith>, keyof TRel>] extends [never] ? TRow : Omit<TRow, keyof PopulatedFields<TRel, TWith>> & PopulatedFields<TRel, TWith> : TRow;
149
+ /** The result row for a given `columns` selection and populated `with`. */
150
+ type Result<TDoc, TRel, TColumns, TWith = undefined> = ApplyWith<NarrowColumns<TDoc, TColumns>, TRel, TWith>;
151
+ /** Params whose `columns`/`with` are typed to the passed selection (drives inference). */
152
+ type Params<TBase, TColumns, TWith> = Omit<TBase, "columns" | "with"> & {
153
+ columns?: TColumns;
154
+ with?: TWith;
155
+ };
156
+ /**
157
+ * Table-scoped repository (stage 1: reads + scoped). Read methods infer their
158
+ * return type from the `columns` argument, mirroring `@querykitjs/drizzle-pg`.
159
+ */
160
+ interface Repository<TDoc = Record<string, unknown>, TRel extends Record<string, unknown> = Record<never, never>> {
161
+ /** The underlying Mongoose model — escape hatch for custom queries. */
162
+ readonly model: Model<TDoc>;
163
+ findAll<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith>[]>;
164
+ findOne<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith> | undefined>;
165
+ findById<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(id: Id, params?: Params<ByIdParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith> | undefined>;
166
+ findList<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<OffsetParams<TDoc>, TColumns, TWith>): Promise<OffsetResult<Result<TDoc, TRel, TColumns, TWith>>>;
167
+ findInfinite<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<InfiniteParams<TDoc>, TColumns, TWith>): Promise<InfiniteResult<Result<TDoc, TRel, TColumns, TWith>>>;
168
+ findCursor<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<CursorParams<TDoc>, TColumns, TWith>): Promise<CursorResult<Result<TDoc, TRel, TColumns, TWith>>>;
169
+ count(filter?: Filter<TDoc>): Promise<number>;
170
+ exists(filter?: Filter<TDoc>): Promise<boolean>;
171
+ /** Create one document and return it. */
172
+ create(values: Insert<TDoc>): Promise<Row<TDoc>>;
173
+ /** Create many documents in one round trip. */
174
+ createMany(values: Insert<TDoc>[]): Promise<Row<TDoc>[]>;
175
+ /** Insert, or update on `target` conflict (`findOneAndUpdate` upsert). */
176
+ upsert(values: Insert<TDoc>, options: UpsertOptions<TDoc>): Promise<Row<TDoc>>;
177
+ /** Bulk upsert (chunked `bulkWrite`). */
178
+ upsertMany(values: Insert<TDoc>[], options: UpsertOptions<TDoc>): Promise<Row<TDoc>[]>;
179
+ /** Update one document by id; returns the updated doc (or undefined). */
180
+ updateById(id: Id, patch: Partial<Insert<TDoc>>, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
181
+ /** Update all documents matching the filter; returns the updated docs. */
182
+ updateWhere(filter: Filter<TDoc>, patch: Partial<Insert<TDoc>>): Promise<Row<TDoc>[]>;
183
+ /** Hard-delete one document by id; returns the deleted doc (or undefined). */
184
+ deleteById(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
185
+ /** Hard-delete all documents matching the filter; returns the deleted docs. */
186
+ deleteWhere(filter: Filter<TDoc>): Promise<Row<TDoc>[]>;
187
+ /** Soft-delete (set `deletedAt`); requires a `deletedAt` schema path. */
188
+ softDelete(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
189
+ /** Restore a soft-deleted document (`deletedAt = null`). */
190
+ restore(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
191
+ /** Aggregate: `count`/`sum`/`avg`/`min`/`max` with optional `groupBy`. */
192
+ aggregate(spec: AggregateSpec<TDoc>): Promise<AggregateRow[]>;
193
+ scoped(scope: Scope<TDoc>): Repository<TDoc, TRel>;
194
+ }
195
+ /** {@link createRegistry} options — pagination defaults. */
196
+ interface RegistryOptions {
197
+ /** `findList` default page size (core `DEFAULT_PER_PAGE` = 20). */
198
+ defaultPerPage?: number;
199
+ /** `findInfinite`/`findCursor` default limit (core `DEFAULT_LIMIT` = 20). */
200
+ defaultLimit?: number;
201
+ }
202
+ /** Extends a base repository with custom, model-specific methods (drizzle-pg style). */
203
+ type RepositoryExtender<TDoc, TRel extends Record<string, unknown>, TExt extends Record<string, unknown>> = (base: Repository<TDoc, TRel>) => TExt;
204
+ /** DB registry: created once, yields model repositories. */
205
+ interface Registry {
206
+ /** Repository for a model. */
207
+ repository<TDoc>(model: Model<TDoc>): Repository<TDoc>;
208
+ /** …extended with custom methods (2nd-arg extender fn — same as drizzle-pg). */
209
+ repository<TDoc, TExt extends Record<string, unknown>>(model: Model<TDoc>, extend: RepositoryExtender<TDoc, Record<never, never>, TExt>): Repository<TDoc> & TExt;
210
+ /** …with typed relations so populated `with` fields are typed. */
211
+ repository<TDoc, TRel extends RelationMap>(model: Model<TDoc>, options: RepositoryOptions<TRel>): Repository<TDoc, RelationDocs<TRel>>;
212
+ /** …with both typed relations and custom methods. */
213
+ repository<TDoc, TRel extends RelationMap, TExt extends Record<string, unknown>>(model: Model<TDoc>, options: RepositoryOptions<TRel>, extend: RepositoryExtender<TDoc, RelationDocs<TRel>, TExt>): Repository<TDoc, RelationDocs<TRel>> & TExt;
214
+ /** Runs `fn` inside a MongoDB transaction (requires a replica set). */
215
+ transaction<T>(fn: () => Promise<T>): Promise<T>;
216
+ }
217
+
218
+ /**
219
+ * Create the DB **registry** once. It holds the Mongoose connection (for
220
+ * transactions) and yields a {@link Repository} per model. Transactions flow
221
+ * ambiently via AsyncLocalStorage — repositories used inside
222
+ * `registry.transaction(...)` automatically run on that session.
223
+ *
224
+ * @param connection - the Mongoose `Connection` the models belong to.
225
+ * @param options - `defaultPerPage` (findList) / `defaultLimit` (infinite/cursor);
226
+ * defaults to `@querykitjs/core`'s `DEFAULT_PER_PAGE` / `DEFAULT_LIMIT` (20).
227
+ * @example
228
+ * ```ts
229
+ * const registry = createRegistry(mongoose.connection);
230
+ * const usersRepo = registry.repository(User);
231
+ * ```
232
+ */
233
+ declare function createRegistry(connection: Connection, options?: RegistryOptions): Registry;
234
+
235
+ /** Registry-level runtime injected into every repository. */
236
+ interface RepoRuntime {
237
+ /** Active transaction session, if any. */
238
+ getSession: () => ClientSession | undefined;
239
+ defaultPerPage: number;
240
+ defaultLimit: number;
241
+ }
242
+ /** Per-repository configuration (scope). */
243
+ interface RepoConfig<TDoc> {
244
+ scope?: Scope<TDoc>;
245
+ }
246
+ /**
247
+ * Build a model-scoped repository. `runtime` (session getter + defaults) comes
248
+ * from the registry, so this file never touches the connection directly.
249
+ */
250
+ declare function buildRepository<TDoc>(runtime: RepoRuntime, model: Model<TDoc>, config?: RepoConfig<TDoc>): Repository<TDoc>;
251
+
252
+ /**
253
+ * Typed, ergonomic filter builders for a document type (field-name autocomplete).
254
+ * A thin wrapper over `@querykitjs/core`'s builder — the key type is the model's
255
+ * field key, the raw escape-hatch is a native Mongo `QueryFilter`.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * const uf = createFilters<IUser>();
260
+ * await usersRepo.findAll({ filter: uf.and(uf.eq("status", "active"), uf.gte("age", 18)) });
261
+ * ```
262
+ */
263
+ declare function createFilters<TDoc>(): {
264
+ eq: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
265
+ ne: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
266
+ gt: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
267
+ gte: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
268
+ lt: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
269
+ lte: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
270
+ contains: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
271
+ startsWith: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
272
+ endsWith: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
273
+ like: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
274
+ ilike: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
275
+ notLike: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
276
+ in: (key: FieldKey<TDoc>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
277
+ notIn: (key: FieldKey<TDoc>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
278
+ between: (key: FieldKey<TDoc>, min: _querykitjs_core.FilterScalar | Date, max: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
279
+ range: (key: FieldKey<TDoc>, from: _querykitjs_core.FilterScalar | Date, to: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>[];
280
+ isNull: (key: FieldKey<TDoc>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
281
+ isNotNull: (key: FieldKey<TDoc>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
282
+ and: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>[]) => _querykitjs_core.AndGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
283
+ or: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>[]) => _querykitjs_core.OrGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
284
+ not: (node: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>) => _querykitjs_core.NotGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
285
+ };
286
+
287
+ export { type AggregateRow, type AggregateSpec, type AndGroup, type ByIdParams, type ColumnSelection, type CursorMeta, type CursorParams, type CursorResult, type FieldCondition, type FieldKey, type Filter, type FilterNode, type FilterOperator, type FilterScalar, type FilterValue, type Id, type InfiniteMeta, type InfiniteParams, type InfiniteResult, type Insert, type NotGroup, type OffsetMeta, type OffsetParams, type OffsetResult, type OrGroup, type QueryParams, type RawFilter, type Registry, type RegistryOptions, type RelationDocs, type RelationMap, type RepoConfig, type RepoRuntime, type Repository, type RepositoryExtender, type RepositoryOptions, type Result, type Row, type Scope, type Sort, type SortDirection, type SortItem, type UpsertOptions, type WithRelations, buildRepository, createFilters, createRegistry };
@@ -0,0 +1,287 @@
1
+ import { Types, Model, Connection, ClientSession } from 'mongoose';
2
+ import * as _querykitjs_core from '@querykitjs/core';
3
+ export { f } from '@querykitjs/core';
4
+
5
+ /**
6
+ * QueryKit Mongoose adapter — public types. The DSL/filter/meta types come from
7
+ * `@querykitjs/core` (shared with the Postgres adapter); they are only
8
+ * re-specialized here over a Mongoose document type (`TDoc`), with the raw
9
+ * escape-hatch bound to a native Mongo `FilterQuery`.
10
+ *
11
+ * The public surface mirrors `@querykitjs/drizzle-pg` so the same frontend
12
+ * request contract works regardless of backend.
13
+ *
14
+ * NOTE: Stage 1 exposes only read methods (+ scoped). Writes/upsert/aggregate/
15
+ * transaction land in stage 2.
16
+ */
17
+
18
+ /** Plain (lean) document type — what reads return. */
19
+ type Row<TDoc> = TDoc;
20
+ /**
21
+ * Insertable shape — the document minus auto-managed fields (`_id`, timestamps),
22
+ * with the rest optional (Mongoose enforces `required`/defaults at runtime; that
23
+ * metadata isn't in the doc type, so unlike drizzle-pg we can't mark it here).
24
+ */
25
+ type Insert<TDoc> = Partial<Omit<TDoc, "_id" | "createdAt" | "updatedAt">>;
26
+ /** Value accepted anywhere an id is expected. */
27
+ type Id = string | number | Types.ObjectId;
28
+ /**
29
+ * Raw Mongo predicate fragment — the filter escape hatch (`TRaw`). A plain Mongo
30
+ * query object (kept simple on purpose; Mongoose's `QueryFilter<TDoc>` generic is
31
+ * too invariant to compose cleanly through the core DSL).
32
+ */
33
+ type RawFilter<_TDoc = unknown> = Record<string, unknown>;
34
+ /** Union of a document's field keys. */
35
+ type FieldKey<TDoc> = keyof TDoc & string;
36
+ type FilterOperator = _querykitjs_core.FilterOperator;
37
+ type FilterScalar = _querykitjs_core.FilterScalar;
38
+ type FilterValue = _querykitjs_core.FilterValue;
39
+ type FieldCondition<TDoc = Record<string, unknown>> = _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
40
+ type AndGroup<TDoc = Record<string, unknown>> = _querykitjs_core.AndGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
41
+ type OrGroup<TDoc = Record<string, unknown>> = _querykitjs_core.OrGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
42
+ type NotGroup<TDoc = Record<string, unknown>> = _querykitjs_core.NotGroup<FieldKey<TDoc>, RawFilter<TDoc>>;
43
+ type FilterNode<TDoc = Record<string, unknown>> = _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<TDoc>>;
44
+ type Filter<TDoc = Record<string, unknown>> = _querykitjs_core.Filter<FieldKey<TDoc>, RawFilter<TDoc>>;
45
+ type SortDirection = _querykitjs_core.SortDirection;
46
+ /** One sort field by typed field key (`{ key, direction }`) — autocomplete. */
47
+ type SortItem<TDoc = Record<string, unknown>> = _querykitjs_core.SortItem<FieldKey<TDoc>>;
48
+ /**
49
+ * Sort — **always an array** of `{ key, direction }` (multi-field). This is the
50
+ * exact shape the querykit frontend sends (`@querykitjs/web` decodes its
51
+ * `sortType=-createdAt,id` URL string into it).
52
+ */
53
+ type Sort<TDoc = Record<string, unknown>> = SortItem<TDoc>[];
54
+ type ColumnSelection<TDoc = Record<string, unknown>> = Partial<Record<FieldKey<TDoc>, boolean>>;
55
+ /** Relations to populate (Mongoose `populate`). */
56
+ type WithRelations = Record<string, unknown>;
57
+ interface QueryParams<TDoc = Record<string, unknown>> {
58
+ filter?: Filter<TDoc>;
59
+ sort?: Sort<TDoc>;
60
+ columns?: ColumnSelection<TDoc>;
61
+ with?: WithRelations;
62
+ /** Include soft-deleted docs (schemas with a `deletedAt` path). */
63
+ withDeleted?: boolean;
64
+ }
65
+ /** Equality scope applied to every operation of a scoped repository. */
66
+ type Scope<TDoc = Record<string, unknown>> = Partial<Record<FieldKey<TDoc>, FilterScalar>>;
67
+ interface ByIdParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
68
+ /** Field to match against the id. Defaults to `"id"` (→ `_id`). */
69
+ idKey?: FieldKey<TDoc>;
70
+ }
71
+ /** Options for {@link Repository.upsert} / {@link Repository.upsertMany}. */
72
+ interface UpsertOptions<TDoc = Record<string, unknown>> {
73
+ /** Unique field(s) whose match triggers an update instead of an insert. */
74
+ target: FieldKey<TDoc> | FieldKey<TDoc>[];
75
+ /** Fields to update on conflict. Defaults to the inserted values minus `target`. */
76
+ set?: Partial<Insert<TDoc>>;
77
+ }
78
+ type AggKeys<TDoc> = FieldKey<TDoc> | FieldKey<TDoc>[];
79
+ /** Aggregate query specification (mirrors drizzle-pg). */
80
+ interface AggregateSpec<TDoc = Record<string, unknown>> {
81
+ filter?: Filter<TDoc>;
82
+ /** Group by these field(s); each appears in the output rows. */
83
+ groupBy?: AggKeys<TDoc>;
84
+ /** `$sum:1` → `count`. */
85
+ count?: boolean;
86
+ /** `$sum` → `sum_<field>`. */
87
+ sum?: AggKeys<TDoc>;
88
+ /** `$avg` → `avg_<field>`. */
89
+ avg?: AggKeys<TDoc>;
90
+ /** `$min` → `min_<field>`. */
91
+ min?: AggKeys<TDoc>;
92
+ /** `$max` → `max_<field>`. */
93
+ max?: AggKeys<TDoc>;
94
+ withDeleted?: boolean;
95
+ }
96
+ /** One aggregated result row (group fields + aggregate values). */
97
+ type AggregateRow = Record<string, unknown>;
98
+ interface OffsetParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
99
+ page?: number;
100
+ perPage?: number;
101
+ }
102
+ type OffsetMeta = _querykitjs_core.OffsetMeta;
103
+ interface OffsetResult<T> {
104
+ data: T[];
105
+ meta: OffsetMeta;
106
+ }
107
+ interface InfiniteParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
108
+ limit?: number;
109
+ offset?: number;
110
+ }
111
+ type InfiniteMeta = _querykitjs_core.InfiniteMeta;
112
+ interface InfiniteResult<T> {
113
+ data: T[];
114
+ meta: InfiniteMeta;
115
+ }
116
+ interface CursorParams<TDoc = Record<string, unknown>> extends QueryParams<TDoc> {
117
+ limit?: number;
118
+ cursor?: string | null;
119
+ /** Field the cursor walks over. Defaults to `"id"` (→ `_id`). */
120
+ cursorKey?: FieldKey<TDoc>;
121
+ order?: SortDirection;
122
+ direction?: "forward" | "backward";
123
+ }
124
+ type CursorMeta = _querykitjs_core.CursorMeta;
125
+ interface CursorResult<T> {
126
+ data: T[];
127
+ meta: CursorMeta;
128
+ }
129
+ /** Map of relation field name → the Mongoose model it refers to. */
130
+ type RelationMap = Record<string, Model<any>>;
131
+ type DocOf<M> = M extends Model<infer T> ? T : never;
132
+ /** Resolve a {@link RelationMap} to `{ field: <its document type> }`. */
133
+ type RelationDocs<TRel extends RelationMap> = {
134
+ [K in keyof TRel]: DocOf<TRel[K]>;
135
+ };
136
+ /** Options for `registry.repository(model, options)`. */
137
+ interface RepositoryOptions<TRel extends RelationMap = RelationMap> {
138
+ /** Declare populatable relations (field → model) so `with` types the result. */
139
+ relations?: TRel;
140
+ }
141
+ type TrueKeys<T> = {
142
+ [K in keyof T]: T[K] extends true ? K : never;
143
+ }[keyof T];
144
+ type NarrowColumns<TDoc, TColumns> = TColumns extends ColumnSelection<TDoc> ? ([TrueKeys<TColumns>] extends [never] ? TDoc : Pick<TDoc, TrueKeys<TColumns> & keyof TDoc>) : TDoc;
145
+ /** Relation fields selected in `with` (`true`) that exist in the repo's `TRel`. */
146
+ type PopulatedFields<TRel, TWith> = Pick<TRel, Extract<TrueKeys<TWith>, keyof TRel>>;
147
+ /** Replace populated relation fields (e.g. `author: ObjectId` → `author: IUser`). */
148
+ type ApplyWith<TRow, TRel, TWith> = TWith extends object ? [Extract<TrueKeys<TWith>, keyof TRel>] extends [never] ? TRow : Omit<TRow, keyof PopulatedFields<TRel, TWith>> & PopulatedFields<TRel, TWith> : TRow;
149
+ /** The result row for a given `columns` selection and populated `with`. */
150
+ type Result<TDoc, TRel, TColumns, TWith = undefined> = ApplyWith<NarrowColumns<TDoc, TColumns>, TRel, TWith>;
151
+ /** Params whose `columns`/`with` are typed to the passed selection (drives inference). */
152
+ type Params<TBase, TColumns, TWith> = Omit<TBase, "columns" | "with"> & {
153
+ columns?: TColumns;
154
+ with?: TWith;
155
+ };
156
+ /**
157
+ * Table-scoped repository (stage 1: reads + scoped). Read methods infer their
158
+ * return type from the `columns` argument, mirroring `@querykitjs/drizzle-pg`.
159
+ */
160
+ interface Repository<TDoc = Record<string, unknown>, TRel extends Record<string, unknown> = Record<never, never>> {
161
+ /** The underlying Mongoose model — escape hatch for custom queries. */
162
+ readonly model: Model<TDoc>;
163
+ findAll<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith>[]>;
164
+ findOne<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<QueryParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith> | undefined>;
165
+ findById<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(id: Id, params?: Params<ByIdParams<TDoc>, TColumns, TWith>): Promise<Result<TDoc, TRel, TColumns, TWith> | undefined>;
166
+ findList<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<OffsetParams<TDoc>, TColumns, TWith>): Promise<OffsetResult<Result<TDoc, TRel, TColumns, TWith>>>;
167
+ findInfinite<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<InfiniteParams<TDoc>, TColumns, TWith>): Promise<InfiniteResult<Result<TDoc, TRel, TColumns, TWith>>>;
168
+ findCursor<const TColumns extends ColumnSelection<TDoc> | undefined = undefined, const TWith extends WithRelations | undefined = undefined>(params?: Params<CursorParams<TDoc>, TColumns, TWith>): Promise<CursorResult<Result<TDoc, TRel, TColumns, TWith>>>;
169
+ count(filter?: Filter<TDoc>): Promise<number>;
170
+ exists(filter?: Filter<TDoc>): Promise<boolean>;
171
+ /** Create one document and return it. */
172
+ create(values: Insert<TDoc>): Promise<Row<TDoc>>;
173
+ /** Create many documents in one round trip. */
174
+ createMany(values: Insert<TDoc>[]): Promise<Row<TDoc>[]>;
175
+ /** Insert, or update on `target` conflict (`findOneAndUpdate` upsert). */
176
+ upsert(values: Insert<TDoc>, options: UpsertOptions<TDoc>): Promise<Row<TDoc>>;
177
+ /** Bulk upsert (chunked `bulkWrite`). */
178
+ upsertMany(values: Insert<TDoc>[], options: UpsertOptions<TDoc>): Promise<Row<TDoc>[]>;
179
+ /** Update one document by id; returns the updated doc (or undefined). */
180
+ updateById(id: Id, patch: Partial<Insert<TDoc>>, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
181
+ /** Update all documents matching the filter; returns the updated docs. */
182
+ updateWhere(filter: Filter<TDoc>, patch: Partial<Insert<TDoc>>): Promise<Row<TDoc>[]>;
183
+ /** Hard-delete one document by id; returns the deleted doc (or undefined). */
184
+ deleteById(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
185
+ /** Hard-delete all documents matching the filter; returns the deleted docs. */
186
+ deleteWhere(filter: Filter<TDoc>): Promise<Row<TDoc>[]>;
187
+ /** Soft-delete (set `deletedAt`); requires a `deletedAt` schema path. */
188
+ softDelete(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
189
+ /** Restore a soft-deleted document (`deletedAt = null`). */
190
+ restore(id: Id, idKey?: FieldKey<TDoc>): Promise<Row<TDoc> | undefined>;
191
+ /** Aggregate: `count`/`sum`/`avg`/`min`/`max` with optional `groupBy`. */
192
+ aggregate(spec: AggregateSpec<TDoc>): Promise<AggregateRow[]>;
193
+ scoped(scope: Scope<TDoc>): Repository<TDoc, TRel>;
194
+ }
195
+ /** {@link createRegistry} options — pagination defaults. */
196
+ interface RegistryOptions {
197
+ /** `findList` default page size (core `DEFAULT_PER_PAGE` = 20). */
198
+ defaultPerPage?: number;
199
+ /** `findInfinite`/`findCursor` default limit (core `DEFAULT_LIMIT` = 20). */
200
+ defaultLimit?: number;
201
+ }
202
+ /** Extends a base repository with custom, model-specific methods (drizzle-pg style). */
203
+ type RepositoryExtender<TDoc, TRel extends Record<string, unknown>, TExt extends Record<string, unknown>> = (base: Repository<TDoc, TRel>) => TExt;
204
+ /** DB registry: created once, yields model repositories. */
205
+ interface Registry {
206
+ /** Repository for a model. */
207
+ repository<TDoc>(model: Model<TDoc>): Repository<TDoc>;
208
+ /** …extended with custom methods (2nd-arg extender fn — same as drizzle-pg). */
209
+ repository<TDoc, TExt extends Record<string, unknown>>(model: Model<TDoc>, extend: RepositoryExtender<TDoc, Record<never, never>, TExt>): Repository<TDoc> & TExt;
210
+ /** …with typed relations so populated `with` fields are typed. */
211
+ repository<TDoc, TRel extends RelationMap>(model: Model<TDoc>, options: RepositoryOptions<TRel>): Repository<TDoc, RelationDocs<TRel>>;
212
+ /** …with both typed relations and custom methods. */
213
+ repository<TDoc, TRel extends RelationMap, TExt extends Record<string, unknown>>(model: Model<TDoc>, options: RepositoryOptions<TRel>, extend: RepositoryExtender<TDoc, RelationDocs<TRel>, TExt>): Repository<TDoc, RelationDocs<TRel>> & TExt;
214
+ /** Runs `fn` inside a MongoDB transaction (requires a replica set). */
215
+ transaction<T>(fn: () => Promise<T>): Promise<T>;
216
+ }
217
+
218
+ /**
219
+ * Create the DB **registry** once. It holds the Mongoose connection (for
220
+ * transactions) and yields a {@link Repository} per model. Transactions flow
221
+ * ambiently via AsyncLocalStorage — repositories used inside
222
+ * `registry.transaction(...)` automatically run on that session.
223
+ *
224
+ * @param connection - the Mongoose `Connection` the models belong to.
225
+ * @param options - `defaultPerPage` (findList) / `defaultLimit` (infinite/cursor);
226
+ * defaults to `@querykitjs/core`'s `DEFAULT_PER_PAGE` / `DEFAULT_LIMIT` (20).
227
+ * @example
228
+ * ```ts
229
+ * const registry = createRegistry(mongoose.connection);
230
+ * const usersRepo = registry.repository(User);
231
+ * ```
232
+ */
233
+ declare function createRegistry(connection: Connection, options?: RegistryOptions): Registry;
234
+
235
+ /** Registry-level runtime injected into every repository. */
236
+ interface RepoRuntime {
237
+ /** Active transaction session, if any. */
238
+ getSession: () => ClientSession | undefined;
239
+ defaultPerPage: number;
240
+ defaultLimit: number;
241
+ }
242
+ /** Per-repository configuration (scope). */
243
+ interface RepoConfig<TDoc> {
244
+ scope?: Scope<TDoc>;
245
+ }
246
+ /**
247
+ * Build a model-scoped repository. `runtime` (session getter + defaults) comes
248
+ * from the registry, so this file never touches the connection directly.
249
+ */
250
+ declare function buildRepository<TDoc>(runtime: RepoRuntime, model: Model<TDoc>, config?: RepoConfig<TDoc>): Repository<TDoc>;
251
+
252
+ /**
253
+ * Typed, ergonomic filter builders for a document type (field-name autocomplete).
254
+ * A thin wrapper over `@querykitjs/core`'s builder — the key type is the model's
255
+ * field key, the raw escape-hatch is a native Mongo `QueryFilter`.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * const uf = createFilters<IUser>();
260
+ * await usersRepo.findAll({ filter: uf.and(uf.eq("status", "active"), uf.gte("age", 18)) });
261
+ * ```
262
+ */
263
+ declare function createFilters<TDoc>(): {
264
+ eq: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
265
+ ne: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
266
+ gt: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
267
+ gte: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
268
+ lt: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
269
+ lte: (key: FieldKey<TDoc>, value: _querykitjs_core.FilterValue) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
270
+ contains: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
271
+ startsWith: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
272
+ endsWith: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
273
+ like: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
274
+ ilike: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
275
+ notLike: (key: FieldKey<TDoc>, value: string) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
276
+ in: (key: FieldKey<TDoc>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
277
+ notIn: (key: FieldKey<TDoc>, value: Array<string | number>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
278
+ between: (key: FieldKey<TDoc>, min: _querykitjs_core.FilterScalar | Date, max: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
279
+ range: (key: FieldKey<TDoc>, from: _querykitjs_core.FilterScalar | Date, to: _querykitjs_core.FilterScalar | Date) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>[];
280
+ isNull: (key: FieldKey<TDoc>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
281
+ isNotNull: (key: FieldKey<TDoc>) => _querykitjs_core.FieldCondition<FieldKey<TDoc>>;
282
+ and: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>[]) => _querykitjs_core.AndGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
283
+ or: (...nodes: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>[]) => _querykitjs_core.OrGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
284
+ not: (node: _querykitjs_core.FilterNode<FieldKey<TDoc>, RawFilter<_TDoc>>) => _querykitjs_core.NotGroup<FieldKey<TDoc>, RawFilter<_TDoc>>;
285
+ };
286
+
287
+ export { type AggregateRow, type AggregateSpec, type AndGroup, type ByIdParams, type ColumnSelection, type CursorMeta, type CursorParams, type CursorResult, type FieldCondition, type FieldKey, type Filter, type FilterNode, type FilterOperator, type FilterScalar, type FilterValue, type Id, type InfiniteMeta, type InfiniteParams, type InfiniteResult, type Insert, type NotGroup, type OffsetMeta, type OffsetParams, type OffsetResult, type OrGroup, type QueryParams, type RawFilter, type Registry, type RegistryOptions, type RelationDocs, type RelationMap, type RepoConfig, type RepoRuntime, type Repository, type RepositoryExtender, type RepositoryOptions, type Result, type Row, type Scope, type Sort, type SortDirection, type SortItem, type UpsertOptions, type WithRelations, buildRepository, createFilters, createRegistry };