@classytic/mongokit 3.2.0 → 3.2.2

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.
Files changed (48) hide show
  1. package/README.md +470 -193
  2. package/dist/actions/index.d.mts +9 -0
  3. package/dist/actions/index.mjs +15 -0
  4. package/dist/aggregate-BAi4Do-X.mjs +767 -0
  5. package/dist/aggregate-CCHI7F51.d.mts +269 -0
  6. package/dist/ai/index.d.mts +125 -0
  7. package/dist/ai/index.mjs +203 -0
  8. package/dist/cache-keys-C8Z9B5sw.mjs +204 -0
  9. package/dist/chunk-DQk6qfdC.mjs +18 -0
  10. package/dist/create-BuO6xt0v.mjs +55 -0
  11. package/dist/custom-id.plugin-B_zIs6gE.mjs +1818 -0
  12. package/dist/custom-id.plugin-BzZI4gnE.d.mts +893 -0
  13. package/dist/index.d.mts +1012 -0
  14. package/dist/index.mjs +1906 -0
  15. package/dist/limits-DsNeCx4D.mjs +299 -0
  16. package/dist/logger-D8ily-PP.mjs +51 -0
  17. package/dist/mongooseToJsonSchema-COdDEkIJ.mjs +317 -0
  18. package/dist/{mongooseToJsonSchema-CaRF_bCN.d.ts → mongooseToJsonSchema-Wbvjfwkn.d.mts} +16 -89
  19. package/dist/pagination/PaginationEngine.d.mts +93 -0
  20. package/dist/pagination/PaginationEngine.mjs +196 -0
  21. package/dist/plugins/index.d.mts +3 -0
  22. package/dist/plugins/index.mjs +3 -0
  23. package/dist/types-D-gploPr.d.mts +1241 -0
  24. package/dist/utils/{index.d.ts → index.d.mts} +14 -21
  25. package/dist/utils/index.mjs +5 -0
  26. package/package.json +21 -21
  27. package/dist/actions/index.d.ts +0 -3
  28. package/dist/actions/index.js +0 -5
  29. package/dist/ai/index.d.ts +0 -175
  30. package/dist/ai/index.js +0 -206
  31. package/dist/chunks/chunk-2ZN65ZOP.js +0 -93
  32. package/dist/chunks/chunk-44KXLGPO.js +0 -388
  33. package/dist/chunks/chunk-DEVXDBRL.js +0 -1226
  34. package/dist/chunks/chunk-I7CWNAJB.js +0 -46
  35. package/dist/chunks/chunk-JWUAVZ3L.js +0 -8
  36. package/dist/chunks/chunk-UE2IEXZJ.js +0 -306
  37. package/dist/chunks/chunk-URLJFIR7.js +0 -22
  38. package/dist/chunks/chunk-VWKIKZYF.js +0 -737
  39. package/dist/chunks/chunk-WSFCRVEQ.js +0 -7
  40. package/dist/index-BDn5fSTE.d.ts +0 -516
  41. package/dist/index.d.ts +0 -1422
  42. package/dist/index.js +0 -1893
  43. package/dist/pagination/PaginationEngine.d.ts +0 -117
  44. package/dist/pagination/PaginationEngine.js +0 -3
  45. package/dist/plugins/index.d.ts +0 -922
  46. package/dist/plugins/index.js +0 -6
  47. package/dist/types-Jni1KgkP.d.ts +0 -780
  48. package/dist/utils/index.js +0 -5
@@ -0,0 +1,1241 @@
1
+ import * as mongoose$1 from "mongoose";
2
+ import { ClientSession, Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
3
+
4
+ //#region src/query/LookupBuilder.d.ts
5
+ interface LookupOptions {
6
+ /** Collection to join with */
7
+ from: string;
8
+ /** Field from the input documents */
9
+ localField: string;
10
+ /** Field from the documents of the "from" collection */
11
+ foreignField: string;
12
+ /** Name of the new array field to add to the input documents */
13
+ as?: string;
14
+ /** Whether to unwrap array to single object */
15
+ single?: boolean;
16
+ /** Additional pipeline to run on the joined collection */
17
+ pipeline?: PipelineStage[];
18
+ /** Optional let variables for pipeline */
19
+ let?: Record<string, string>;
20
+ /** Query filter to apply before join (legacy, for aggregate.ts compatibility) */
21
+ query?: Record<string, unknown>;
22
+ /** Query options (legacy, for aggregate.ts compatibility) */
23
+ options?: {
24
+ session?: ClientSession;
25
+ };
26
+ /** Sanitize pipeline stages (default: true). Set false only for trusted server-side pipelines */
27
+ sanitize?: boolean;
28
+ }
29
+ /**
30
+ * Fluent builder for MongoDB $lookup aggregation stage
31
+ * Optimized for custom field joins at scale
32
+ */
33
+ declare class LookupBuilder {
34
+ private options;
35
+ constructor(from?: string);
36
+ /**
37
+ * Set the collection to join with
38
+ */
39
+ from(collection: string): this;
40
+ /**
41
+ * Set the local field (source collection)
42
+ * IMPORTANT: This field should be indexed for optimal performance
43
+ */
44
+ localField(field: string): this;
45
+ /**
46
+ * Set the foreign field (target collection)
47
+ * IMPORTANT: This field should be indexed (preferably unique) for optimal performance
48
+ */
49
+ foreignField(field: string): this;
50
+ /**
51
+ * Set the output field name
52
+ * Defaults to the collection name if not specified
53
+ */
54
+ as(fieldName: string): this;
55
+ /**
56
+ * Mark this lookup as returning a single document
57
+ * Automatically unwraps the array result to a single object or null
58
+ */
59
+ single(isSingle?: boolean): this;
60
+ /**
61
+ * Add a pipeline to filter/transform joined documents
62
+ * Useful for filtering, sorting, or limiting joined results
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * lookup.pipeline([
67
+ * { $match: { status: 'active' } },
68
+ * { $sort: { priority: -1 } },
69
+ * { $limit: 5 }
70
+ * ]);
71
+ * ```
72
+ */
73
+ pipeline(stages: PipelineStage[]): this;
74
+ /**
75
+ * Set let variables for use in pipeline
76
+ * Allows referencing local document fields in the pipeline
77
+ */
78
+ let(variables: Record<string, string>): this;
79
+ /**
80
+ * Build the $lookup aggregation stage(s)
81
+ * Returns an array of pipeline stages including $lookup and optional $unwind
82
+ *
83
+ * IMPORTANT: MongoDB $lookup has two mutually exclusive forms:
84
+ * 1. Simple form: { from, localField, foreignField, as }
85
+ * 2. Pipeline form: { from, let, pipeline, as }
86
+ *
87
+ * When pipeline or let is specified, we use the pipeline form.
88
+ * Otherwise, we use the simpler localField/foreignField form.
89
+ */
90
+ build(): PipelineStage[];
91
+ /**
92
+ * Build and return only the $lookup stage (without $unwind)
93
+ * Useful when you want to handle unwrapping yourself
94
+ */
95
+ buildLookupOnly(): PipelineStage.Lookup;
96
+ /**
97
+ * Static helper: Create a simple lookup in one line
98
+ */
99
+ static simple(from: string, localField: string, foreignField: string, options?: {
100
+ as?: string;
101
+ single?: boolean;
102
+ }): PipelineStage[];
103
+ /**
104
+ * Static helper: Create multiple lookups at once
105
+ *
106
+ * @example
107
+ * ```typescript
108
+ * const pipeline = LookupBuilder.multiple([
109
+ * { from: 'departments', localField: 'deptSlug', foreignField: 'slug', single: true },
110
+ * { from: 'managers', localField: 'managerId', foreignField: '_id', single: true }
111
+ * ]);
112
+ * ```
113
+ */
114
+ static multiple(lookups: LookupOptions[]): PipelineStage[];
115
+ /**
116
+ * Static helper: Create a nested lookup (lookup within lookup)
117
+ * Useful for multi-level joins like Order -> Product -> Category
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * // Join orders with products, then products with categories
122
+ * const pipeline = LookupBuilder.nested([
123
+ * { from: 'products', localField: 'productSku', foreignField: 'sku', as: 'product', single: true },
124
+ * { from: 'categories', localField: 'product.categorySlug', foreignField: 'slug', as: 'product.category', single: true }
125
+ * ]);
126
+ * ```
127
+ */
128
+ static nested(lookups: LookupOptions[]): PipelineStage[];
129
+ /**
130
+ * Sanitize pipeline stages by blocking dangerous stages and operators.
131
+ * Used internally by build() and available for external use (e.g., aggregate.ts).
132
+ */
133
+ static sanitizePipeline(stages: PipelineStage[]): PipelineStage[];
134
+ /**
135
+ * Recursively remove dangerous operators from an expression object.
136
+ */
137
+ private static _sanitizeDeep;
138
+ }
139
+ //#endregion
140
+ //#region src/types/controller.types.d.ts
141
+ /**
142
+ * Framework-agnostic request context
143
+ *
144
+ * Extract this from your framework's request object:
145
+ * - Express: { query: req.query, body: req.body, params: req.params, user: req.user }
146
+ * - Fastify: { query: request.query, body: request.body, params: request.params, user: request.user }
147
+ * - Next.js: { query: searchParams, body: await request.json(), params: params, user: await getUser() }
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * // Express
152
+ * const context: IRequestContext = {
153
+ * query: req.query,
154
+ * body: req.body,
155
+ * params: req.params,
156
+ * user: req.user,
157
+ * context: { organizationId: req.headers['x-org-id'] }
158
+ * };
159
+ *
160
+ * // Fastify
161
+ * const context: IRequestContext = {
162
+ * query: request.query,
163
+ * body: request.body,
164
+ * params: request.params,
165
+ * user: request.user,
166
+ * };
167
+ *
168
+ * // Next.js App Router
169
+ * const context: IRequestContext = {
170
+ * query: Object.fromEntries(request.nextUrl.searchParams),
171
+ * body: await request.json(),
172
+ * params: params,
173
+ * user: await auth(),
174
+ * };
175
+ * ```
176
+ */
177
+ interface IRequestContext {
178
+ /** Query parameters (from URL query string) */
179
+ query: Record<string, unknown>;
180
+ /** Request body (parsed JSON) */
181
+ body: Record<string, unknown>;
182
+ /** Route parameters (dynamic segments in URL) */
183
+ params: Record<string, string>;
184
+ /**
185
+ * Authenticated user (from your auth middleware)
186
+ * Shape depends on your auth system
187
+ */
188
+ user?: {
189
+ id: string;
190
+ role?: string;
191
+ [key: string]: unknown;
192
+ };
193
+ /**
194
+ * Custom context (tenant ID, organization, locale, etc.)
195
+ * Use this for multi-tenant apps, feature flags, etc.
196
+ */
197
+ context?: Record<string, unknown>;
198
+ }
199
+ /**
200
+ * Framework-agnostic controller response
201
+ *
202
+ * Your controller methods should return this shape.
203
+ * Adapt it to your framework's response format in the handler layer.
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * // In controller method
208
+ * async list(context: IRequestContext): Promise<IControllerResponse> {
209
+ * const result = await this.repository.getAll(context.query);
210
+ * return {
211
+ * success: true,
212
+ * data: result,
213
+ * status: 200,
214
+ * };
215
+ * }
216
+ *
217
+ * // In Express handler
218
+ * const response = await controller.list(context);
219
+ * res.status(response.status).json(response);
220
+ *
221
+ * // In Fastify handler
222
+ * const response = await controller.list(context);
223
+ * return reply.code(response.status).send(response);
224
+ *
225
+ * // In Next.js handler
226
+ * const response = await controller.list(context);
227
+ * return NextResponse.json(response.data, { status: response.status });
228
+ * ```
229
+ */
230
+ interface IControllerResponse<T = unknown> {
231
+ /** Whether the operation succeeded */
232
+ success: boolean;
233
+ /** Response data (undefined on error) */
234
+ data?: T;
235
+ /** Error message (only if success = false) */
236
+ error?: string;
237
+ /** Additional error details (validation errors, stack trace, etc.) */
238
+ details?: unknown;
239
+ /** HTTP status code */
240
+ status: number;
241
+ /** Optional metadata (pagination info, warnings, etc.) */
242
+ meta?: Record<string, unknown>;
243
+ }
244
+ /**
245
+ * Framework-agnostic CRUD controller interface
246
+ *
247
+ * Implement this interface in your controller classes.
248
+ * Each method receives a framework-agnostic context and returns a framework-agnostic response.
249
+ *
250
+ * @template TDoc - The Mongoose document type
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * import { IController, IRequestContext, IControllerResponse } from '@mongokit/core';
255
+ *
256
+ * class UserController implements IController<IUser> {
257
+ * async list(context: IRequestContext): Promise<IControllerResponse> {
258
+ * // Implementation
259
+ * }
260
+ *
261
+ * async get(context: IRequestContext): Promise<IControllerResponse> {
262
+ * // Implementation
263
+ * }
264
+ *
265
+ * // ... other methods
266
+ * }
267
+ * ```
268
+ */
269
+ interface IController<TDoc> {
270
+ /**
271
+ * List resources with filtering, pagination, sorting, lookups
272
+ *
273
+ * @param context - Framework-agnostic request context
274
+ * @returns Promise resolving to controller response with paginated data
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // URL: GET /users?status=active&sort=-createdAt&limit=20
279
+ * const response = await controller.list({
280
+ * query: { status: 'active', sort: '-createdAt', limit: '20' },
281
+ * body: {},
282
+ * params: {},
283
+ * });
284
+ *
285
+ * // Response:
286
+ * {
287
+ * success: true,
288
+ * data: {
289
+ * method: 'offset',
290
+ * docs: [...],
291
+ * total: 100,
292
+ * page: 1,
293
+ * pages: 5
294
+ * },
295
+ * status: 200
296
+ * }
297
+ * ```
298
+ */
299
+ list(context: IRequestContext): Promise<IControllerResponse<PaginationResult<TDoc>>>;
300
+ /**
301
+ * Get single resource by ID
302
+ *
303
+ * @param context - Framework-agnostic request context (id in params)
304
+ * @returns Promise resolving to controller response with single document
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * // URL: GET /users/507f1f77bcf86cd799439011
309
+ * const response = await controller.get({
310
+ * query: {},
311
+ * body: {},
312
+ * params: { id: '507f1f77bcf86cd799439011' },
313
+ * });
314
+ *
315
+ * // Success response:
316
+ * {
317
+ * success: true,
318
+ * data: { _id: '...', name: 'John', ... },
319
+ * status: 200
320
+ * }
321
+ *
322
+ * // Not found response:
323
+ * {
324
+ * success: false,
325
+ * error: 'Resource not found',
326
+ * status: 404
327
+ * }
328
+ * ```
329
+ */
330
+ get(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
331
+ /**
332
+ * Create new resource
333
+ *
334
+ * @param context - Framework-agnostic request context (data in body)
335
+ * @returns Promise resolving to controller response with created document
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * // URL: POST /users
340
+ * // Body: { name: 'John', email: 'john@example.com' }
341
+ * const response = await controller.create({
342
+ * query: {},
343
+ * body: { name: 'John', email: 'john@example.com' },
344
+ * params: {},
345
+ * });
346
+ *
347
+ * // Response:
348
+ * {
349
+ * success: true,
350
+ * data: { _id: '...', name: 'John', email: '...', createdAt: '...' },
351
+ * status: 201
352
+ * }
353
+ * ```
354
+ */
355
+ create(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
356
+ /**
357
+ * Update existing resource
358
+ *
359
+ * @param context - Framework-agnostic request context (id in params, updates in body)
360
+ * @returns Promise resolving to controller response with updated document
361
+ *
362
+ * @example
363
+ * ```typescript
364
+ * // URL: PATCH /users/507f1f77bcf86cd799439011
365
+ * // Body: { name: 'Jane' }
366
+ * const response = await controller.update({
367
+ * query: {},
368
+ * body: { name: 'Jane' },
369
+ * params: { id: '507f1f77bcf86cd799439011' },
370
+ * });
371
+ *
372
+ * // Response:
373
+ * {
374
+ * success: true,
375
+ * data: { _id: '...', name: 'Jane', ... },
376
+ * status: 200
377
+ * }
378
+ * ```
379
+ */
380
+ update(context: IRequestContext): Promise<IControllerResponse<TDoc>>;
381
+ /**
382
+ * Delete resource
383
+ *
384
+ * @param context - Framework-agnostic request context (id in params)
385
+ * @returns Promise resolving to controller response with deletion result
386
+ *
387
+ * @example
388
+ * ```typescript
389
+ * // URL: DELETE /users/507f1f77bcf86cd799439011
390
+ * const response = await controller.delete({
391
+ * query: {},
392
+ * body: {},
393
+ * params: { id: '507f1f77bcf86cd799439011' },
394
+ * });
395
+ *
396
+ * // Response:
397
+ * {
398
+ * success: true,
399
+ * data: { message: 'Resource deleted successfully' },
400
+ * status: 200
401
+ * }
402
+ * ```
403
+ */
404
+ delete(context: IRequestContext): Promise<IControllerResponse<{
405
+ message: string;
406
+ }>>;
407
+ }
408
+ /**
409
+ * Optional: Response formatter utilities
410
+ *
411
+ * Helper functions to create standardized controller responses.
412
+ * Use these to maintain consistent response shapes across your API.
413
+ *
414
+ * @example
415
+ * ```typescript
416
+ * class BaseController {
417
+ * protected success<T>(data: T, status = 200): IControllerResponse<T> {
418
+ * return { success: true, data, status };
419
+ * }
420
+ *
421
+ * protected error(message: string, status = 500, details?: unknown): IControllerResponse {
422
+ * return { success: false, error: message, status, details };
423
+ * }
424
+ *
425
+ * protected paginated<T>(result: PaginationResult<T>): IControllerResponse<PaginationResult<T>> {
426
+ * return { success: true, data: result, status: 200 };
427
+ * }
428
+ * }
429
+ * ```
430
+ */
431
+ interface IResponseFormatter {
432
+ /**
433
+ * Format successful response
434
+ * @param data - Response data
435
+ * @param status - HTTP status code (default: 200)
436
+ */
437
+ success<T>(data: T, status?: number): IControllerResponse<T>;
438
+ /**
439
+ * Format error response
440
+ * @param message - Error message
441
+ * @param status - HTTP status code (default: 500)
442
+ * @param details - Additional error details
443
+ */
444
+ error(message: string, status?: number, details?: unknown): IControllerResponse;
445
+ /**
446
+ * Format paginated response
447
+ * @param result - Pagination result from Repository.getAll()
448
+ */
449
+ paginated<T>(result: PaginationResult<T>): IControllerResponse<PaginationResult<T>>;
450
+ }
451
+ //#endregion
452
+ //#region src/types.d.ts
453
+ /** Read Preference Type for replica sets */
454
+ type ReadPreferenceType = "primary" | "primaryPreferred" | "secondary" | "secondaryPreferred" | "nearest" | (string & {});
455
+ /** Re-export mongoose ObjectId */
456
+ type ObjectId = Types.ObjectId;
457
+ /** Generic document type */
458
+ type AnyDocument = Document & Record<string, unknown>;
459
+ /** Generic model type */
460
+ type AnyModel = Model<AnyDocument>;
461
+ /** Sort direction */
462
+ type SortDirection = 1 | -1;
463
+ /** Sort specification for MongoDB queries */
464
+ type SortSpec = Record<string, SortDirection>;
465
+ /** Populate specification */
466
+ type PopulateSpec = string | string[] | PopulateOptions | PopulateOptions[];
467
+ /** Select specification */
468
+ type SelectSpec = string | string[] | Record<string, 0 | 1>;
469
+ /** Filter query type for MongoDB queries (compatible with Mongoose 8 & 9) */
470
+ type FilterQuery<_T = unknown> = Record<string, unknown>;
471
+ /**
472
+ * Infer document type from a Mongoose Model
473
+ * @example
474
+ * type UserDoc = InferDocument<typeof UserModel>;
475
+ */
476
+ type InferDocument<TModel> = TModel extends Model<infer TDoc> ? TDoc : never;
477
+ /**
478
+ * Infer raw document shape (without Mongoose Document methods)
479
+ * @example
480
+ * type User = InferRawDoc<typeof UserModel>;
481
+ */
482
+ type InferRawDoc<TModel> = TModel extends Model<infer TDoc> ? TDoc extends Document ? Omit<TDoc, keyof Document> : TDoc : never;
483
+ /**
484
+ * Make specific fields optional
485
+ * @example
486
+ * type CreateUser = PartialBy<User, 'createdAt' | 'updatedAt'>;
487
+ */
488
+ type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
489
+ /**
490
+ * Make specific fields required
491
+ * @example
492
+ * type UserWithId = RequiredBy<User, '_id'>;
493
+ */
494
+ type RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
495
+ /**
496
+ * Extract keys of type T that have values of type V
497
+ * @example
498
+ * type StringFields = KeysOfType<User, string>; // 'name' | 'email'
499
+ */
500
+ type KeysOfType<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
501
+ /**
502
+ * Deep partial - makes all nested properties optional
503
+ */
504
+ type DeepPartial<T> = T extends object ? { [P in keyof T]?: DeepPartial<T[P]> } : T;
505
+ /**
506
+ * Strict object type - prevents excess properties
507
+ * Use with `satisfies` for compile-time validation
508
+ */
509
+ type Strict<T> = T & { [K in Exclude<string, keyof T>]?: never };
510
+ /**
511
+ * NonNullable fields extractor
512
+ */
513
+ type NonNullableFields<T> = { [K in keyof T]: NonNullable<T[K]> };
514
+ /**
515
+ * Create/Update input types from document
516
+ */
517
+ type CreateInput<TDoc> = Omit<TDoc, "_id" | "createdAt" | "updatedAt" | "__v">;
518
+ type UpdateInput<TDoc> = Partial<Omit<TDoc, "_id" | "createdAt" | "__v">>;
519
+ /** Hook execution mode */
520
+ type HookMode = "sync" | "async";
521
+ /** Repository options */
522
+ interface RepositoryOptions {
523
+ /** Whether repository event hooks are awaited */
524
+ hooks?: HookMode;
525
+ }
526
+ /** Pagination configuration */
527
+ interface PaginationConfig {
528
+ /** Default number of documents per page (default: 10) */
529
+ defaultLimit?: number;
530
+ /** Maximum allowed limit (default: 100) */
531
+ maxLimit?: number;
532
+ /** Maximum allowed page number (default: 10000) */
533
+ maxPage?: number;
534
+ /** Page number that triggers performance warning (default: 100) */
535
+ deepPageThreshold?: number;
536
+ /** Cursor version for forward compatibility (default: 1) */
537
+ cursorVersion?: number;
538
+ /** Use estimatedDocumentCount for faster counts on large collections */
539
+ useEstimatedCount?: boolean;
540
+ }
541
+ /** Base pagination options */
542
+ interface BasePaginationOptions {
543
+ /** Pagination mode (explicit override) */
544
+ mode?: "offset" | "keyset";
545
+ /** MongoDB query filters */
546
+ filters?: FilterQuery<AnyDocument>;
547
+ /** Sort specification */
548
+ sort?: SortSpec;
549
+ /** Number of documents per page */
550
+ limit?: number;
551
+ /** Fields to select */
552
+ select?: SelectSpec;
553
+ /** Fields to populate */
554
+ populate?: PopulateSpec;
555
+ /** Return plain JavaScript objects */
556
+ lean?: boolean;
557
+ /** MongoDB session for transactions */
558
+ session?: ClientSession;
559
+ /** Query hint (index name or document) */
560
+ hint?: string | Record<string, 1 | -1>;
561
+ /** Maximum execution time in milliseconds */
562
+ maxTimeMS?: number;
563
+ /** Read preference for replica sets (e.g. 'secondaryPreferred') */
564
+ readPreference?: ReadPreferenceType;
565
+ }
566
+ /** Offset pagination options */
567
+ interface OffsetPaginationOptions extends BasePaginationOptions {
568
+ /** Page number (1-indexed) */
569
+ page?: number;
570
+ /** Count strategy for filtered queries (default: 'exact') */
571
+ countStrategy?: "exact" | "estimated" | "none";
572
+ }
573
+ /** Keyset (cursor) pagination options */
574
+ interface KeysetPaginationOptions extends BasePaginationOptions {
575
+ /** Cursor token for next page */
576
+ after?: string;
577
+ /** Sort is required for keyset pagination */
578
+ sort: SortSpec;
579
+ }
580
+ /** Aggregate pagination options */
581
+ interface AggregatePaginationOptions {
582
+ /** Aggregation pipeline stages */
583
+ pipeline?: PipelineStage[];
584
+ /** Page number (1-indexed) */
585
+ page?: number;
586
+ /** Number of documents per page */
587
+ limit?: number;
588
+ /** MongoDB session for transactions */
589
+ session?: ClientSession;
590
+ /** Query hint (index name or document) for aggregation */
591
+ hint?: string | Record<string, 1 | -1>;
592
+ /** Maximum execution time in milliseconds */
593
+ maxTimeMS?: number;
594
+ /** Count strategy (default: 'exact' via $facet) */
595
+ countStrategy?: "exact" | "none";
596
+ /** Pagination mode (reserved for API consistency) */
597
+ mode?: "offset";
598
+ /** Read preference for replica sets (e.g. 'secondaryPreferred') */
599
+ readPreference?: ReadPreferenceType;
600
+ }
601
+ /** Offset pagination result */
602
+ interface OffsetPaginationResult<T = unknown> {
603
+ /** Pagination method used */
604
+ method: "offset";
605
+ /** Array of documents */
606
+ docs: T[];
607
+ /** Current page number */
608
+ page: number;
609
+ /** Documents per page */
610
+ limit: number;
611
+ /** Total document count */
612
+ total: number;
613
+ /** Total page count */
614
+ pages: number;
615
+ /** Whether next page exists */
616
+ hasNext: boolean;
617
+ /** Whether previous page exists */
618
+ hasPrev: boolean;
619
+ /** Performance warning for deep pagination */
620
+ warning?: string;
621
+ }
622
+ /** Keyset pagination result */
623
+ interface KeysetPaginationResult<T = unknown> {
624
+ /** Pagination method used */
625
+ method: "keyset";
626
+ /** Array of documents */
627
+ docs: T[];
628
+ /** Documents per page */
629
+ limit: number;
630
+ /** Whether more documents exist */
631
+ hasMore: boolean;
632
+ /** Cursor token for next page */
633
+ next: string | null;
634
+ }
635
+ /** Aggregate pagination result */
636
+ interface AggregatePaginationResult<T = unknown> {
637
+ /** Pagination method used */
638
+ method: "aggregate";
639
+ /** Array of documents */
640
+ docs: T[];
641
+ /** Current page number */
642
+ page: number;
643
+ /** Documents per page */
644
+ limit: number;
645
+ /** Total document count */
646
+ total: number;
647
+ /** Total page count */
648
+ pages: number;
649
+ /** Whether next page exists */
650
+ hasNext: boolean;
651
+ /** Whether previous page exists */
652
+ hasPrev: boolean;
653
+ /** Performance warning for deep pagination */
654
+ warning?: string;
655
+ }
656
+ /** Union type for all pagination results */
657
+ type PaginationResult<T = unknown> = OffsetPaginationResult<T> | KeysetPaginationResult<T> | AggregatePaginationResult<T>;
658
+ /** Repository operation options */
659
+ interface OperationOptions {
660
+ /** MongoDB session for transactions */
661
+ session?: ClientSession;
662
+ /** Fields to select */
663
+ select?: SelectSpec;
664
+ /** Fields to populate */
665
+ populate?: PopulateSpec;
666
+ /** Return plain JavaScript objects */
667
+ lean?: boolean;
668
+ /** Throw error if document not found (default: true) */
669
+ throwOnNotFound?: boolean;
670
+ /** Additional query filters (e.g., for soft delete) */
671
+ query?: Record<string, unknown>;
672
+ /** Read preference for replica sets (e.g. 'secondaryPreferred') */
673
+ readPreference?: "primary" | "primaryPreferred" | "secondary" | "secondaryPreferred" | "nearest" | string;
674
+ }
675
+ /** withTransaction options */
676
+ interface WithTransactionOptions {
677
+ /** Allow non-transactional fallback when transactions are unsupported */
678
+ allowFallback?: boolean;
679
+ /** Optional hook to observe fallback triggers */
680
+ onFallback?: (error: Error) => void;
681
+ /** MongoDB transaction options (readConcern, writeConcern, readPreference, maxCommitTimeMS) */
682
+ transactionOptions?: mongoose$1.mongo.TransactionOptions;
683
+ }
684
+ /** Create operation options */
685
+ interface CreateOptions {
686
+ /** MongoDB session for transactions */
687
+ session?: ClientSession;
688
+ /** Keep insertion order on error (default: true) */
689
+ ordered?: boolean;
690
+ }
691
+ /** Update operation options */
692
+ interface UpdateOptions extends OperationOptions {
693
+ /** Enable update pipeline syntax */
694
+ updatePipeline?: boolean;
695
+ }
696
+ /** Delete result */
697
+ interface DeleteResult {
698
+ success: boolean;
699
+ message: string;
700
+ count?: number;
701
+ }
702
+ /** Update many result */
703
+ interface UpdateManyResult {
704
+ matchedCount: number;
705
+ modifiedCount: number;
706
+ }
707
+ /** Validation result */
708
+ interface ValidationResult {
709
+ valid: boolean;
710
+ violations?: Array<{
711
+ field: string;
712
+ reason: string;
713
+ }>;
714
+ message?: string;
715
+ }
716
+ /** Update with validation result */
717
+ type UpdateWithValidationResult<T> = {
718
+ success: true;
719
+ data: T;
720
+ } | {
721
+ success: false;
722
+ error: {
723
+ code: number;
724
+ message: string;
725
+ violations?: ValidationResult["violations"];
726
+ };
727
+ };
728
+ /** User context for operations */
729
+ interface UserContext {
730
+ _id?: ObjectId | string;
731
+ id?: string;
732
+ roles?: string | string[];
733
+ [key: string]: unknown;
734
+ }
735
+ /** Repository operation context */
736
+ interface RepositoryContext {
737
+ /** Operation name */
738
+ operation: string;
739
+ /** Model name */
740
+ model: string;
741
+ /** Document data (for create/update) */
742
+ data?: Record<string, unknown>;
743
+ /** Array of documents (for createMany) */
744
+ dataArray?: Record<string, unknown>[];
745
+ /** Document ID (for update/delete/getById) */
746
+ id?: string | ObjectId;
747
+ /** Query filters */
748
+ query?: FilterQuery<AnyDocument>;
749
+ /** User making the request */
750
+ user?: UserContext;
751
+ /** Organization ID for multi-tenancy */
752
+ organizationId?: string | ObjectId;
753
+ /** Fields to select */
754
+ select?: SelectSpec;
755
+ /** Fields to populate */
756
+ populate?: PopulateSpec;
757
+ /** Return lean documents */
758
+ lean?: boolean;
759
+ /** MongoDB session */
760
+ session?: ClientSession;
761
+ /** Read preference for replica sets */
762
+ readPreference?: ReadPreferenceType;
763
+ /** Pagination filters */
764
+ filters?: Record<string, unknown>;
765
+ /** Sort specification */
766
+ sort?: SortSpec;
767
+ /** Page number (offset pagination) */
768
+ page?: number;
769
+ /** Items per page */
770
+ limit?: number;
771
+ /** Cursor for next page (keyset pagination) */
772
+ after?: string;
773
+ /** Search query string */
774
+ search?: string;
775
+ /** Pagination mode */
776
+ mode?: "offset" | "keyset";
777
+ /** Query hint */
778
+ hint?: string | Record<string, 1 | -1>;
779
+ /** Maximum execution time in milliseconds */
780
+ maxTimeMS?: number;
781
+ /** Count strategy for offset pagination */
782
+ countStrategy?: "exact" | "estimated" | "none";
783
+ /** Whether this is a soft delete operation (set by softDeletePlugin) */
784
+ softDeleted?: boolean;
785
+ /** Include soft-deleted documents in queries */
786
+ includeDeleted?: boolean;
787
+ /** Skip cache for this operation */
788
+ skipCache?: boolean;
789
+ /** Custom TTL for this operation (seconds) */
790
+ cacheTtl?: number;
791
+ /** Whether result was served from cache (internal) */
792
+ _cacheHit?: boolean;
793
+ /** Cached result (internal) */
794
+ _cachedResult?: unknown;
795
+ /** IDs to cascade delete (internal) */
796
+ _cascadeIds?: unknown[];
797
+ /** Custom context data from plugins */
798
+ [key: string]: unknown;
799
+ }
800
+ /** Plugin interface */
801
+ interface Plugin {
802
+ /** Plugin name */
803
+ name: string;
804
+ /** Apply plugin to repository */
805
+ apply(repo: RepositoryInstance): void;
806
+ }
807
+ /** Plugin function signature */
808
+ type PluginFunction = (repo: RepositoryInstance) => void;
809
+ /** Plugin type (object or function) */
810
+ type PluginType = Plugin | PluginFunction;
811
+ /** Repository instance for plugin type reference */
812
+ interface RepositoryInstance {
813
+ Model: Model<any>;
814
+ model: string;
815
+ _hooks: Map<string, Array<(data: any) => void | Promise<void>>>;
816
+ _pagination: unknown;
817
+ use(plugin: PluginType): this;
818
+ on(event: string, listener: (data: any) => void | Promise<void>): this;
819
+ off(event: string, listener: (data: any) => void | Promise<void>): this;
820
+ removeAllListeners(event?: string): this;
821
+ emit(event: string, data: unknown): void;
822
+ emitAsync(event: string, data: unknown): Promise<void>;
823
+ registerMethod?(name: string, fn: Function): void;
824
+ hasMethod?(name: string): boolean;
825
+ [key: string]: unknown;
826
+ }
827
+ /** Repository operation names */
828
+ type RepositoryOperation = "create" | "createMany" | "update" | "updateMany" | "delete" | "deleteMany" | "getById" | "getByQuery" | "getAll" | "aggregatePaginate" | "lookupPopulate";
829
+ /** Event lifecycle phases */
830
+ type EventPhase = "before" | "after" | "error";
831
+ /** Repository event names (generated from template literals) */
832
+ type RepositoryEvent = `${EventPhase}:${RepositoryOperation}` | "method:registered" | "error:hook";
833
+ /**
834
+ * Type-safe event handler map
835
+ *
836
+ * Hook signature contract:
837
+ * - `before:*` — receives `context: RepositoryContext` directly (not wrapped).
838
+ * Plugins mutate context in-place to inject filters, data, etc.
839
+ * - `after:*` — receives `{ context, result }` where result is the operation output.
840
+ * - `error:*` — receives `{ context, error }` where error is the caught Error.
841
+ */
842
+ type EventHandlers<TDoc = unknown> = { [K in RepositoryEvent]?: K extends `before:${string}` ? (context: RepositoryContext) => void | Promise<void> : K extends `after:${string}` ? (payload: {
843
+ context: RepositoryContext;
844
+ result: TDoc | TDoc[];
845
+ }) => void | Promise<void> : K extends `error:${string}` ? (payload: {
846
+ context: RepositoryContext;
847
+ error: Error;
848
+ }) => void | Promise<void> : (payload: {
849
+ context: RepositoryContext;
850
+ }) => void | Promise<void> };
851
+ /** Event payload */
852
+ interface EventPayload {
853
+ context: RepositoryContext;
854
+ result?: unknown;
855
+ error?: Error;
856
+ }
857
+ /** Field preset configuration */
858
+ interface FieldPreset {
859
+ /** Fields visible to everyone */
860
+ public: string[];
861
+ /** Additional fields for authenticated users */
862
+ authenticated?: string[];
863
+ /** Additional fields for admins */
864
+ admin?: string[];
865
+ }
866
+ /** Field rules for schema building */
867
+ interface FieldRules {
868
+ [fieldName: string]: {
869
+ /** Field cannot be updated */immutable?: boolean; /** Alias for immutable */
870
+ immutableAfterCreate?: boolean; /** System-only field (omitted from create/update) */
871
+ systemManaged?: boolean; /** Remove from required array */
872
+ optional?: boolean;
873
+ };
874
+ }
875
+ /** Schema builder options */
876
+ interface SchemaBuilderOptions {
877
+ /** Field rules for create/update */
878
+ fieldRules?: FieldRules;
879
+ /** Strict additional properties (default: false) */
880
+ strictAdditionalProperties?: boolean;
881
+ /** Date format: 'date' | 'datetime' */
882
+ dateAs?: "date" | "datetime";
883
+ /** Create schema options */
884
+ create?: {
885
+ /** Fields to omit from create schema */omitFields?: string[]; /** Override required status */
886
+ requiredOverrides?: Record<string, boolean>; /** Override optional status */
887
+ optionalOverrides?: Record<string, boolean>; /** Schema overrides */
888
+ schemaOverrides?: Record<string, unknown>;
889
+ };
890
+ /** Update schema options */
891
+ update?: {
892
+ /** Fields to omit from update schema */omitFields?: string[]; /** Require at least one field to be provided (default: false) */
893
+ requireAtLeastOne?: boolean;
894
+ };
895
+ /** Query schema options */
896
+ query?: {
897
+ /** Filterable fields */filterableFields?: Record<string, {
898
+ type: string;
899
+ } | unknown>;
900
+ };
901
+ }
902
+ /** JSON Schema type */
903
+ interface JsonSchema {
904
+ type: string;
905
+ properties?: Record<string, unknown>;
906
+ required?: string[];
907
+ additionalProperties?: boolean | unknown;
908
+ items?: unknown;
909
+ enum?: string[];
910
+ format?: string;
911
+ pattern?: string;
912
+ minProperties?: number;
913
+ maxProperties?: number;
914
+ minLength?: number;
915
+ maxLength?: number;
916
+ minimum?: number;
917
+ maximum?: number;
918
+ }
919
+ /** CRUD schemas result - framework-agnostic JSON schemas */
920
+ interface CrudSchemas {
921
+ /** JSON Schema for create request body */
922
+ createBody: JsonSchema;
923
+ /** JSON Schema for update request body */
924
+ updateBody: JsonSchema;
925
+ /** JSON Schema for route params (id validation) */
926
+ params: JsonSchema;
927
+ /** JSON Schema for list/query parameters */
928
+ listQuery: JsonSchema;
929
+ }
930
+ /** Decoded cursor */
931
+ interface DecodedCursor {
932
+ /** Primary sort field value (rehydrated) */
933
+ value: unknown;
934
+ /** Document ID (rehydrated) */
935
+ id: ObjectId | string;
936
+ /** Sort specification */
937
+ sort: SortSpec;
938
+ /** Cursor version */
939
+ version: number;
940
+ }
941
+ /** Validator definition */
942
+ interface ValidatorDefinition {
943
+ /** Validator name */
944
+ name: string;
945
+ /** Operations to apply validator to */
946
+ operations?: Array<"create" | "createMany" | "update" | "delete">;
947
+ /** Validation function */
948
+ validate: (context: RepositoryContext, repo?: RepositoryInstance) => void | Promise<void>;
949
+ }
950
+ /** Validation chain options */
951
+ interface ValidationChainOptions {
952
+ /** Stop on first validation error (default: true) */
953
+ stopOnFirstError?: boolean;
954
+ }
955
+ /** Logger interface for audit plugin */
956
+ interface Logger {
957
+ info?(message: string, meta?: Record<string, unknown>): void;
958
+ error?(message: string, meta?: Record<string, unknown>): void;
959
+ warn?(message: string, meta?: Record<string, unknown>): void;
960
+ debug?(message: string, meta?: Record<string, unknown>): void;
961
+ }
962
+ /** Filter mode for soft delete queries */
963
+ type SoftDeleteFilterMode = "null" | "exists";
964
+ /** Soft delete plugin options */
965
+ interface SoftDeleteOptions {
966
+ /** Field name for deletion timestamp (default: 'deletedAt') */
967
+ deletedField?: string;
968
+ /** Field name for deleting user (default: 'deletedBy') */
969
+ deletedByField?: string;
970
+ /** Enable soft delete (default: true) */
971
+ soft?: boolean;
972
+ /**
973
+ * Filter mode for excluding deleted documents (default: 'null')
974
+ * - 'null': Filters where deletedField is null (works with `default: null` in schema)
975
+ * - 'exists': Filters where deletedField does not exist (legacy behavior)
976
+ */
977
+ filterMode?: SoftDeleteFilterMode;
978
+ /** Add restore method to repository (default: true) */
979
+ addRestoreMethod?: boolean;
980
+ /** Add getDeleted method to repository (default: true) */
981
+ addGetDeletedMethod?: boolean;
982
+ /**
983
+ * TTL in days for auto-cleanup of deleted documents.
984
+ * When set, creates a TTL index on the deletedField.
985
+ * Documents will be automatically removed after the specified days.
986
+ */
987
+ ttlDays?: number;
988
+ }
989
+ /** Repository with soft delete methods */
990
+ interface SoftDeleteRepository {
991
+ /**
992
+ * Restore a soft-deleted document by setting deletedAt to null
993
+ * @param id - Document ID to restore
994
+ * @param options - Optional session for transactions
995
+ * @returns The restored document
996
+ */
997
+ restore(id: string | ObjectId, options?: {
998
+ session?: ClientSession;
999
+ }): Promise<unknown>;
1000
+ /**
1001
+ * Get all soft-deleted documents
1002
+ * @param params - Query parameters (filters, pagination, etc.)
1003
+ * @param options - Query options (select, populate, etc.)
1004
+ * @returns Paginated result of deleted documents
1005
+ */
1006
+ getDeleted(params?: {
1007
+ filters?: Record<string, unknown>;
1008
+ sort?: SortSpec | string;
1009
+ page?: number;
1010
+ limit?: number;
1011
+ }, options?: {
1012
+ select?: SelectSpec;
1013
+ populate?: PopulateSpec;
1014
+ lean?: boolean;
1015
+ session?: ClientSession;
1016
+ }): Promise<OffsetPaginationResult<unknown>>;
1017
+ }
1018
+ /** Group result */
1019
+ interface GroupResult {
1020
+ _id: unknown;
1021
+ count: number;
1022
+ }
1023
+ /** Min/Max result */
1024
+ interface MinMaxResult {
1025
+ min: unknown;
1026
+ max: unknown;
1027
+ }
1028
+ /**
1029
+ * Cache adapter interface - bring your own cache implementation
1030
+ * Works with Redis, Memcached, in-memory, or any key-value store
1031
+ *
1032
+ * @example Redis implementation:
1033
+ * ```typescript
1034
+ * const redisCache: CacheAdapter = {
1035
+ * async get(key) { return JSON.parse(await redis.get(key) || 'null'); },
1036
+ * async set(key, value, ttl) { await redis.setex(key, ttl, JSON.stringify(value)); },
1037
+ * async del(key) { await redis.del(key); },
1038
+ * async clear(pattern) {
1039
+ * const keys = await redis.keys(pattern || '*');
1040
+ * if (keys.length) await redis.del(...keys);
1041
+ * }
1042
+ * };
1043
+ * ```
1044
+ */
1045
+ interface CacheAdapter {
1046
+ /** Get value by key, returns null if not found or expired */
1047
+ get<T = unknown>(key: string): Promise<T | null>;
1048
+ /** Set value with TTL in seconds */
1049
+ set<T = unknown>(key: string, value: T, ttl: number): Promise<void>;
1050
+ /** Delete single key */
1051
+ del(key: string): Promise<void>;
1052
+ /** Clear keys matching pattern (optional, used for bulk invalidation) */
1053
+ clear?(pattern?: string): Promise<void>;
1054
+ }
1055
+ /** Cache plugin options */
1056
+ interface CacheOptions {
1057
+ /** Cache adapter implementation (required) */
1058
+ adapter: CacheAdapter;
1059
+ /** Default TTL in seconds (default: 60) */
1060
+ ttl?: number;
1061
+ /** TTL for byId queries in seconds (default: same as ttl) */
1062
+ byIdTtl?: number;
1063
+ /** TTL for query/list results in seconds (default: same as ttl) */
1064
+ queryTtl?: number;
1065
+ /** Key prefix for namespacing (default: 'mk') */
1066
+ prefix?: string;
1067
+ /** Enable debug logging (default: false) */
1068
+ debug?: boolean;
1069
+ /**
1070
+ * Skip caching for queries with these characteristics:
1071
+ * - largeLimit: Skip if limit > value (default: 100)
1072
+ */
1073
+ skipIf?: {
1074
+ largeLimit?: number;
1075
+ };
1076
+ }
1077
+ /** Options for cache-aware operations */
1078
+ interface CacheOperationOptions {
1079
+ /** Skip cache for this operation (read from DB directly) */
1080
+ skipCache?: boolean;
1081
+ /** Custom TTL for this operation in seconds */
1082
+ cacheTtl?: number;
1083
+ }
1084
+ /** Cache statistics (for debugging/monitoring) */
1085
+ interface CacheStats {
1086
+ hits: number;
1087
+ misses: number;
1088
+ sets: number;
1089
+ invalidations: number;
1090
+ }
1091
+ /** Cascade relation definition */
1092
+ interface CascadeRelation {
1093
+ /** Model name to cascade delete to */
1094
+ model: string;
1095
+ /** Foreign key field in the related model that references the deleted document */
1096
+ foreignKey: string;
1097
+ /** Whether to use soft delete if available (default: follows parent behavior) */
1098
+ softDelete?: boolean;
1099
+ }
1100
+ /** Cascade delete plugin options */
1101
+ interface CascadeOptions {
1102
+ /** Relations to cascade delete */
1103
+ relations: CascadeRelation[];
1104
+ /** Run cascade deletes in parallel (default: true) */
1105
+ parallel?: boolean;
1106
+ /** Logger for cascade operations */
1107
+ logger?: Logger;
1108
+ }
1109
+ /** HTTP Error with status code */
1110
+ interface HttpError extends Error {
1111
+ status: number;
1112
+ validationErrors?: Array<{
1113
+ validator: string;
1114
+ error: string;
1115
+ }>;
1116
+ }
1117
+ /**
1118
+ * Combines all plugin method types into a single type
1119
+ * Useful when you're using all plugins and want full type safety
1120
+ *
1121
+ * @example
1122
+ * ```typescript
1123
+ * import { Repository } from '@classytic/mongokit';
1124
+ * import type { AllPluginMethods } from '@classytic/mongokit';
1125
+ *
1126
+ * class UserRepo extends Repository<IUser> {}
1127
+ *
1128
+ * const repo = new UserRepo(Model, [...allPlugins]) as UserRepo & AllPluginMethods<IUser>;
1129
+ *
1130
+ * // TypeScript knows about all plugin methods!
1131
+ * await repo.increment(id, 'views', 1);
1132
+ * await repo.restore(id);
1133
+ * await repo.invalidateCache(id);
1134
+ * ```
1135
+ *
1136
+ * Note: Import the individual plugin method types if you need them:
1137
+ * ```typescript
1138
+ * import type {
1139
+ * MongoOperationsMethods,
1140
+ * BatchOperationsMethods,
1141
+ * AggregateHelpersMethods,
1142
+ * SubdocumentMethods,
1143
+ * SoftDeleteMethods,
1144
+ * CacheMethods,
1145
+ * } from '@classytic/mongokit';
1146
+ * ```
1147
+ */
1148
+ type AllPluginMethods<TDoc> = {
1149
+ upsert(query: Record<string, unknown>, data: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
1150
+ increment(id: string | ObjectId, field: string, value?: number, options?: Record<string, unknown>): Promise<TDoc>;
1151
+ decrement(id: string | ObjectId, field: string, value?: number, options?: Record<string, unknown>): Promise<TDoc>;
1152
+ pushToArray(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1153
+ pullFromArray(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1154
+ addToSet(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1155
+ setField(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1156
+ unsetField(id: string | ObjectId, fields: string | string[], options?: Record<string, unknown>): Promise<TDoc>;
1157
+ renameField(id: string | ObjectId, oldName: string, newName: string, options?: Record<string, unknown>): Promise<TDoc>;
1158
+ multiplyField(id: string | ObjectId, field: string, multiplier: number, options?: Record<string, unknown>): Promise<TDoc>;
1159
+ setMin(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1160
+ setMax(id: string | ObjectId, field: string, value: unknown, options?: Record<string, unknown>): Promise<TDoc>;
1161
+ updateMany(query: Record<string, unknown>, data: Record<string, unknown>, options?: {
1162
+ session?: ClientSession;
1163
+ updatePipeline?: boolean;
1164
+ }): Promise<{
1165
+ acknowledged: boolean;
1166
+ matchedCount: number;
1167
+ modifiedCount: number;
1168
+ upsertedCount: number;
1169
+ upsertedId: unknown;
1170
+ }>;
1171
+ deleteMany(query: Record<string, unknown>, options?: Record<string, unknown>): Promise<{
1172
+ acknowledged: boolean;
1173
+ deletedCount: number;
1174
+ }>;
1175
+ groupBy(field: string, options?: {
1176
+ limit?: number;
1177
+ session?: unknown;
1178
+ }): Promise<Array<{
1179
+ _id: unknown;
1180
+ count: number;
1181
+ }>>;
1182
+ sum(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
1183
+ average(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
1184
+ min(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
1185
+ max(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
1186
+ addSubdocument(parentId: string | ObjectId, arrayPath: string, subData: Record<string, unknown>, options?: Record<string, unknown>): Promise<TDoc>;
1187
+ getSubdocument(parentId: string | ObjectId, arrayPath: string, subId: string | ObjectId, options?: {
1188
+ lean?: boolean;
1189
+ session?: unknown;
1190
+ }): Promise<Record<string, unknown>>;
1191
+ updateSubdocument(parentId: string | ObjectId, arrayPath: string, subId: string | ObjectId, updateData: Record<string, unknown>, options?: {
1192
+ session?: unknown;
1193
+ }): Promise<TDoc>;
1194
+ deleteSubdocument(parentId: string | ObjectId, arrayPath: string, subId: string | ObjectId, options?: Record<string, unknown>): Promise<TDoc>;
1195
+ restore(id: string | ObjectId, options?: {
1196
+ session?: ClientSession;
1197
+ }): Promise<TDoc>;
1198
+ getDeleted(params?: {
1199
+ filters?: Record<string, unknown>;
1200
+ sort?: SortSpec | string;
1201
+ page?: number;
1202
+ limit?: number;
1203
+ }, options?: {
1204
+ select?: SelectSpec;
1205
+ populate?: PopulateSpec;
1206
+ lean?: boolean;
1207
+ session?: ClientSession;
1208
+ }): Promise<OffsetPaginationResult<TDoc>>;
1209
+ invalidateCache(id: string): Promise<void>;
1210
+ invalidateListCache(): Promise<void>;
1211
+ invalidateAllCache(): Promise<void>;
1212
+ getCacheStats(): CacheStats;
1213
+ resetCacheStats(): void;
1214
+ };
1215
+ /**
1216
+ * Helper type to add all plugin methods to a repository class
1217
+ * Cleaner than manually typing the intersection
1218
+ *
1219
+ * @example
1220
+ * ```typescript
1221
+ * import { Repository } from '@classytic/mongokit';
1222
+ * import type { WithPlugins } from '@classytic/mongokit';
1223
+ *
1224
+ * class OrderRepo extends Repository<IOrder> {
1225
+ * async getCustomerOrders(customerId: string) {
1226
+ * return this.getAll({ filters: { customerId } });
1227
+ * }
1228
+ * }
1229
+ *
1230
+ * const orderRepo = new OrderRepo(Model, [
1231
+ * ...allPlugins
1232
+ * ]) as WithPlugins<IOrder, OrderRepo>;
1233
+ *
1234
+ * // Works: custom methods + plugin methods
1235
+ * await orderRepo.getCustomerOrders('123');
1236
+ * await orderRepo.increment(orderId, 'total', 100);
1237
+ * ```
1238
+ */
1239
+ type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
1240
+ //#endregion
1241
+ export { SchemaBuilderOptions as $, KeysetPaginationOptions as A, PaginationResult as B, GroupResult as C, InferRawDoc as D, InferDocument as E, ObjectId as F, PopulateSpec as G, Plugin as H, OffsetPaginationOptions as I, RepositoryEvent as J, ReadPreferenceType as K, OffsetPaginationResult as L, Logger as M, MinMaxResult as N, JsonSchema as O, NonNullableFields as P, RequiredBy as Q, OperationOptions as R, FieldRules as S, LookupOptions as St, HttpError as T, PluginFunction as U, PartialBy as V, PluginType as W, RepositoryOperation as X, RepositoryInstance as Y, RepositoryOptions as Z, DeleteResult as _, IController as _t, AnyModel as a, SortSpec as at, EventPhase as b, IResponseFormatter as bt, CacheOptions as c, UpdateManyResult as ct, CascadeRelation as d, UserContext as dt, SelectSpec as et, CreateInput as f, ValidationChainOptions as ft, DeepPartial as g, WithTransactionOptions as gt, DecodedCursor as h, WithPlugins as ht, AnyDocument as i, SortDirection as it, KeysetPaginationResult as j, KeysOfType as k, CacheStats as l, UpdateOptions as lt, CrudSchemas as m, ValidatorDefinition as mt, AggregatePaginationResult as n, SoftDeleteOptions as nt, CacheAdapter as o, Strict as ot, CreateOptions as p, ValidationResult as pt, RepositoryContext as q, AllPluginMethods as r, SoftDeleteRepository as rt, CacheOperationOptions as s, UpdateInput as st, AggregatePaginationOptions as t, SoftDeleteFilterMode as tt, CascadeOptions as u, UpdateWithValidationResult as ut, EventHandlers as v, IControllerResponse as vt, HookMode as w, FieldPreset as x, LookupBuilder as xt, EventPayload as y, IRequestContext as yt, PaginationConfig as z };