@classytic/mongokit 3.1.5 → 3.2.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/README.md +89 -4
- package/dist/actions/index.d.ts +2 -2
- package/dist/actions/index.js +5 -3
- package/dist/ai/index.d.ts +175 -0
- package/dist/ai/index.js +206 -0
- package/dist/chunks/{chunk-M2XHQGZB.js → chunk-44KXLGPO.js} +28 -1
- package/dist/chunks/{chunk-CSLJ2PL2.js → chunk-DEVXDBRL.js} +143 -9
- package/dist/chunks/{chunk-CF6FLC2G.js → chunk-I7CWNAJB.js} +1 -1
- package/dist/chunks/chunk-JWUAVZ3L.js +8 -0
- package/dist/chunks/{chunk-IT7DCOKR.js → chunk-UE2IEXZJ.js} +15 -8
- package/dist/chunks/chunk-URLJFIR7.js +22 -0
- package/dist/chunks/{chunk-SAKSLT47.js → chunk-VWKIKZYF.js} +274 -7
- package/dist/chunks/chunk-WSFCRVEQ.js +7 -0
- package/dist/{index-BXSSv1pW.d.ts → index-BDn5fSTE.d.ts} +13 -1
- package/dist/index.d.ts +151 -42
- package/dist/index.js +299 -299
- package/dist/{mongooseToJsonSchema-Cc5AwuDu.d.ts → mongooseToJsonSchema-CaRF_bCN.d.ts} +33 -2
- package/dist/pagination/PaginationEngine.d.ts +1 -1
- package/dist/pagination/PaginationEngine.js +3 -2
- package/dist/plugins/index.d.ts +125 -2
- package/dist/plugins/index.js +5 -3
- package/dist/{types-B5Uv6Ak7.d.ts → types-Jni1KgkP.d.ts} +18 -11
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +4 -2
- package/package.json +10 -2
- package/dist/chunks/chunk-VJXDGP3C.js +0 -14
package/dist/index.js
CHANGED
|
@@ -1,224 +1,18 @@
|
|
|
1
|
-
import { getById, getByQuery, getOrCreate, count, exists, update, deleteById, aggregate, distinct } from './chunks/chunk-
|
|
2
|
-
export { actions_exports as actions } from './chunks/chunk-
|
|
3
|
-
import { PaginationEngine } from './chunks/chunk-
|
|
4
|
-
export { PaginationEngine } from './chunks/chunk-
|
|
5
|
-
export { aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, fieldFilterPlugin, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin } from './chunks/chunk-
|
|
6
|
-
import { create, createMany } from './chunks/chunk-
|
|
7
|
-
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, createMemoryCache, getImmutableFields, getSystemManagedFields, isFieldUpdateAllowed, validateUpdateBody } from './chunks/chunk-
|
|
1
|
+
import { LookupBuilder, getById, getByQuery, getOrCreate, count, exists, update, deleteById, aggregate, distinct } from './chunks/chunk-VWKIKZYF.js';
|
|
2
|
+
export { LookupBuilder, actions_exports as actions } from './chunks/chunk-VWKIKZYF.js';
|
|
3
|
+
import { PaginationEngine } from './chunks/chunk-44KXLGPO.js';
|
|
4
|
+
export { PaginationEngine } from './chunks/chunk-44KXLGPO.js';
|
|
5
|
+
export { aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, fieldFilterPlugin, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin } from './chunks/chunk-DEVXDBRL.js';
|
|
6
|
+
import { create, createMany } from './chunks/chunk-I7CWNAJB.js';
|
|
7
|
+
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, createMemoryCache, getImmutableFields, getSystemManagedFields, isFieldUpdateAllowed, validateUpdateBody } from './chunks/chunk-UE2IEXZJ.js';
|
|
8
8
|
export { createFieldPreset, filterResponseData, getFieldsForUser, getMongooseProjection } from './chunks/chunk-2ZN65ZOP.js';
|
|
9
|
-
import {
|
|
10
|
-
export {
|
|
9
|
+
import { warn } from './chunks/chunk-URLJFIR7.js';
|
|
10
|
+
export { configureLogger } from './chunks/chunk-URLJFIR7.js';
|
|
11
|
+
import { createError } from './chunks/chunk-JWUAVZ3L.js';
|
|
12
|
+
export { createError } from './chunks/chunk-JWUAVZ3L.js';
|
|
13
|
+
import './chunks/chunk-WSFCRVEQ.js';
|
|
11
14
|
import mongoose from 'mongoose';
|
|
12
15
|
|
|
13
|
-
// src/query/LookupBuilder.ts
|
|
14
|
-
var LookupBuilder = class _LookupBuilder {
|
|
15
|
-
options = {};
|
|
16
|
-
constructor(from) {
|
|
17
|
-
if (from) this.options.from = from;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Set the collection to join with
|
|
21
|
-
*/
|
|
22
|
-
from(collection) {
|
|
23
|
-
this.options.from = collection;
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Set the local field (source collection)
|
|
28
|
-
* IMPORTANT: This field should be indexed for optimal performance
|
|
29
|
-
*/
|
|
30
|
-
localField(field) {
|
|
31
|
-
this.options.localField = field;
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Set the foreign field (target collection)
|
|
36
|
-
* IMPORTANT: This field should be indexed (preferably unique) for optimal performance
|
|
37
|
-
*/
|
|
38
|
-
foreignField(field) {
|
|
39
|
-
this.options.foreignField = field;
|
|
40
|
-
return this;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Set the output field name
|
|
44
|
-
* Defaults to the collection name if not specified
|
|
45
|
-
*/
|
|
46
|
-
as(fieldName) {
|
|
47
|
-
this.options.as = fieldName;
|
|
48
|
-
return this;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Mark this lookup as returning a single document
|
|
52
|
-
* Automatically unwraps the array result to a single object or null
|
|
53
|
-
*/
|
|
54
|
-
single(isSingle = true) {
|
|
55
|
-
this.options.single = isSingle;
|
|
56
|
-
return this;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Add a pipeline to filter/transform joined documents
|
|
60
|
-
* Useful for filtering, sorting, or limiting joined results
|
|
61
|
-
*
|
|
62
|
-
* @example
|
|
63
|
-
* ```typescript
|
|
64
|
-
* lookup.pipeline([
|
|
65
|
-
* { $match: { status: 'active' } },
|
|
66
|
-
* { $sort: { priority: -1 } },
|
|
67
|
-
* { $limit: 5 }
|
|
68
|
-
* ]);
|
|
69
|
-
* ```
|
|
70
|
-
*/
|
|
71
|
-
pipeline(stages) {
|
|
72
|
-
this.options.pipeline = stages;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Set let variables for use in pipeline
|
|
77
|
-
* Allows referencing local document fields in the pipeline
|
|
78
|
-
*/
|
|
79
|
-
let(variables) {
|
|
80
|
-
this.options.let = variables;
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Build the $lookup aggregation stage(s)
|
|
85
|
-
* Returns an array of pipeline stages including $lookup and optional $unwind
|
|
86
|
-
*
|
|
87
|
-
* IMPORTANT: MongoDB $lookup has two mutually exclusive forms:
|
|
88
|
-
* 1. Simple form: { from, localField, foreignField, as }
|
|
89
|
-
* 2. Pipeline form: { from, let, pipeline, as }
|
|
90
|
-
*
|
|
91
|
-
* When pipeline or let is specified, we use the pipeline form.
|
|
92
|
-
* Otherwise, we use the simpler localField/foreignField form.
|
|
93
|
-
*/
|
|
94
|
-
build() {
|
|
95
|
-
const { from, localField, foreignField, as, single, pipeline, let: letVars } = this.options;
|
|
96
|
-
if (!from) {
|
|
97
|
-
throw new Error('LookupBuilder: "from" collection is required');
|
|
98
|
-
}
|
|
99
|
-
const outputField = as || from;
|
|
100
|
-
const stages = [];
|
|
101
|
-
const usePipelineForm = pipeline || letVars;
|
|
102
|
-
let lookupStage;
|
|
103
|
-
if (usePipelineForm) {
|
|
104
|
-
if (!pipeline || pipeline.length === 0) {
|
|
105
|
-
if (!localField || !foreignField) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
"LookupBuilder: When using pipeline form without a custom pipeline, both localField and foreignField are required to auto-generate the pipeline"
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
const autoPipeline = [
|
|
111
|
-
{
|
|
112
|
-
$match: {
|
|
113
|
-
$expr: {
|
|
114
|
-
$eq: [`$${foreignField}`, `$$${localField}`]
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
];
|
|
119
|
-
lookupStage = {
|
|
120
|
-
$lookup: {
|
|
121
|
-
from,
|
|
122
|
-
let: { [localField]: `$${localField}`, ...letVars || {} },
|
|
123
|
-
pipeline: autoPipeline,
|
|
124
|
-
as: outputField
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
} else {
|
|
128
|
-
lookupStage = {
|
|
129
|
-
$lookup: {
|
|
130
|
-
from,
|
|
131
|
-
...letVars && { let: letVars },
|
|
132
|
-
pipeline,
|
|
133
|
-
as: outputField
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
if (!localField || !foreignField) {
|
|
139
|
-
throw new Error("LookupBuilder: localField and foreignField are required for simple lookup");
|
|
140
|
-
}
|
|
141
|
-
lookupStage = {
|
|
142
|
-
$lookup: {
|
|
143
|
-
from,
|
|
144
|
-
localField,
|
|
145
|
-
foreignField,
|
|
146
|
-
as: outputField
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
stages.push(lookupStage);
|
|
151
|
-
if (single) {
|
|
152
|
-
stages.push({
|
|
153
|
-
$unwind: {
|
|
154
|
-
path: `$${outputField}`,
|
|
155
|
-
preserveNullAndEmptyArrays: true
|
|
156
|
-
// Keep documents even if no match found
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
return stages;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Build and return only the $lookup stage (without $unwind)
|
|
164
|
-
* Useful when you want to handle unwrapping yourself
|
|
165
|
-
*/
|
|
166
|
-
buildLookupOnly() {
|
|
167
|
-
const stages = this.build();
|
|
168
|
-
return stages[0];
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Static helper: Create a simple lookup in one line
|
|
172
|
-
*/
|
|
173
|
-
static simple(from, localField, foreignField, options = {}) {
|
|
174
|
-
return new _LookupBuilder(from).localField(localField).foreignField(foreignField).as(options.as || from).single(options.single || false).build();
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Static helper: Create multiple lookups at once
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* ```typescript
|
|
181
|
-
* const pipeline = LookupBuilder.multiple([
|
|
182
|
-
* { from: 'departments', localField: 'deptSlug', foreignField: 'slug', single: true },
|
|
183
|
-
* { from: 'managers', localField: 'managerId', foreignField: '_id', single: true }
|
|
184
|
-
* ]);
|
|
185
|
-
* ```
|
|
186
|
-
*/
|
|
187
|
-
static multiple(lookups) {
|
|
188
|
-
return lookups.flatMap((lookup) => {
|
|
189
|
-
const builder = new _LookupBuilder(lookup.from).localField(lookup.localField).foreignField(lookup.foreignField);
|
|
190
|
-
if (lookup.as) builder.as(lookup.as);
|
|
191
|
-
if (lookup.single) builder.single(lookup.single);
|
|
192
|
-
if (lookup.pipeline) builder.pipeline(lookup.pipeline);
|
|
193
|
-
if (lookup.let) builder.let(lookup.let);
|
|
194
|
-
return builder.build();
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Static helper: Create a nested lookup (lookup within lookup)
|
|
199
|
-
* Useful for multi-level joins like Order -> Product -> Category
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* ```typescript
|
|
203
|
-
* // Join orders with products, then products with categories
|
|
204
|
-
* const pipeline = LookupBuilder.nested([
|
|
205
|
-
* { from: 'products', localField: 'productSku', foreignField: 'sku', as: 'product', single: true },
|
|
206
|
-
* { from: 'categories', localField: 'product.categorySlug', foreignField: 'slug', as: 'product.category', single: true }
|
|
207
|
-
* ]);
|
|
208
|
-
* ```
|
|
209
|
-
*/
|
|
210
|
-
static nested(lookups) {
|
|
211
|
-
return lookups.flatMap((lookup, index) => {
|
|
212
|
-
const builder = new _LookupBuilder(lookup.from).localField(lookup.localField).foreignField(lookup.foreignField);
|
|
213
|
-
if (lookup.as) builder.as(lookup.as);
|
|
214
|
-
if (lookup.single !== void 0) builder.single(lookup.single);
|
|
215
|
-
if (lookup.pipeline) builder.pipeline(lookup.pipeline);
|
|
216
|
-
if (lookup.let) builder.let(lookup.let);
|
|
217
|
-
return builder.build();
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
16
|
// src/query/AggregationBuilder.ts
|
|
223
17
|
function normalizeSortSpec(sortSpec) {
|
|
224
18
|
const normalized = {};
|
|
@@ -235,6 +29,7 @@ function normalizeSortSpec(sortSpec) {
|
|
|
235
29
|
}
|
|
236
30
|
var AggregationBuilder = class _AggregationBuilder {
|
|
237
31
|
pipeline = [];
|
|
32
|
+
_diskUse = false;
|
|
238
33
|
/**
|
|
239
34
|
* Get the current pipeline
|
|
240
35
|
*/
|
|
@@ -247,11 +42,35 @@ var AggregationBuilder = class _AggregationBuilder {
|
|
|
247
42
|
build() {
|
|
248
43
|
return this.get();
|
|
249
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Build pipeline with execution options (allowDiskUse, etc.)
|
|
47
|
+
*/
|
|
48
|
+
plan() {
|
|
49
|
+
return { pipeline: this.get(), allowDiskUse: this._diskUse };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build and execute the pipeline against a model
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const results = await new AggregationBuilder()
|
|
57
|
+
* .match({ status: 'active' })
|
|
58
|
+
* .allowDiskUse()
|
|
59
|
+
* .exec(MyModel);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
async exec(model, session) {
|
|
63
|
+
const agg = model.aggregate(this.build());
|
|
64
|
+
if (this._diskUse) agg.allowDiskUse(true);
|
|
65
|
+
if (session) agg.session(session);
|
|
66
|
+
return agg.exec();
|
|
67
|
+
}
|
|
250
68
|
/**
|
|
251
69
|
* Reset the pipeline
|
|
252
70
|
*/
|
|
253
71
|
reset() {
|
|
254
72
|
this.pipeline = [];
|
|
73
|
+
this._diskUse = false;
|
|
255
74
|
return this;
|
|
256
75
|
}
|
|
257
76
|
/**
|
|
@@ -543,6 +362,25 @@ var AggregationBuilder = class _AggregationBuilder {
|
|
|
543
362
|
return this;
|
|
544
363
|
}
|
|
545
364
|
// ============================================================
|
|
365
|
+
// EXECUTION OPTIONS
|
|
366
|
+
// ============================================================
|
|
367
|
+
/**
|
|
368
|
+
* Enable allowDiskUse for large aggregations that exceed 100MB memory limit
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```typescript
|
|
372
|
+
* const results = await new AggregationBuilder()
|
|
373
|
+
* .match({ status: 'active' })
|
|
374
|
+
* .group({ _id: '$category', total: { $sum: '$amount' } })
|
|
375
|
+
* .allowDiskUse()
|
|
376
|
+
* .exec(Model);
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
allowDiskUse(enable = true) {
|
|
380
|
+
this._diskUse = enable;
|
|
381
|
+
return this;
|
|
382
|
+
}
|
|
383
|
+
// ============================================================
|
|
546
384
|
// UTILITY METHODS
|
|
547
385
|
// ============================================================
|
|
548
386
|
/**
|
|
@@ -626,6 +464,56 @@ var AggregationBuilder = class _AggregationBuilder {
|
|
|
626
464
|
return this;
|
|
627
465
|
}
|
|
628
466
|
// ============================================================
|
|
467
|
+
// ATLAS VECTOR SEARCH (MongoDB Atlas 7.0+)
|
|
468
|
+
// ============================================================
|
|
469
|
+
/**
|
|
470
|
+
* $vectorSearch - Semantic similarity search using vector embeddings (Atlas only)
|
|
471
|
+
*
|
|
472
|
+
* Requires an Atlas Vector Search index on the target field.
|
|
473
|
+
* Must be the first stage in the pipeline.
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```typescript
|
|
477
|
+
* const results = await new AggregationBuilder()
|
|
478
|
+
* .vectorSearch({
|
|
479
|
+
* index: 'vector_index',
|
|
480
|
+
* path: 'embedding',
|
|
481
|
+
* queryVector: await getEmbedding('running shoes'),
|
|
482
|
+
* limit: 10,
|
|
483
|
+
* numCandidates: 100,
|
|
484
|
+
* filter: { category: 'footwear' }
|
|
485
|
+
* })
|
|
486
|
+
* .project({ embedding: 0, score: { $meta: 'vectorSearchScore' } })
|
|
487
|
+
* .exec(ProductModel);
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
vectorSearch(options) {
|
|
491
|
+
if (this.pipeline.length > 0) {
|
|
492
|
+
throw new Error("[mongokit] $vectorSearch must be the first stage in the pipeline");
|
|
493
|
+
}
|
|
494
|
+
const rawCandidates = options.numCandidates ?? Math.max(options.limit * 10, 100);
|
|
495
|
+
const numCandidates = Math.min(Math.max(rawCandidates, options.limit), 1e4);
|
|
496
|
+
this.pipeline.push({
|
|
497
|
+
$vectorSearch: {
|
|
498
|
+
index: options.index,
|
|
499
|
+
path: options.path,
|
|
500
|
+
queryVector: options.queryVector,
|
|
501
|
+
numCandidates,
|
|
502
|
+
limit: options.limit,
|
|
503
|
+
...options.filter && { filter: options.filter },
|
|
504
|
+
...options.exact && { exact: options.exact }
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
return this;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Add vectorSearchScore as a field after $vectorSearch
|
|
511
|
+
* Convenience for `.addFields({ score: { $meta: 'vectorSearchScore' } })`
|
|
512
|
+
*/
|
|
513
|
+
withVectorScore(fieldName = "score") {
|
|
514
|
+
return this.addFields({ [fieldName]: { $meta: "vectorSearchScore" } });
|
|
515
|
+
}
|
|
516
|
+
// ============================================================
|
|
629
517
|
// HELPER FACTORY METHODS
|
|
630
518
|
// ============================================================
|
|
631
519
|
/**
|
|
@@ -680,6 +568,28 @@ var Repository = class {
|
|
|
680
568
|
this._hooks.get(event).push(listener);
|
|
681
569
|
return this;
|
|
682
570
|
}
|
|
571
|
+
/**
|
|
572
|
+
* Remove a specific event listener
|
|
573
|
+
*/
|
|
574
|
+
off(event, listener) {
|
|
575
|
+
const listeners = this._hooks.get(event);
|
|
576
|
+
if (listeners) {
|
|
577
|
+
const idx = listeners.indexOf(listener);
|
|
578
|
+
if (idx !== -1) listeners.splice(idx, 1);
|
|
579
|
+
}
|
|
580
|
+
return this;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Remove all listeners for an event, or all listeners entirely
|
|
584
|
+
*/
|
|
585
|
+
removeAllListeners(event) {
|
|
586
|
+
if (event) {
|
|
587
|
+
this._hooks.delete(event);
|
|
588
|
+
} else {
|
|
589
|
+
this._hooks.clear();
|
|
590
|
+
}
|
|
591
|
+
return this;
|
|
592
|
+
}
|
|
683
593
|
/**
|
|
684
594
|
* Emit event (sync - for backwards compatibility)
|
|
685
595
|
*/
|
|
@@ -756,26 +666,38 @@ var Repository = class {
|
|
|
756
666
|
* Get document by ID
|
|
757
667
|
*/
|
|
758
668
|
async getById(id, options = {}) {
|
|
759
|
-
const
|
|
669
|
+
const populateSpec = options.populateOptions || options.populate;
|
|
670
|
+
const context = await this._buildContext("getById", { id, ...options, populate: populateSpec });
|
|
760
671
|
if (context._cacheHit) {
|
|
761
672
|
return context._cachedResult;
|
|
762
673
|
}
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
674
|
+
try {
|
|
675
|
+
const result = await getById(this.Model, id, context);
|
|
676
|
+
await this._emitHook("after:getById", { context, result });
|
|
677
|
+
return result;
|
|
678
|
+
} catch (error) {
|
|
679
|
+
await this._emitErrorHook("error:getById", { context, error });
|
|
680
|
+
throw this._handleError(error);
|
|
681
|
+
}
|
|
766
682
|
}
|
|
767
683
|
/**
|
|
768
684
|
* Get single document by query
|
|
769
685
|
*/
|
|
770
686
|
async getByQuery(query, options = {}) {
|
|
771
|
-
const
|
|
687
|
+
const populateSpec = options.populateOptions || options.populate;
|
|
688
|
+
const context = await this._buildContext("getByQuery", { query, ...options, populate: populateSpec });
|
|
772
689
|
if (context._cacheHit) {
|
|
773
690
|
return context._cachedResult;
|
|
774
691
|
}
|
|
775
692
|
const finalQuery = context.query || query;
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
693
|
+
try {
|
|
694
|
+
const result = await getByQuery(this.Model, finalQuery, context);
|
|
695
|
+
await this._emitHook("after:getByQuery", { context, result });
|
|
696
|
+
return result;
|
|
697
|
+
} catch (error) {
|
|
698
|
+
await this._emitErrorHook("error:getByQuery", { context, error });
|
|
699
|
+
throw this._handleError(error);
|
|
700
|
+
}
|
|
779
701
|
}
|
|
780
702
|
/**
|
|
781
703
|
* Unified pagination - auto-detects offset vs keyset based on params
|
|
@@ -805,17 +727,16 @@ var Repository = class {
|
|
|
805
727
|
if (context._cacheHit) {
|
|
806
728
|
return context._cachedResult;
|
|
807
729
|
}
|
|
808
|
-
const
|
|
809
|
-
const
|
|
810
|
-
const
|
|
811
|
-
const
|
|
812
|
-
const
|
|
813
|
-
const
|
|
814
|
-
const
|
|
815
|
-
const limit = params.limit || params.pagination?.limit || this._pagination.config.defaultLimit;
|
|
730
|
+
const filters = context.filters ?? params.filters ?? {};
|
|
731
|
+
const search = context.search ?? params.search;
|
|
732
|
+
const sort = context.sort ?? params.sort ?? "-createdAt";
|
|
733
|
+
const limit = context.limit ?? params.limit ?? params.pagination?.limit ?? this._pagination.config.defaultLimit;
|
|
734
|
+
const page = context.page ?? params.pagination?.page ?? params.page;
|
|
735
|
+
const after = context.after ?? params.cursor ?? params.after;
|
|
736
|
+
const useKeyset = !page && (after || sort !== "-createdAt" && (context.sort ?? params.sort));
|
|
816
737
|
let query = { ...filters };
|
|
817
738
|
if (search) query.$text = { $search: search };
|
|
818
|
-
const populateSpec = options.populateOptions || context.populate || options.populate;
|
|
739
|
+
const populateSpec = options.populateOptions || params.populateOptions || context.populate || options.populate;
|
|
819
740
|
const paginationOptions = {
|
|
820
741
|
filters: query,
|
|
821
742
|
sort: this._parseSort(sort),
|
|
@@ -825,23 +746,26 @@ var Repository = class {
|
|
|
825
746
|
lean: context.lean ?? options.lean ?? true,
|
|
826
747
|
session: options.session
|
|
827
748
|
};
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
749
|
+
try {
|
|
750
|
+
let result;
|
|
751
|
+
if (useKeyset) {
|
|
752
|
+
result = await this._pagination.stream({
|
|
753
|
+
...paginationOptions,
|
|
754
|
+
sort: paginationOptions.sort,
|
|
755
|
+
after
|
|
756
|
+
});
|
|
757
|
+
} else {
|
|
758
|
+
result = await this._pagination.paginate({
|
|
759
|
+
...paginationOptions,
|
|
760
|
+
page: page || 1
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
await this._emitHook("after:getAll", { context, result });
|
|
764
|
+
return result;
|
|
765
|
+
} catch (error) {
|
|
766
|
+
await this._emitErrorHook("error:getAll", { context, error });
|
|
767
|
+
throw this._handleError(error);
|
|
842
768
|
}
|
|
843
|
-
await this._emitHook("after:getAll", { context, result });
|
|
844
|
-
return result;
|
|
845
769
|
}
|
|
846
770
|
/**
|
|
847
771
|
* Get or create document
|
|
@@ -886,7 +810,7 @@ var Repository = class {
|
|
|
886
810
|
await this._emitHook("after:delete", { context, result: result2 });
|
|
887
811
|
return result2;
|
|
888
812
|
}
|
|
889
|
-
const result = await deleteById(this.Model, id, options);
|
|
813
|
+
const result = await deleteById(this.Model, id, { session: options.session, query: context.query });
|
|
890
814
|
await this._emitHook("after:delete", { context, result });
|
|
891
815
|
return result;
|
|
892
816
|
} catch (error) {
|
|
@@ -942,8 +866,9 @@ var Repository = class {
|
|
|
942
866
|
const context = await this._buildContext("lookupPopulate", options);
|
|
943
867
|
try {
|
|
944
868
|
const builder = new AggregationBuilder();
|
|
945
|
-
|
|
946
|
-
|
|
869
|
+
const filters = context.filters ?? options.filters;
|
|
870
|
+
if (filters && Object.keys(filters).length > 0) {
|
|
871
|
+
builder.match(filters);
|
|
947
872
|
}
|
|
948
873
|
builder.multiLookup(options.lookups);
|
|
949
874
|
if (options.sort) {
|
|
@@ -955,12 +880,12 @@ var Repository = class {
|
|
|
955
880
|
const SAFE_LIMIT = 1e3;
|
|
956
881
|
const SAFE_MAX_OFFSET = 1e4;
|
|
957
882
|
if (limit > SAFE_LIMIT) {
|
|
958
|
-
|
|
883
|
+
warn(
|
|
959
884
|
`[mongokit] Large limit (${limit}) in lookupPopulate. $facet results must be <16MB. Consider using smaller limits or stream-based pagination for large datasets.`
|
|
960
885
|
);
|
|
961
886
|
}
|
|
962
887
|
if (skip > SAFE_MAX_OFFSET) {
|
|
963
|
-
|
|
888
|
+
warn(
|
|
964
889
|
`[mongokit] Large offset (${skip}) in lookupPopulate. $facet with high offsets can exceed 16MB. For deep pagination, consider using keyset/cursor-based pagination instead.`
|
|
965
890
|
);
|
|
966
891
|
}
|
|
@@ -1057,40 +982,52 @@ var Repository = class {
|
|
|
1057
982
|
return new LookupBuilder(from);
|
|
1058
983
|
}
|
|
1059
984
|
/**
|
|
1060
|
-
* Execute callback within a transaction
|
|
985
|
+
* Execute callback within a transaction with automatic retry on transient failures.
|
|
986
|
+
*
|
|
987
|
+
* Uses the MongoDB driver's `session.withTransaction()` which automatically retries
|
|
988
|
+
* on `TransientTransactionError` and `UnknownTransactionCommitResult`.
|
|
989
|
+
*
|
|
990
|
+
* The callback always receives a `ClientSession`. When `allowFallback` is true
|
|
991
|
+
* and the MongoDB deployment doesn't support transactions (e.g., standalone),
|
|
992
|
+
* the callback runs without a transaction on the same session.
|
|
993
|
+
*
|
|
994
|
+
* @param callback - Receives a `ClientSession` to pass to repository operations
|
|
995
|
+
* @param options.allowFallback - Run without transaction on standalone MongoDB (default: false)
|
|
996
|
+
* @param options.onFallback - Called when falling back to non-transactional execution
|
|
997
|
+
* @param options.transactionOptions - MongoDB driver transaction options (readConcern, writeConcern, etc.)
|
|
998
|
+
*
|
|
999
|
+
* @example
|
|
1000
|
+
* ```typescript
|
|
1001
|
+
* const result = await repo.withTransaction(async (session) => {
|
|
1002
|
+
* const order = await repo.create({ total: 100 }, { session });
|
|
1003
|
+
* await paymentRepo.create({ orderId: order._id }, { session });
|
|
1004
|
+
* return order;
|
|
1005
|
+
* });
|
|
1006
|
+
*
|
|
1007
|
+
* // With fallback for standalone/dev environments
|
|
1008
|
+
* await repo.withTransaction(callback, {
|
|
1009
|
+
* allowFallback: true,
|
|
1010
|
+
* onFallback: (err) => logger.warn('Running without transaction', err),
|
|
1011
|
+
* });
|
|
1012
|
+
* ```
|
|
1061
1013
|
*/
|
|
1062
1014
|
async withTransaction(callback, options = {}) {
|
|
1063
1015
|
const session = await mongoose.startSession();
|
|
1064
|
-
let started = false;
|
|
1065
1016
|
try {
|
|
1066
|
-
session.
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1017
|
+
const result = await session.withTransaction(
|
|
1018
|
+
() => callback(session),
|
|
1019
|
+
options.transactionOptions
|
|
1020
|
+
);
|
|
1070
1021
|
return result;
|
|
1071
1022
|
} catch (error) {
|
|
1072
1023
|
const err = error;
|
|
1073
1024
|
if (options.allowFallback && this._isTransactionUnsupported(err)) {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1077
|
-
if (started) {
|
|
1078
|
-
try {
|
|
1079
|
-
await session.abortTransaction();
|
|
1080
|
-
} catch {
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
return await callback(null);
|
|
1084
|
-
}
|
|
1085
|
-
if (started) {
|
|
1086
|
-
try {
|
|
1087
|
-
await session.abortTransaction();
|
|
1088
|
-
} catch {
|
|
1089
|
-
}
|
|
1025
|
+
options.onFallback?.(err);
|
|
1026
|
+
return await callback(session);
|
|
1090
1027
|
}
|
|
1091
1028
|
throw err;
|
|
1092
1029
|
} finally {
|
|
1093
|
-
session.endSession();
|
|
1030
|
+
await session.endSession();
|
|
1094
1031
|
}
|
|
1095
1032
|
}
|
|
1096
1033
|
_isTransactionUnsupported(error) {
|
|
@@ -1197,10 +1134,11 @@ var QueryParser = class {
|
|
|
1197
1134
|
enableLookups: options.enableLookups ?? true,
|
|
1198
1135
|
enableAggregations: options.enableAggregations ?? false,
|
|
1199
1136
|
searchMode: options.searchMode ?? "text",
|
|
1200
|
-
searchFields: options.searchFields
|
|
1137
|
+
searchFields: options.searchFields,
|
|
1138
|
+
allowedLookupCollections: options.allowedLookupCollections
|
|
1201
1139
|
};
|
|
1202
1140
|
if (this.options.searchMode === "regex" && (!this.options.searchFields || this.options.searchFields.length === 0)) {
|
|
1203
|
-
|
|
1141
|
+
warn('[mongokit] searchMode "regex" requires searchFields to be specified. Falling back to "text" mode.');
|
|
1204
1142
|
this.options.searchMode = "text";
|
|
1205
1143
|
}
|
|
1206
1144
|
this.dangerousOperators = [
|
|
@@ -1240,7 +1178,7 @@ var QueryParser = class {
|
|
|
1240
1178
|
parsedLimit = 20;
|
|
1241
1179
|
}
|
|
1242
1180
|
if (parsedLimit > this.options.maxLimit) {
|
|
1243
|
-
|
|
1181
|
+
warn(`[mongokit] Limit ${parsedLimit} exceeds maximum ${this.options.maxLimit}, capping to max`);
|
|
1244
1182
|
parsedLimit = this.options.maxLimit;
|
|
1245
1183
|
}
|
|
1246
1184
|
const sanitizedSearch = this._sanitizeSearch(search);
|
|
@@ -1339,7 +1277,7 @@ var QueryParser = class {
|
|
|
1339
1277
|
lookups.push(lookupConfig);
|
|
1340
1278
|
}
|
|
1341
1279
|
} catch (error) {
|
|
1342
|
-
|
|
1280
|
+
warn(`[mongokit] Invalid lookup config for ${collectionName}:`, error);
|
|
1343
1281
|
}
|
|
1344
1282
|
}
|
|
1345
1283
|
return lookups;
|
|
@@ -1350,8 +1288,13 @@ var QueryParser = class {
|
|
|
1350
1288
|
_parseSingleLookup(collectionName, config) {
|
|
1351
1289
|
if (!config) return null;
|
|
1352
1290
|
if (typeof config === "string") {
|
|
1291
|
+
const from = this._pluralize(collectionName);
|
|
1292
|
+
if (this.options.allowedLookupCollections && !this.options.allowedLookupCollections.includes(from)) {
|
|
1293
|
+
warn(`[mongokit] Blocked lookup to disallowed collection: ${from}`);
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1353
1296
|
return {
|
|
1354
|
-
from
|
|
1297
|
+
from,
|
|
1355
1298
|
localField: `${collectionName}${this._capitalize(config)}`,
|
|
1356
1299
|
foreignField: config,
|
|
1357
1300
|
as: collectionName,
|
|
@@ -1363,8 +1306,12 @@ var QueryParser = class {
|
|
|
1363
1306
|
const from = opts.from || this._pluralize(collectionName);
|
|
1364
1307
|
const localField = opts.localField;
|
|
1365
1308
|
const foreignField = opts.foreignField;
|
|
1309
|
+
if (this.options.allowedLookupCollections && !this.options.allowedLookupCollections.includes(from)) {
|
|
1310
|
+
warn(`[mongokit] Blocked lookup to disallowed collection: ${from}`);
|
|
1311
|
+
return null;
|
|
1312
|
+
}
|
|
1366
1313
|
if (!localField || !foreignField) {
|
|
1367
|
-
|
|
1314
|
+
warn(`[mongokit] Lookup requires localField and foreignField for ${collectionName}`);
|
|
1368
1315
|
return null;
|
|
1369
1316
|
}
|
|
1370
1317
|
return {
|
|
@@ -1373,7 +1320,7 @@ var QueryParser = class {
|
|
|
1373
1320
|
foreignField,
|
|
1374
1321
|
as: opts.as || collectionName,
|
|
1375
1322
|
single: opts.single === true || opts.single === "true",
|
|
1376
|
-
...opts.pipeline && Array.isArray(opts.pipeline) ? { pipeline: opts.pipeline } : {}
|
|
1323
|
+
...opts.pipeline && Array.isArray(opts.pipeline) ? { pipeline: this._sanitizePipeline(opts.pipeline) } : {}
|
|
1377
1324
|
};
|
|
1378
1325
|
}
|
|
1379
1326
|
return null;
|
|
@@ -1411,7 +1358,7 @@ var QueryParser = class {
|
|
|
1411
1358
|
pipeline.push({ $project: config });
|
|
1412
1359
|
}
|
|
1413
1360
|
} catch (error) {
|
|
1414
|
-
|
|
1361
|
+
warn(`[mongokit] Invalid aggregation stage ${stage}:`, error);
|
|
1415
1362
|
}
|
|
1416
1363
|
}
|
|
1417
1364
|
return pipeline.length > 0 ? pipeline : void 0;
|
|
@@ -1477,7 +1424,7 @@ var QueryParser = class {
|
|
|
1477
1424
|
const populateOptions = [];
|
|
1478
1425
|
for (const [path, config] of Object.entries(populateObj)) {
|
|
1479
1426
|
if (path.startsWith("$") || this.dangerousOperators.includes(path)) {
|
|
1480
|
-
|
|
1427
|
+
warn(`[mongokit] Blocked dangerous populate path: ${path}`);
|
|
1481
1428
|
continue;
|
|
1482
1429
|
}
|
|
1483
1430
|
const option = this._parseSinglePopulate(path, config);
|
|
@@ -1494,7 +1441,7 @@ var QueryParser = class {
|
|
|
1494
1441
|
*/
|
|
1495
1442
|
_parseSinglePopulate(path, config, depth = 0) {
|
|
1496
1443
|
if (depth > 5) {
|
|
1497
|
-
|
|
1444
|
+
warn(`[mongokit] Populate depth exceeds maximum (5), truncating at path: ${path}`);
|
|
1498
1445
|
return { path };
|
|
1499
1446
|
}
|
|
1500
1447
|
if (typeof config === "string") {
|
|
@@ -1566,14 +1513,14 @@ var QueryParser = class {
|
|
|
1566
1513
|
*/
|
|
1567
1514
|
_parseFilters(filters, depth = 0) {
|
|
1568
1515
|
if (depth > this.options.maxFilterDepth) {
|
|
1569
|
-
|
|
1516
|
+
warn(`[mongokit] Filter depth ${depth} exceeds maximum ${this.options.maxFilterDepth}, truncating`);
|
|
1570
1517
|
return {};
|
|
1571
1518
|
}
|
|
1572
1519
|
const parsedFilters = {};
|
|
1573
1520
|
const regexFields = {};
|
|
1574
1521
|
for (const [key, value] of Object.entries(filters)) {
|
|
1575
1522
|
if (this.dangerousOperators.includes(key) || key.startsWith("$") && !["$or", "$and"].includes(key)) {
|
|
1576
|
-
|
|
1523
|
+
warn(`[mongokit] Blocked dangerous operator: ${key}`);
|
|
1577
1524
|
continue;
|
|
1578
1525
|
}
|
|
1579
1526
|
if (["page", "limit", "sort", "populate", "search", "select", "lean", "includeDeleted", "lookup", "aggregate", "or", "OR", "$or"].includes(key)) {
|
|
@@ -1583,7 +1530,7 @@ var QueryParser = class {
|
|
|
1583
1530
|
if (operatorMatch) {
|
|
1584
1531
|
const [, , operator] = operatorMatch;
|
|
1585
1532
|
if (this.dangerousOperators.includes("$" + operator)) {
|
|
1586
|
-
|
|
1533
|
+
warn(`[mongokit] Blocked dangerous operator: ${operator}`);
|
|
1587
1534
|
continue;
|
|
1588
1535
|
}
|
|
1589
1536
|
this._handleOperatorSyntax(parsedFilters, regexFields, operatorMatch, value);
|
|
@@ -1622,7 +1569,7 @@ var QueryParser = class {
|
|
|
1622
1569
|
}
|
|
1623
1570
|
const mongoOperator = this._toMongoOperator(operator);
|
|
1624
1571
|
if (this.dangerousOperators.includes(mongoOperator)) {
|
|
1625
|
-
|
|
1572
|
+
warn(`[mongokit] Blocked dangerous operator: ${mongoOperator}`);
|
|
1626
1573
|
return;
|
|
1627
1574
|
}
|
|
1628
1575
|
if (mongoOperator === "$eq") {
|
|
@@ -1655,7 +1602,7 @@ var QueryParser = class {
|
|
|
1655
1602
|
*/
|
|
1656
1603
|
_handleBracketSyntax(field, operators, parsedFilters, depth = 0) {
|
|
1657
1604
|
if (depth > this.options.maxFilterDepth) {
|
|
1658
|
-
|
|
1605
|
+
warn(`[mongokit] Nested filter depth exceeds maximum, skipping field: ${field}`);
|
|
1659
1606
|
return;
|
|
1660
1607
|
}
|
|
1661
1608
|
if (!parsedFilters[field]) {
|
|
@@ -1714,11 +1661,11 @@ var QueryParser = class {
|
|
|
1714
1661
|
if (pattern === null || pattern === void 0) return null;
|
|
1715
1662
|
const patternStr = String(pattern);
|
|
1716
1663
|
if (patternStr.length > this.options.maxRegexLength) {
|
|
1717
|
-
|
|
1664
|
+
warn(`[mongokit] Regex pattern too long, truncating`);
|
|
1718
1665
|
return new RegExp(this._escapeRegex(patternStr.substring(0, this.options.maxRegexLength)), flags);
|
|
1719
1666
|
}
|
|
1720
1667
|
if (this.dangerousRegexPatterns.test(patternStr)) {
|
|
1721
|
-
|
|
1668
|
+
warn("[mongokit] Potentially dangerous regex pattern, escaping");
|
|
1722
1669
|
return new RegExp(this._escapeRegex(patternStr), flags);
|
|
1723
1670
|
}
|
|
1724
1671
|
try {
|
|
@@ -1738,7 +1685,7 @@ var QueryParser = class {
|
|
|
1738
1685
|
const sanitized = {};
|
|
1739
1686
|
for (const [key, value] of Object.entries(config)) {
|
|
1740
1687
|
if (this.dangerousOperators.includes(key)) {
|
|
1741
|
-
|
|
1688
|
+
warn(`[mongokit] Blocked dangerous operator in aggregation: ${key}`);
|
|
1742
1689
|
continue;
|
|
1743
1690
|
}
|
|
1744
1691
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
@@ -1756,12 +1703,65 @@ var QueryParser = class {
|
|
|
1756
1703
|
}
|
|
1757
1704
|
return sanitized;
|
|
1758
1705
|
}
|
|
1706
|
+
/**
|
|
1707
|
+
* Sanitize pipeline stages for use in $lookup.
|
|
1708
|
+
* Blocks dangerous stages ($out, $merge, etc.) and recursively sanitizes
|
|
1709
|
+
* operator expressions within $match, $addFields, and $set stages.
|
|
1710
|
+
*/
|
|
1711
|
+
_sanitizePipeline(stages) {
|
|
1712
|
+
const blockedStages = ["$out", "$merge", "$unionWith", "$collStats", "$currentOp", "$listSessions"];
|
|
1713
|
+
const sanitized = [];
|
|
1714
|
+
for (const stage of stages) {
|
|
1715
|
+
if (!stage || typeof stage !== "object") continue;
|
|
1716
|
+
const entries = Object.entries(stage);
|
|
1717
|
+
if (entries.length !== 1) continue;
|
|
1718
|
+
const [op, config] = entries[0];
|
|
1719
|
+
if (blockedStages.includes(op)) {
|
|
1720
|
+
warn(`[mongokit] Blocked dangerous pipeline stage in lookup: ${op}`);
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
if (op === "$match" && typeof config === "object" && config !== null) {
|
|
1724
|
+
sanitized.push({ $match: this._sanitizeMatchConfig(config) });
|
|
1725
|
+
} else if ((op === "$addFields" || op === "$set") && typeof config === "object" && config !== null) {
|
|
1726
|
+
sanitized.push({ [op]: this._sanitizeExpressions(config) });
|
|
1727
|
+
} else {
|
|
1728
|
+
sanitized.push(stage);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
return sanitized;
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Recursively sanitize expression objects, blocking dangerous operators
|
|
1735
|
+
* like $where, $function, $accumulator inside $addFields/$set stages.
|
|
1736
|
+
*/
|
|
1737
|
+
_sanitizeExpressions(config) {
|
|
1738
|
+
const sanitized = {};
|
|
1739
|
+
for (const [key, value] of Object.entries(config)) {
|
|
1740
|
+
if (this.dangerousOperators.includes(key)) {
|
|
1741
|
+
warn(`[mongokit] Blocked dangerous operator in pipeline expression: ${key}`);
|
|
1742
|
+
continue;
|
|
1743
|
+
}
|
|
1744
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1745
|
+
sanitized[key] = this._sanitizeExpressions(value);
|
|
1746
|
+
} else if (Array.isArray(value)) {
|
|
1747
|
+
sanitized[key] = value.map((item) => {
|
|
1748
|
+
if (item && typeof item === "object" && !Array.isArray(item)) {
|
|
1749
|
+
return this._sanitizeExpressions(item);
|
|
1750
|
+
}
|
|
1751
|
+
return item;
|
|
1752
|
+
});
|
|
1753
|
+
} else {
|
|
1754
|
+
sanitized[key] = value;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
return sanitized;
|
|
1758
|
+
}
|
|
1759
1759
|
_sanitizeSearch(search) {
|
|
1760
1760
|
if (search === null || search === void 0 || search === "") return void 0;
|
|
1761
1761
|
let searchStr = String(search).trim();
|
|
1762
1762
|
if (!searchStr) return void 0;
|
|
1763
1763
|
if (searchStr.length > this.options.maxSearchLength) {
|
|
1764
|
-
|
|
1764
|
+
warn(`[mongokit] Search query too long, truncating`);
|
|
1765
1765
|
searchStr = searchStr.substring(0, this.options.maxSearchLength);
|
|
1766
1766
|
}
|
|
1767
1767
|
return searchStr;
|
|
@@ -1890,4 +1890,4 @@ var index_default = Repository;
|
|
|
1890
1890
|
* ```
|
|
1891
1891
|
*/
|
|
1892
1892
|
|
|
1893
|
-
export { AggregationBuilder,
|
|
1893
|
+
export { AggregationBuilder, QueryParser, Repository, createRepository, index_default as default };
|