@classytic/mongokit 3.5.5 → 3.5.6
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 +99 -5
- package/dist/actions/index.d.mts +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/id-resolution-LZ-cCwR5.mjs +81 -0
- package/dist/{index-Buj7P006.d.mts → index-DmVbUzDL.d.mts} +1 -1
- package/dist/index.d.mts +21 -6
- package/dist/index.mjs +3 -3
- package/dist/{mongooseToJsonSchema-DpjhFfaO.mjs → mongooseToJsonSchema-8Cyu3NFJ.mjs} +50 -45
- package/dist/{mongooseToJsonSchema-BlDfbg_0.d.mts → mongooseToJsonSchema-DQMU0Dlv.d.mts} +4 -3
- package/dist/pagination/PaginationEngine.d.mts +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/query/primitives/coercion.d.mts +1 -1
- package/dist/query/primitives/coercion.mjs +4 -3
- package/dist/query/primitives/indexes.d.mts +1 -1
- package/dist/utils/index.d.mts +60 -3
- package/dist/utils/index.mjs +4 -3
- package/dist/{validation-chain.plugin-Dn41aAbK.d.mts → validation-chain.plugin-BtmT-FFB.d.mts} +1 -1
- package/dist/{validation-chain.plugin-BCPh2UZG.mjs → validation-chain.plugin-DCYjnPau.mjs} +57 -33
- package/package.json +2 -2
- /package/dist/{coercion-01tEHsKF.d.mts → coercion-B19MD38s.d.mts} +0 -0
- /package/dist/{field-selection-reyDRzXf.mjs → field-selection-C5o7jyyA.mjs} +0 -0
- /package/dist/{indexes-CiIT-Zli.d.mts → indexes-qIBCZ3mY.d.mts} +0 -0
- /package/dist/{types-CWUL0yiw.d.mts → types-BwO4u3JN.d.mts} +0 -0
package/README.md
CHANGED
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
- **17 built-in plugins** - Caching, soft delete, audit trail, validation, multi-tenant, custom IDs, observability, Elasticsearch, and more
|
|
16
16
|
- **Distributed cache safety** - List cache versions stored in the adapter (Redis) for multi-pod correctness
|
|
17
17
|
- **Search governance** - Text index guard (throws `400` if no index), allowlisted sort/filter fields, ReDoS protection
|
|
18
|
+
- **Geo queries** - `[near]`, `[nearSphere]`, `[withinRadius]`, `[geoWithin]` URL operators with auto sort/count handling
|
|
19
|
+
- **Schema-aware coercion** - Pass `schema: Model.schema` to QueryParser for deterministic type-safe filter values
|
|
20
|
+
- **String/UUID/Number `_id` support** - Works out of the box with any `_id` type, not just ObjectId
|
|
21
|
+
- **Query primitives** - Composable pure functions (`coercion`, `geo`, `indexes`) importable standalone for custom parsers
|
|
18
22
|
- **Vector search** - MongoDB Atlas `$vectorSearch` with auto-embedding and multimodal support
|
|
19
23
|
- **TypeScript first** - Full type safety with discriminated unions, typed events, and field autocomplete
|
|
20
|
-
- **Extensively tested** — battle-tested and production-ready
|
|
24
|
+
- **Extensively tested** — 1500+ tests, Mongoose 9.4.1 aligned, battle-tested and production-ready
|
|
21
25
|
|
|
22
26
|
## Installation
|
|
23
27
|
|
|
@@ -25,7 +29,7 @@
|
|
|
25
29
|
npm install @classytic/mongokit mongoose
|
|
26
30
|
```
|
|
27
31
|
|
|
28
|
-
> Requires Mongoose
|
|
32
|
+
> Requires Mongoose `>=9.4.1` | Node.js `>=22`
|
|
29
33
|
|
|
30
34
|
## Quick Start
|
|
31
35
|
|
|
@@ -231,6 +235,89 @@ User input is regex-escaped (literal substring match), and any pre-existing
|
|
|
231
235
|
> (Express/Nest/raw handlers), or both for defense in depth — they will not
|
|
232
236
|
> conflict because the parser clears `parsed.search` before it reaches the repo.
|
|
233
237
|
|
|
238
|
+
### Geo queries
|
|
239
|
+
|
|
240
|
+
QueryParser supports 4 geo operators that compile to canonical GeoJSON-shaped
|
|
241
|
+
MongoDB queries. All validate coordinate ranges and reject malformed input by
|
|
242
|
+
dropping the filter (not silently widening).
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
const parser = new QueryParser({ schema: PlaceModel.schema });
|
|
246
|
+
|
|
247
|
+
// Distance-sorted (implicit distance ordering, no explicit sort allowed)
|
|
248
|
+
const near = parser.parse({ "location[near]": "-73.98,40.76,5000" });
|
|
249
|
+
// → { location: { $near: { $geometry: { type: 'Point', coordinates: [-73.98, 40.76] }, $maxDistance: 5000 } } }
|
|
250
|
+
|
|
251
|
+
// Count-compatible radius — use this for paginated getAll (supports sort + count)
|
|
252
|
+
const radius = parser.parse({ "location[withinRadius]": "-73.98,40.76,5000" });
|
|
253
|
+
// → { location: { $geoWithin: { $centerSphere: [[-73.98, 40.76], 0.000783...] } } }
|
|
254
|
+
|
|
255
|
+
// Bounding box (works without a 2dsphere index)
|
|
256
|
+
const box = parser.parse({ "location[geoWithin]": "-74,40.7,-73.9,40.8" });
|
|
257
|
+
// → { location: { $geoWithin: { $box: [[-74, 40.7], [-73.9, 40.8]] } } }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Repository integration:** `Repository.getAll` auto-detects `$near` /
|
|
261
|
+
`$nearSphere` in filters and handles the MongoDB constraints transparently:
|
|
262
|
+
|
|
263
|
+
- Skips the default `-createdAt` sort (MongoDB forbids explicit sort with `$near`)
|
|
264
|
+
- Warns and drops any caller-supplied sort that conflicts
|
|
265
|
+
- Rewrites the count query via `$geoWithin: $centerSphere` for accurate `total`
|
|
266
|
+
- Falls back to `countStrategy: 'none'` for unbounded `$near` (no `$maxDistance`)
|
|
267
|
+
- All plugins (multi-tenant, soft-delete, cache, populate) compose unchanged
|
|
268
|
+
|
|
269
|
+
Use `[near]` when you need distance-sorted results. Use `[withinRadius]` when
|
|
270
|
+
you need paginated lists with `total` and custom sort.
|
|
271
|
+
|
|
272
|
+
### String / UUID / Number `_id`
|
|
273
|
+
|
|
274
|
+
Schemas that declare `_id` as `String`, `Number`, or `UUID` instead of the
|
|
275
|
+
default `ObjectId` work out of the box — no configuration needed:
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
const SessionSchema = new Schema({
|
|
279
|
+
_id: { type: String, default: () => randomUUID() },
|
|
280
|
+
userId: { type: String, required: true },
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const repo = new Repository(SessionModel);
|
|
284
|
+
await repo.create({ userId: "user-1" }); // _id auto-generated as UUID
|
|
285
|
+
await repo.getById("550e8400-e29b-..."); // works — no ObjectId validation
|
|
286
|
+
await repo.update("550e8400-e29b-...", { ... }); // works
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The `id-resolution` utility detects the `_id` type from the schema and
|
|
290
|
+
validates accordingly. Schema generation also includes String `_id` as an
|
|
291
|
+
optional field in `createBody` so Fastify/AJV validation accepts user-supplied
|
|
292
|
+
IDs. Available standalone:
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { getSchemaIdType, isValidIdForType } from "@classytic/mongokit/utils";
|
|
296
|
+
|
|
297
|
+
const idType = getSchemaIdType(SessionModel.schema); // 'string'
|
|
298
|
+
isValidIdForType("550e8400-...", idType); // true
|
|
299
|
+
isValidIdForType("not-valid", "objectid"); // false
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Query primitives
|
|
303
|
+
|
|
304
|
+
Pure, composable functions that power QueryParser internals — importable
|
|
305
|
+
standalone for custom parsers, Arc controllers, migration scripts, and AI
|
|
306
|
+
agents that build queries programmatically:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
// Geo
|
|
310
|
+
import { parseGeoFilter, hasNearOperator } from "@classytic/mongokit/query/primitives/geo";
|
|
311
|
+
// Coercion
|
|
312
|
+
import { coerceFieldValue, buildFieldTypeMap } from "@classytic/mongokit/query/primitives/coercion";
|
|
313
|
+
// Schema indexes
|
|
314
|
+
import { extractSchemaIndexes } from "@classytic/mongokit/query/primitives/indexes";
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Each primitive is independently unit-tested, tree-shake friendly (no barrel),
|
|
318
|
+
and decoupled from Mongoose — they accept structural types, not concrete
|
|
319
|
+
Mongoose instances.
|
|
320
|
+
|
|
234
321
|
## Plugins
|
|
235
322
|
|
|
236
323
|
### Using Plugins
|
|
@@ -1206,6 +1293,12 @@ GET /users?sort=-createdAt,name
|
|
|
1206
1293
|
# Search (default: text index; or configure searchMode: 'regex' / 'auto')
|
|
1207
1294
|
GET /users?search=john
|
|
1208
1295
|
|
|
1296
|
+
# Geo queries (requires 2dsphere index for near/nearSphere)
|
|
1297
|
+
GET /places?location[near]=-73.98,40.76,5000 # Within 5km, distance-sorted
|
|
1298
|
+
GET /places?location[nearSphere]=-73.98,40.76,5000 # Spherical variant
|
|
1299
|
+
GET /places?location[withinRadius]=-73.98,40.76,5000 # Count-compatible radius (use for paginated lists)
|
|
1300
|
+
GET /places?location[geoWithin]=-74,40.7,-73.9,40.8 # Bounding box (no index required)
|
|
1301
|
+
|
|
1209
1302
|
# Simple populate
|
|
1210
1303
|
GET /posts?populate=author,category
|
|
1211
1304
|
|
|
@@ -1601,14 +1694,15 @@ await repo.getById('my-chat-uuid', { idField: 'chatId' }); // by chatId
|
|
|
1601
1694
|
|
|
1602
1695
|
All plugins respect `idField`: soft-delete, cascade, audit-log, audit-trail, validation-chain, elastic, cache, observability.
|
|
1603
1696
|
|
|
1604
|
-
##
|
|
1697
|
+
## Mongoose Compatibility
|
|
1605
1698
|
|
|
1606
|
-
|
|
1699
|
+
Requires Mongoose `>=9.4.1`. Tested and aligned with Mongoose 9.4.1. The package:
|
|
1607
1700
|
|
|
1608
1701
|
- Uses its own event system (not Mongoose middleware)
|
|
1609
1702
|
- Defines its own `FilterQuery` type (unaffected by Mongoose 9 rename)
|
|
1610
1703
|
- Properly gates update pipelines (safe for Mongoose 9's stricter defaults)
|
|
1611
|
-
-
|
|
1704
|
+
- Reads `embeddedSchemaType.instance` for array element types (Mongoose 9.x)
|
|
1705
|
+
- 1500+ tests pass on Mongoose 9.4.1
|
|
1612
1706
|
|
|
1613
1707
|
## License
|
|
1614
1708
|
|
package/dist/actions/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-
|
|
1
|
+
import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-DmVbUzDL.mjs";
|
|
2
2
|
export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
|
package/dist/ai/index.d.mts
CHANGED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
//#region src/utils/id-resolution.ts
|
|
3
|
+
/**
|
|
4
|
+
* ID Resolution Primitive
|
|
5
|
+
*
|
|
6
|
+
* Pure functions for detecting a Mongoose schema's _id type and validating
|
|
7
|
+
* id values against it. Used by Repository.getById to decide whether to
|
|
8
|
+
* run ObjectId validation (for ObjectId schemas) or skip it (for String /
|
|
9
|
+
* Number / UUID schemas).
|
|
10
|
+
*
|
|
11
|
+
* Also usable standalone — Arc controllers, custom repositories, migration
|
|
12
|
+
* scripts, or test harnesses can import these directly to make the same
|
|
13
|
+
* decision without depending on Repository.
|
|
14
|
+
*
|
|
15
|
+
* Design: no `this`, no Model, no DB calls. Only reads `schema.paths._id.instance`.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Check if a Mongoose SchemaType `instance` string is an ObjectId variant.
|
|
19
|
+
* Mongoose 9.x uses `'ObjectId'`, older versions use `'ObjectID'`. This
|
|
20
|
+
* is the single source of truth — use it instead of inline
|
|
21
|
+
* `instance === 'ObjectId' || instance === 'ObjectID'` checks.
|
|
22
|
+
*/
|
|
23
|
+
function isObjectIdInstance(instance) {
|
|
24
|
+
return instance === "ObjectId" || instance === "ObjectID";
|
|
25
|
+
}
|
|
26
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
27
|
+
/**
|
|
28
|
+
* Detect the _id path type from a Mongoose schema.
|
|
29
|
+
*
|
|
30
|
+
* Returns 'objectid' as the safe default when:
|
|
31
|
+
* - No schema is provided (mock models, test harnesses)
|
|
32
|
+
* - The schema has no `paths._id` (auto-generated ObjectId)
|
|
33
|
+
* - The instance string is unrecognized
|
|
34
|
+
*
|
|
35
|
+
* This safe-default means existing callers that don't set _id explicitly
|
|
36
|
+
* get the same ObjectId validation they always had — the behavioral change
|
|
37
|
+
* is strictly opt-in via schema declaration.
|
|
38
|
+
*/
|
|
39
|
+
function getSchemaIdType(schema) {
|
|
40
|
+
const instance = schema?.paths?._id?.instance;
|
|
41
|
+
if (!instance) return "objectid";
|
|
42
|
+
if (isObjectIdInstance(instance)) return "objectid";
|
|
43
|
+
switch (instance) {
|
|
44
|
+
case "String": return "string";
|
|
45
|
+
case "Number": return "number";
|
|
46
|
+
case "UUID": return "uuid";
|
|
47
|
+
default: return "unknown";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validate an id value against a known _id type — WITHOUT hitting the DB.
|
|
52
|
+
*
|
|
53
|
+
* Returns `true` when the value is structurally valid for the given type.
|
|
54
|
+
* Returns `false` when it's definitely invalid (Repository should return
|
|
55
|
+
* 404 / null immediately rather than wasting a round-trip).
|
|
56
|
+
*
|
|
57
|
+
* For `'string'` and `'unknown'` types, validation is intentionally
|
|
58
|
+
* permissive: any truthy, non-empty value is accepted. The DB is the
|
|
59
|
+
* authority for whether the document exists — we only reject values that
|
|
60
|
+
* are structurally impossible (null, undefined, empty string).
|
|
61
|
+
*/
|
|
62
|
+
function isValidIdForType(id, idType) {
|
|
63
|
+
if (id === null || id === void 0) return false;
|
|
64
|
+
switch (idType) {
|
|
65
|
+
case "objectid": {
|
|
66
|
+
if (typeof id === "object") return mongoose.Types.ObjectId.isValid(id);
|
|
67
|
+
const s = String(id);
|
|
68
|
+
return s.length > 0 && mongoose.Types.ObjectId.isValid(s);
|
|
69
|
+
}
|
|
70
|
+
case "string": return String(id).length > 0;
|
|
71
|
+
case "number": {
|
|
72
|
+
if (typeof id === "number") return Number.isFinite(id);
|
|
73
|
+
const n = Number(id);
|
|
74
|
+
return Number.isFinite(n);
|
|
75
|
+
}
|
|
76
|
+
case "uuid": return typeof id === "string" && UUID_RE.test(id);
|
|
77
|
+
case "unknown": return String(id).length > 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { isObjectIdInstance as n, isValidIdForType as r, getSchemaIdType as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as ReadPreferenceType, At as LookupOptions, D as GroupResult, V as ObjectId, W as OperationOptions, Z as PopulateSpec, _t as UpdateOptions, a as AnyDocument, b as DeleteResult, g as CreateOptions, gt as UpdateManyResult, pt as SortSpec, st as SelectSpec, vt as UpdateWithValidationResult, z as MinMaxResult } from "./types-
|
|
1
|
+
import { $ as ReadPreferenceType, At as LookupOptions, D as GroupResult, V as ObjectId, W as OperationOptions, Z as PopulateSpec, _t as UpdateOptions, a as AnyDocument, b as DeleteResult, g as CreateOptions, gt as UpdateManyResult, pt as SortSpec, st as SelectSpec, vt as UpdateWithValidationResult, z as MinMaxResult } from "./types-BwO4u3JN.mjs";
|
|
2
2
|
import { ClientSession, Model, PipelineStage } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/actions/aggregate.d.ts
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { $ as ReadPreferenceType, A as InferDocument, At as LookupOptions, B as NonNullableFields, C as EventPayload, Ct as WithPlugins, D as GroupResult, Dt as IRequestContext, E as FieldRules, Et as IControllerResponse, F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, I as Logger, J as Plugin, K as PaginationResult, L as LookupPopulateOptions, M as JsonSchema, N as KeysOfType, O as HookMode, Ot as IResponseFormatter, P as KeysetPaginationOptions, Q as ReadOptions, R as LookupPopulateResult, S as EventHandlers, St as ValidatorDefinition, T as FieldPreset, Tt as IController, U as OffsetPaginationResult, V as ObjectId, W as OperationOptions, X as PluginType, Y as PluginFunction, Z as PopulateSpec, _ as CrudSchemas, _t as UpdateOptions, a as AnyDocument, at as RequiredBy, b as DeleteResult, bt as ValidationChainOptions, c as CacheOperationOptions, ct as SessionOptions, d as CacheableOptions, dt as SoftDeleteRepository, et as RepositoryContext, f as CascadeOptions, ft as SortDirection, g as CreateOptions, gt as UpdateManyResult, h as CreateInput, ht as UpdateInput, i as AllPluginMethods, it as RepositoryOptions, j as InferRawDoc, k as HttpError, kt as LookupBuilder, l as CacheOptions, lt as SoftDeleteFilterMode, m as CollationOptions, mt as Strict, n as AggregatePaginationOptions, nt as RepositoryInstance, o as AnyModel, ot as SchemaBuilderOptions, p as CascadeRelation, pt as SortSpec, q as PartialBy, r as AggregatePaginationResult, rt as RepositoryOperation, s as CacheAdapter, st as SelectSpec, t as AggregateOptions, tt as RepositoryEvent, u as CacheStats, ut as SoftDeleteOptions, v as DecodedCursor, vt as UpdateWithValidationResult, w as EventPhase, wt as WithTransactionOptions, x as DocField, xt as ValidationResult, y as DeepPartial, yt as UserContext, z as MinMaxResult } from "./types-
|
|
2
|
-
import { t as index_d_exports } from "./index-
|
|
1
|
+
import { $ as ReadPreferenceType, A as InferDocument, At as LookupOptions, B as NonNullableFields, C as EventPayload, Ct as WithPlugins, D as GroupResult, Dt as IRequestContext, E as FieldRules, Et as IControllerResponse, F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, I as Logger, J as Plugin, K as PaginationResult, L as LookupPopulateOptions, M as JsonSchema, N as KeysOfType, O as HookMode, Ot as IResponseFormatter, P as KeysetPaginationOptions, Q as ReadOptions, R as LookupPopulateResult, S as EventHandlers, St as ValidatorDefinition, T as FieldPreset, Tt as IController, U as OffsetPaginationResult, V as ObjectId, W as OperationOptions, X as PluginType, Y as PluginFunction, Z as PopulateSpec, _ as CrudSchemas, _t as UpdateOptions, a as AnyDocument, at as RequiredBy, b as DeleteResult, bt as ValidationChainOptions, c as CacheOperationOptions, ct as SessionOptions, d as CacheableOptions, dt as SoftDeleteRepository, et as RepositoryContext, f as CascadeOptions, ft as SortDirection, g as CreateOptions, gt as UpdateManyResult, h as CreateInput, ht as UpdateInput, i as AllPluginMethods, it as RepositoryOptions, j as InferRawDoc, k as HttpError, kt as LookupBuilder, l as CacheOptions, lt as SoftDeleteFilterMode, m as CollationOptions, mt as Strict, n as AggregatePaginationOptions, nt as RepositoryInstance, o as AnyModel, ot as SchemaBuilderOptions, p as CascadeRelation, pt as SortSpec, q as PartialBy, r as AggregatePaginationResult, rt as RepositoryOperation, s as CacheAdapter, st as SelectSpec, t as AggregateOptions, tt as RepositoryEvent, u as CacheStats, ut as SoftDeleteOptions, v as DecodedCursor, vt as UpdateWithValidationResult, w as EventPhase, wt as WithTransactionOptions, x as DocField, xt as ValidationResult, y as DeepPartial, yt as UserContext, z as MinMaxResult } from "./types-BwO4u3JN.mjs";
|
|
2
|
+
import { t as index_d_exports } from "./index-DmVbUzDL.mjs";
|
|
3
3
|
import { PaginationEngine } from "./pagination/PaginationEngine.mjs";
|
|
4
|
-
import { A as dateSequentialId, B as AuditEntry, C as elasticSearchPlugin, D as PrefixedIdOptions, E as IdGenerator, F as CacheMethods, G as AuditTrailOptions, H as AuditQueryOptions, I as cachePlugin, J as auditLogPlugin, K as AuditTrailQuery, L as BatchOperationsMethods, M as prefixedId, N as sequentialId, O as SequentialIdOptions, P as cascadePlugin, R as BulkWriteResult, S as ElasticSearchOptions, T as DateSequentialIdOptions, U as AuditQueryResult, V as AuditOperation, W as AuditTrailMethods, X as aggregateHelpersPlugin, Y as AggregateHelpersMethods, _ as MongoOperationsMethods, a as uniqueField, b as methodRegistryPlugin, c as SubdocumentMethods, d as softDeletePlugin, f as ObservabilityOptions, g as multiTenantPlugin, h as MultiTenantOptions, i as requireField, j as getNextSequence, k as customIdPlugin, l as subdocumentPlugin, m as observabilityPlugin, n as blockIf, o as validationChainPlugin, p as OperationMetric, q as auditTrailPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as SoftDeleteMethods, v as mongoOperationsPlugin, w as CustomIdOptions, x as fieldFilterPlugin, z as batchOperationsPlugin } from "./validation-chain.plugin-
|
|
5
|
-
import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-
|
|
6
|
-
import { n as SchemaIndexes } from "./indexes-
|
|
7
|
-
import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-
|
|
4
|
+
import { A as dateSequentialId, B as AuditEntry, C as elasticSearchPlugin, D as PrefixedIdOptions, E as IdGenerator, F as CacheMethods, G as AuditTrailOptions, H as AuditQueryOptions, I as cachePlugin, J as auditLogPlugin, K as AuditTrailQuery, L as BatchOperationsMethods, M as prefixedId, N as sequentialId, O as SequentialIdOptions, P as cascadePlugin, R as BulkWriteResult, S as ElasticSearchOptions, T as DateSequentialIdOptions, U as AuditQueryResult, V as AuditOperation, W as AuditTrailMethods, X as aggregateHelpersPlugin, Y as AggregateHelpersMethods, _ as MongoOperationsMethods, a as uniqueField, b as methodRegistryPlugin, c as SubdocumentMethods, d as softDeletePlugin, f as ObservabilityOptions, g as multiTenantPlugin, h as MultiTenantOptions, i as requireField, j as getNextSequence, k as customIdPlugin, l as subdocumentPlugin, m as observabilityPlugin, n as blockIf, o as validationChainPlugin, p as OperationMetric, q as auditTrailPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as SoftDeleteMethods, v as mongoOperationsPlugin, w as CustomIdOptions, x as fieldFilterPlugin, z as batchOperationsPlugin } from "./validation-chain.plugin-BtmT-FFB.mjs";
|
|
5
|
+
import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-B19MD38s.mjs";
|
|
6
|
+
import { n as SchemaIndexes } from "./indexes-qIBCZ3mY.mjs";
|
|
7
|
+
import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-DQMU0Dlv.mjs";
|
|
8
8
|
import * as _$mongoose from "mongoose";
|
|
9
9
|
import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
|
|
10
10
|
|
|
@@ -1111,6 +1111,21 @@ declare class Repository<TDoc = unknown> {
|
|
|
1111
1111
|
* 4. DEFAULT (500) — user-registered hooks
|
|
1112
1112
|
*/
|
|
1113
1113
|
_buildContext(operation: string, options: Record<string, unknown> | object): Promise<RepositoryContext>;
|
|
1114
|
+
/**
|
|
1115
|
+
* Resolve the effective sort for getAll. Handles $near conflict detection
|
|
1116
|
+
* (warns + drops explicit sort when $near is in filters).
|
|
1117
|
+
*/
|
|
1118
|
+
private _resolveSort;
|
|
1119
|
+
/**
|
|
1120
|
+
* Detect whether to use keyset (cursor) or offset (page) pagination.
|
|
1121
|
+
*/
|
|
1122
|
+
private _detectPaginationMode;
|
|
1123
|
+
/**
|
|
1124
|
+
* Build the MongoDB query filter with search merged in. Handles text
|
|
1125
|
+
* search ($text), regex search ($or of $regex), and the search-resolver
|
|
1126
|
+
* plugin contract (search already cleared by a before:getAll hook).
|
|
1127
|
+
*/
|
|
1128
|
+
private _buildSearchQuery;
|
|
1114
1129
|
/**
|
|
1115
1130
|
* Parse sort string or object
|
|
1116
1131
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -2,12 +2,12 @@ import { a as warn, n as parseDuplicateKeyError, r as configureLogger, t as crea
|
|
|
2
2
|
import { y as LookupBuilder } from "./update-AVfKWNGt.mjs";
|
|
3
3
|
import { t as actions_exports } from "./actions/index.mjs";
|
|
4
4
|
import { t as PaginationEngine } from "./PaginationEngine-CpsQl471.mjs";
|
|
5
|
-
import { A as aggregateHelpersPlugin, C as HOOK_PRIORITY, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, T as AggregationBuilder, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, w as Repository, x as cascadePlugin, y as prefixedId } from "./validation-chain.plugin-
|
|
5
|
+
import { A as aggregateHelpersPlugin, C as HOOK_PRIORITY, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, T as AggregationBuilder, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, w as Repository, x as cascadePlugin, y as prefixedId } from "./validation-chain.plugin-DCYjnPau.mjs";
|
|
6
6
|
import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
|
|
7
|
-
import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-
|
|
7
|
+
import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-C5o7jyyA.mjs";
|
|
8
8
|
import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
|
|
9
9
|
import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
|
|
10
|
-
import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-
|
|
10
|
+
import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-8Cyu3NFJ.mjs";
|
|
11
11
|
//#region src/query/QueryParser.ts
|
|
12
12
|
/**
|
|
13
13
|
* Modern Query Parser
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as isObjectIdInstance } from "./id-resolution-LZ-cCwR5.mjs";
|
|
1
2
|
import "mongoose";
|
|
2
3
|
//#region src/utils/memory-cache.ts
|
|
3
4
|
/**
|
|
@@ -93,28 +94,56 @@ function buildCrudSchemasFromModel(mongooseModel, options = {}) {
|
|
|
93
94
|
return buildCrudSchemasFromMongooseSchema(mongooseModel.schema, options);
|
|
94
95
|
}
|
|
95
96
|
/**
|
|
96
|
-
*
|
|
97
|
+
* Collect fields to omit from a generated schema based on field rules and
|
|
98
|
+
* explicit omit lists. This is the single source of truth for "which fields
|
|
99
|
+
* should NOT appear in the create/update body" — called by both the
|
|
100
|
+
* path-based and tree-based schema builders.
|
|
101
|
+
*/
|
|
102
|
+
function collectFieldsToOmit(options, purpose) {
|
|
103
|
+
const result = new Set([
|
|
104
|
+
"createdAt",
|
|
105
|
+
"updatedAt",
|
|
106
|
+
"__v"
|
|
107
|
+
]);
|
|
108
|
+
const rules = options?.fieldRules || {};
|
|
109
|
+
for (const [field, rule] of Object.entries(rules)) {
|
|
110
|
+
if (rule.systemManaged) result.add(field);
|
|
111
|
+
if (purpose === "update" && (rule.immutable || rule.immutableAfterCreate)) result.add(field);
|
|
112
|
+
}
|
|
113
|
+
const explicit = purpose === "create" ? options?.create?.omitFields : options?.update?.omitFields;
|
|
114
|
+
if (explicit) for (const f of explicit) result.add(f);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Apply omissions + optional-overrides to a built JSON schema in-place.
|
|
119
|
+
* Removes properties and adjusts the `required` array.
|
|
120
|
+
*/
|
|
121
|
+
function applyFieldRules(schema, fieldsToOmit, options) {
|
|
122
|
+
for (const field of fieldsToOmit) {
|
|
123
|
+
if (schema.properties?.[field]) delete schema.properties[field];
|
|
124
|
+
if (schema.required) schema.required = schema.required.filter((k) => k !== field);
|
|
125
|
+
}
|
|
126
|
+
const rules = options?.fieldRules || {};
|
|
127
|
+
for (const [field, rule] of Object.entries(rules)) if (rule.optional && schema.required) schema.required = schema.required.filter((k) => k !== field);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get fields that are immutable (cannot be updated).
|
|
131
|
+
* Delegates to `collectFieldsToOmit` internally.
|
|
97
132
|
*/
|
|
98
133
|
function getImmutableFields(options = {}) {
|
|
99
134
|
const immutable = [];
|
|
100
|
-
const
|
|
101
|
-
Object.entries(
|
|
102
|
-
|
|
103
|
-
});
|
|
104
|
-
(options?.update?.omitFields || []).forEach((f) => {
|
|
105
|
-
if (!immutable.includes(f)) immutable.push(f);
|
|
106
|
-
});
|
|
135
|
+
const rules = options?.fieldRules || {};
|
|
136
|
+
for (const [field, rule] of Object.entries(rules)) if (rule.immutable || rule.immutableAfterCreate) immutable.push(field);
|
|
137
|
+
for (const f of options?.update?.omitFields || []) if (!immutable.includes(f)) immutable.push(f);
|
|
107
138
|
return immutable;
|
|
108
139
|
}
|
|
109
140
|
/**
|
|
110
|
-
* Get fields that are system-managed (cannot be set by users)
|
|
141
|
+
* Get fields that are system-managed (cannot be set by users).
|
|
111
142
|
*/
|
|
112
143
|
function getSystemManagedFields(options = {}) {
|
|
113
144
|
const systemManaged = [];
|
|
114
|
-
const
|
|
115
|
-
Object.entries(
|
|
116
|
-
if (rules.systemManaged) systemManaged.push(field);
|
|
117
|
-
});
|
|
145
|
+
const rules = options?.fieldRules || {};
|
|
146
|
+
for (const [field, rule] of Object.entries(rules)) if (rule.systemManaged) systemManaged.push(field);
|
|
118
147
|
return systemManaged;
|
|
119
148
|
}
|
|
120
149
|
/**
|
|
@@ -161,7 +190,11 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
|
|
|
161
190
|
};
|
|
162
191
|
const rootFields = /* @__PURE__ */ new Map();
|
|
163
192
|
for (const [path, schemaType] of Object.entries(paths)) {
|
|
164
|
-
if (path === "
|
|
193
|
+
if (path === "__v") continue;
|
|
194
|
+
if (path === "_id") {
|
|
195
|
+
const instance = schemaType.instance;
|
|
196
|
+
if (!instance || isObjectIdInstance(instance)) continue;
|
|
197
|
+
}
|
|
165
198
|
const rootField = path.split(".")[0];
|
|
166
199
|
if (!rootFields.has(rootField)) rootFields.set(rootField, []);
|
|
167
200
|
rootFields.get(rootField)?.push({
|
|
@@ -183,30 +216,12 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
|
|
|
183
216
|
properties
|
|
184
217
|
};
|
|
185
218
|
if (required.length) schema.required = required;
|
|
186
|
-
|
|
187
|
-
"createdAt",
|
|
188
|
-
"updatedAt",
|
|
189
|
-
"__v"
|
|
190
|
-
]);
|
|
191
|
-
(options?.create?.omitFields || []).forEach((f) => {
|
|
192
|
-
fieldsToOmit.add(f);
|
|
193
|
-
});
|
|
194
|
-
const fieldRules = options?.fieldRules || {};
|
|
195
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
196
|
-
if (rules.systemManaged) fieldsToOmit.add(field);
|
|
197
|
-
});
|
|
198
|
-
fieldsToOmit.forEach((field) => {
|
|
199
|
-
if (schema.properties?.[field]) delete schema.properties[field];
|
|
200
|
-
if (schema.required) schema.required = schema.required.filter((k) => k !== field);
|
|
201
|
-
});
|
|
219
|
+
applyFieldRules(schema, collectFieldsToOmit(options, "create"), options);
|
|
202
220
|
const reqOv = options?.create?.requiredOverrides || {};
|
|
203
221
|
const optOv = options?.create?.optionalOverrides || {};
|
|
204
222
|
schema.required = schema.required || [];
|
|
205
223
|
for (const [k, v] of Object.entries(reqOv)) if (v && !schema.required.includes(k)) schema.required.push(k);
|
|
206
224
|
for (const [k, v] of Object.entries(optOv)) if (v && schema.required) schema.required = schema.required.filter((x) => x !== k);
|
|
207
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
208
|
-
if (rules.optional && schema.required) schema.required = schema.required.filter((x) => x !== field);
|
|
209
|
-
});
|
|
210
225
|
const schemaOverrides = options?.create?.schemaOverrides || {};
|
|
211
226
|
for (const [k, override] of Object.entries(schemaOverrides)) if (schema.properties?.[k]) schema.properties[k] = override;
|
|
212
227
|
if (options?.strictAdditionalProperties === true) schema.additionalProperties = false;
|
|
@@ -270,7 +285,7 @@ function schemaTypeToJsonSchema(schemaType) {
|
|
|
270
285
|
else if (instance === "Date") {
|
|
271
286
|
result.type = "string";
|
|
272
287
|
result.format = "date-time";
|
|
273
|
-
} else if (instance
|
|
288
|
+
} else if (isObjectIdInstance(instance)) {
|
|
274
289
|
result.type = "string";
|
|
275
290
|
result.pattern = "^[0-9a-fA-F]{24}$";
|
|
276
291
|
} else if (instance === "Array") {
|
|
@@ -285,17 +300,7 @@ function schemaTypeToJsonSchema(schemaType) {
|
|
|
285
300
|
function buildJsonSchemaForUpdate(createJson, options) {
|
|
286
301
|
const clone = JSON.parse(JSON.stringify(createJson));
|
|
287
302
|
delete clone.required;
|
|
288
|
-
|
|
289
|
-
(options?.update?.omitFields || []).forEach((f) => {
|
|
290
|
-
fieldsToOmit.add(f);
|
|
291
|
-
});
|
|
292
|
-
const fieldRules = options?.fieldRules || {};
|
|
293
|
-
Object.entries(fieldRules).forEach(([field, rules]) => {
|
|
294
|
-
if (rules.immutable || rules.immutableAfterCreate) fieldsToOmit.add(field);
|
|
295
|
-
});
|
|
296
|
-
fieldsToOmit.forEach((field) => {
|
|
297
|
-
if (clone.properties?.[field]) delete clone.properties[field];
|
|
298
|
-
});
|
|
303
|
+
applyFieldRules(clone, collectFieldsToOmit(options, "update"), options);
|
|
299
304
|
if (options?.strictAdditionalProperties === true) clone.additionalProperties = false;
|
|
300
305
|
if (options?.update?.requireAtLeastOne === true) clone.minProperties = 1;
|
|
301
306
|
return clone;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { T as FieldPreset, _ as CrudSchemas, k as HttpError, ot as SchemaBuilderOptions, s as CacheAdapter, xt as ValidationResult, yt as UserContext } from "./types-
|
|
1
|
+
import { T as FieldPreset, _ as CrudSchemas, k as HttpError, ot as SchemaBuilderOptions, s as CacheAdapter, xt as ValidationResult, yt as UserContext } from "./types-BwO4u3JN.mjs";
|
|
2
2
|
import mongoose, { Schema } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/error.d.ts
|
|
@@ -146,11 +146,12 @@ declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, opti
|
|
|
146
146
|
*/
|
|
147
147
|
declare function buildCrudSchemasFromModel(mongooseModel: mongoose.Model<unknown>, options?: SchemaBuilderOptions): CrudSchemas;
|
|
148
148
|
/**
|
|
149
|
-
* Get fields that are immutable (cannot be updated)
|
|
149
|
+
* Get fields that are immutable (cannot be updated).
|
|
150
|
+
* Delegates to `collectFieldsToOmit` internally.
|
|
150
151
|
*/
|
|
151
152
|
declare function getImmutableFields(options?: SchemaBuilderOptions): string[];
|
|
152
153
|
/**
|
|
153
|
-
* Get fields that are system-managed (cannot be set by users)
|
|
154
|
+
* Get fields that are system-managed (cannot be set by users).
|
|
154
155
|
*/
|
|
155
156
|
declare function getSystemManagedFields(options?: SchemaBuilderOptions): string[];
|
|
156
157
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, P as KeysetPaginationOptions, U as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-
|
|
1
|
+
import { F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, P as KeysetPaginationOptions, U as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-BwO4u3JN.mjs";
|
|
2
2
|
import { Model } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/pagination/PaginationEngine.d.ts
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as dateSequentialId, B as AuditEntry, C as elasticSearchPlugin, D as PrefixedIdOptions, E as IdGenerator, F as CacheMethods, G as AuditTrailOptions, H as AuditQueryOptions, I as cachePlugin, J as auditLogPlugin, K as AuditTrailQuery, L as BatchOperationsMethods, M as prefixedId, N as sequentialId, O as SequentialIdOptions, P as cascadePlugin, S as ElasticSearchOptions, T as DateSequentialIdOptions, U as AuditQueryResult, V as AuditOperation, W as AuditTrailMethods, X as aggregateHelpersPlugin, Y as AggregateHelpersMethods, _ as MongoOperationsMethods, a as uniqueField, b as methodRegistryPlugin, c as SubdocumentMethods, d as softDeletePlugin, f as ObservabilityOptions, g as multiTenantPlugin, h as MultiTenantOptions, i as requireField, j as getNextSequence, k as customIdPlugin, l as subdocumentPlugin, m as observabilityPlugin, n as blockIf, o as validationChainPlugin, p as OperationMetric, q as auditTrailPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as SoftDeleteMethods, v as mongoOperationsPlugin, w as CustomIdOptions, x as fieldFilterPlugin, y as MethodRegistryRepository, z as batchOperationsPlugin } from "../validation-chain.plugin-
|
|
1
|
+
import { A as dateSequentialId, B as AuditEntry, C as elasticSearchPlugin, D as PrefixedIdOptions, E as IdGenerator, F as CacheMethods, G as AuditTrailOptions, H as AuditQueryOptions, I as cachePlugin, J as auditLogPlugin, K as AuditTrailQuery, L as BatchOperationsMethods, M as prefixedId, N as sequentialId, O as SequentialIdOptions, P as cascadePlugin, S as ElasticSearchOptions, T as DateSequentialIdOptions, U as AuditQueryResult, V as AuditOperation, W as AuditTrailMethods, X as aggregateHelpersPlugin, Y as AggregateHelpersMethods, _ as MongoOperationsMethods, a as uniqueField, b as methodRegistryPlugin, c as SubdocumentMethods, d as softDeletePlugin, f as ObservabilityOptions, g as multiTenantPlugin, h as MultiTenantOptions, i as requireField, j as getNextSequence, k as customIdPlugin, l as subdocumentPlugin, m as observabilityPlugin, n as blockIf, o as validationChainPlugin, p as OperationMetric, q as auditTrailPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as SoftDeleteMethods, v as mongoOperationsPlugin, w as CustomIdOptions, x as fieldFilterPlugin, y as MethodRegistryRepository, z as batchOperationsPlugin } from "../validation-chain.plugin-BtmT-FFB.mjs";
|
|
2
2
|
export { type AggregateHelpersMethods, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type CacheMethods, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type IdGenerator, type MethodRegistryRepository, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as aggregateHelpersPlugin, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, x as cascadePlugin, y as prefixedId } from "../validation-chain.plugin-
|
|
1
|
+
import { A as aggregateHelpersPlugin, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, x as cascadePlugin, y as prefixedId } from "../validation-chain.plugin-DCYjnPau.mjs";
|
|
2
2
|
export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as coerceHeuristic, i as coerceFieldValue, n as SchemaPathsLike, o as coerceToType, r as buildFieldTypeMap, s as normalizeMongooseType, t as FieldType } from "../../coercion-
|
|
1
|
+
import { a as coerceHeuristic, i as coerceFieldValue, n as SchemaPathsLike, o as coerceToType, r as buildFieldTypeMap, s as normalizeMongooseType, t as FieldType } from "../../coercion-B19MD38s.mjs";
|
|
2
2
|
export { FieldType, SchemaPathsLike, buildFieldTypeMap, coerceFieldValue, coerceHeuristic, coerceToType, normalizeMongooseType };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as isObjectIdInstance } from "../../id-resolution-LZ-cCwR5.mjs";
|
|
1
2
|
import mongoose from "mongoose";
|
|
2
3
|
//#region src/query/primitives/coercion.ts
|
|
3
4
|
/**
|
|
@@ -39,10 +40,10 @@ function normalizeMongooseType(schemaType) {
|
|
|
39
40
|
case "Number": return "number";
|
|
40
41
|
case "Boolean": return "boolean";
|
|
41
42
|
case "Date": return "date";
|
|
42
|
-
case "ObjectID":
|
|
43
|
-
case "ObjectId": return "objectid";
|
|
44
43
|
case "Mixed": return "mixed";
|
|
45
|
-
default:
|
|
44
|
+
default:
|
|
45
|
+
if (isObjectIdInstance(raw)) return "objectid";
|
|
46
|
+
return null;
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as SchemaIndexes, r as extractSchemaIndexes, t as IndexableSchema } from "../../indexes-
|
|
1
|
+
import { n as SchemaIndexes, r as extractSchemaIndexes, t as IndexableSchema } from "../../indexes-qIBCZ3mY.mjs";
|
|
2
2
|
export { IndexableSchema, SchemaIndexes, extractSchemaIndexes };
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Z as PopulateSpec, pt as SortSpec, st as SelectSpec } from "../types-
|
|
2
|
-
import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "../mongooseToJsonSchema-
|
|
1
|
+
import { Z as PopulateSpec, pt as SortSpec, st as SelectSpec } from "../types-BwO4u3JN.mjs";
|
|
2
|
+
import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "../mongooseToJsonSchema-DQMU0Dlv.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/cache-keys.d.ts
|
|
5
5
|
/**
|
|
@@ -89,4 +89,61 @@ declare function modelPattern(prefix: string, model: string): string;
|
|
|
89
89
|
*/
|
|
90
90
|
declare function listPattern(prefix: string, model: string): string;
|
|
91
91
|
//#endregion
|
|
92
|
-
|
|
92
|
+
//#region src/utils/id-resolution.d.ts
|
|
93
|
+
/**
|
|
94
|
+
* ID Resolution Primitive
|
|
95
|
+
*
|
|
96
|
+
* Pure functions for detecting a Mongoose schema's _id type and validating
|
|
97
|
+
* id values against it. Used by Repository.getById to decide whether to
|
|
98
|
+
* run ObjectId validation (for ObjectId schemas) or skip it (for String /
|
|
99
|
+
* Number / UUID schemas).
|
|
100
|
+
*
|
|
101
|
+
* Also usable standalone — Arc controllers, custom repositories, migration
|
|
102
|
+
* scripts, or test harnesses can import these directly to make the same
|
|
103
|
+
* decision without depending on Repository.
|
|
104
|
+
*
|
|
105
|
+
* Design: no `this`, no Model, no DB calls. Only reads `schema.paths._id.instance`.
|
|
106
|
+
*/
|
|
107
|
+
/** Normalized _id type — covers the common Mongoose _id declarations */
|
|
108
|
+
type IdType = 'objectid' | 'string' | 'number' | 'uuid' | 'unknown';
|
|
109
|
+
/**
|
|
110
|
+
* Check if a Mongoose SchemaType `instance` string is an ObjectId variant.
|
|
111
|
+
* Mongoose 9.x uses `'ObjectId'`, older versions use `'ObjectID'`. This
|
|
112
|
+
* is the single source of truth — use it instead of inline
|
|
113
|
+
* `instance === 'ObjectId' || instance === 'ObjectID'` checks.
|
|
114
|
+
*/
|
|
115
|
+
declare function isObjectIdInstance(instance: string | undefined): boolean;
|
|
116
|
+
/** Minimal structural type for schema introspection (decoupled from Mongoose version) */
|
|
117
|
+
interface SchemaLike {
|
|
118
|
+
paths?: Record<string, {
|
|
119
|
+
instance?: string;
|
|
120
|
+
}>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Detect the _id path type from a Mongoose schema.
|
|
124
|
+
*
|
|
125
|
+
* Returns 'objectid' as the safe default when:
|
|
126
|
+
* - No schema is provided (mock models, test harnesses)
|
|
127
|
+
* - The schema has no `paths._id` (auto-generated ObjectId)
|
|
128
|
+
* - The instance string is unrecognized
|
|
129
|
+
*
|
|
130
|
+
* This safe-default means existing callers that don't set _id explicitly
|
|
131
|
+
* get the same ObjectId validation they always had — the behavioral change
|
|
132
|
+
* is strictly opt-in via schema declaration.
|
|
133
|
+
*/
|
|
134
|
+
declare function getSchemaIdType(schema?: SchemaLike | null): IdType;
|
|
135
|
+
/**
|
|
136
|
+
* Validate an id value against a known _id type — WITHOUT hitting the DB.
|
|
137
|
+
*
|
|
138
|
+
* Returns `true` when the value is structurally valid for the given type.
|
|
139
|
+
* Returns `false` when it's definitely invalid (Repository should return
|
|
140
|
+
* 404 / null immediately rather than wasting a round-trip).
|
|
141
|
+
*
|
|
142
|
+
* For `'string'` and `'unknown'` types, validation is intentionally
|
|
143
|
+
* permissive: any truthy, non-empty value is accepted. The DB is the
|
|
144
|
+
* authority for whether the document exists — we only reject values that
|
|
145
|
+
* are structurally impossible (null, undefined, empty string).
|
|
146
|
+
*/
|
|
147
|
+
declare function isValidIdForType(id: unknown, idType: IdType): boolean;
|
|
148
|
+
//#endregion
|
|
149
|
+
export { type IdType, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, byIdKey, byQueryKey, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getSchemaIdType, getSystemManagedFields, isFieldUpdateAllowed, isObjectIdInstance, isValidIdForType, listPattern, listQueryKey, modelPattern, validateUpdateBody, versionKey };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { r as configureLogger, t as createError } from "../error-Bpbi_NKo.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
4
|
-
|
|
2
|
+
import { n as isObjectIdInstance, r as isValidIdForType, t as getSchemaIdType } from "../id-resolution-LZ-cCwR5.mjs";
|
|
3
|
+
import { a as byIdKey, c as listQueryKey, i as getMongooseProjection, l as modelPattern, n as filterResponseData, o as byQueryKey, r as getFieldsForUser, s as listPattern, t as createFieldPreset, u as versionKey } from "../field-selection-C5o7jyyA.mjs";
|
|
4
|
+
import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-8Cyu3NFJ.mjs";
|
|
5
|
+
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, byIdKey, byQueryKey, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getSchemaIdType, getSystemManagedFields, isFieldUpdateAllowed, isObjectIdInstance, isValidIdForType, listPattern, listQueryKey, modelPattern, validateUpdateBody, versionKey };
|
package/dist/{validation-chain.plugin-Dn41aAbK.d.mts → validation-chain.plugin-BtmT-FFB.d.mts}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as Logger, J as Plugin, St as ValidatorDefinition, T as FieldPreset, U as OffsetPaginationResult, V as ObjectId, Z as PopulateSpec, bt as ValidationChainOptions, et as RepositoryContext, f as CascadeOptions, l as CacheOptions, nt as RepositoryInstance, pt as SortSpec, st as SelectSpec, u as CacheStats, ut as SoftDeleteOptions } from "./types-
|
|
1
|
+
import { I as Logger, J as Plugin, St as ValidatorDefinition, T as FieldPreset, U as OffsetPaginationResult, V as ObjectId, Z as PopulateSpec, bt as ValidationChainOptions, et as RepositoryContext, f as CascadeOptions, l as CacheOptions, nt as RepositoryInstance, pt as SortSpec, st as SelectSpec, u as CacheStats, ut as SoftDeleteOptions } from "./types-BwO4u3JN.mjs";
|
|
2
2
|
import mongoose, { ClientSession } from "mongoose";
|
|
3
3
|
|
|
4
4
|
//#region src/plugins/aggregate-helpers.plugin.d.ts
|
|
@@ -2,7 +2,8 @@ import { a as warn, i as debug, n as parseDuplicateKeyError, t as createError }
|
|
|
2
2
|
import { a as exists, c as getOrCreate, d as deleteByQuery, g as upsert, i as count, m as createMany, n as updateByQuery, o as getById, p as create, s as getByQuery, t as update, u as deleteById, v as distinct, y as LookupBuilder } from "./update-AVfKWNGt.mjs";
|
|
3
3
|
import { t as PaginationEngine } from "./PaginationEngine-CpsQl471.mjs";
|
|
4
4
|
import { hasNearOperator, rewriteNearForCount } from "./query/primitives/geo.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { r as isValidIdForType, t as getSchemaIdType } from "./id-resolution-LZ-cCwR5.mjs";
|
|
6
|
+
import { a as byIdKey, c as listQueryKey, l as modelPattern, o as byQueryKey, r as getFieldsForUser, u as versionKey } from "./field-selection-C5o7jyyA.mjs";
|
|
6
7
|
import mongoose from "mongoose";
|
|
7
8
|
//#region src/plugins/aggregate-helpers.plugin.ts
|
|
8
9
|
/**
|
|
@@ -1370,7 +1371,7 @@ var Repository = class {
|
|
|
1370
1371
|
});
|
|
1371
1372
|
return result;
|
|
1372
1373
|
}
|
|
1373
|
-
if (
|
|
1374
|
+
if (!isValidIdForType(id, getSchemaIdType(this.Model.schema))) {
|
|
1374
1375
|
if (context.throwOnNotFound === false || options.throwOnNotFound === false) return null;
|
|
1375
1376
|
throw createError(404, "Document not found");
|
|
1376
1377
|
}
|
|
@@ -1549,41 +1550,14 @@ var Repository = class {
|
|
|
1549
1550
|
});
|
|
1550
1551
|
const filters = context.filters ?? params.filters ?? {};
|
|
1551
1552
|
const search = context.search;
|
|
1552
|
-
const isNearQuery = hasNearOperator(
|
|
1553
|
-
const
|
|
1554
|
-
if (isNearQuery && explicitSort !== void 0) warn(`[mongokit] Repository "${this.model}" dropping explicit sort (${JSON.stringify(explicitSort)}) because the filter contains $near / $nearSphere. MongoDB forbids explicit sort with $near. Use [withinRadius] instead of [near] if you need a custom sort.`);
|
|
1555
|
-
const sort = isNearQuery ? void 0 : explicitSort ?? "-createdAt";
|
|
1553
|
+
const isNearQuery = hasNearOperator(filters);
|
|
1554
|
+
const sort = this._resolveSort(context, params, isNearQuery);
|
|
1556
1555
|
const limit = context.limit ?? params.limit ?? params.pagination?.limit ?? this._pagination.config.defaultLimit;
|
|
1557
1556
|
const page = context.page ?? params.pagination?.page ?? params.page;
|
|
1558
1557
|
const after = context.after ?? params.cursor ?? params.after;
|
|
1559
1558
|
const mode = context.mode ?? params.mode;
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
else useKeyset = !page && !!(after || sort !== "-createdAt" && (context.sort ?? params.sort));
|
|
1563
|
-
const query = { ...filters };
|
|
1564
|
-
if (search) {
|
|
1565
|
-
if (this._hasTextIndex === null) this._hasTextIndex = this.Model.schema.indexes().some((idx) => idx[0] && Object.values(idx[0]).includes("text"));
|
|
1566
|
-
let effectiveMode = this.searchMode === "regex" ? "regex" : "text";
|
|
1567
|
-
if (this.searchMode === "auto") effectiveMode = this._hasTextIndex ? "text" : "regex";
|
|
1568
|
-
if (effectiveMode === "regex") {
|
|
1569
|
-
if (!this.searchFields || this.searchFields.length === 0) throw createError(400, `Repository "${this.model}" configured with searchMode: '${this.searchMode}' but no searchFields provided.`);
|
|
1570
|
-
const escaped = String(search).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1571
|
-
const orConds = this.searchFields.map((field) => ({ [field]: {
|
|
1572
|
-
$regex: escaped,
|
|
1573
|
-
$options: "i"
|
|
1574
|
-
} }));
|
|
1575
|
-
if (Array.isArray(query.$or)) {
|
|
1576
|
-
const existingOr = query.$or;
|
|
1577
|
-
delete query.$or;
|
|
1578
|
-
query.$and = [
|
|
1579
|
-
...Array.isArray(query.$and) ? query.$and : [],
|
|
1580
|
-
{ $or: existingOr },
|
|
1581
|
-
{ $or: orConds }
|
|
1582
|
-
];
|
|
1583
|
-
} else query.$or = orConds;
|
|
1584
|
-
} else if (this._hasTextIndex) query.$text = { $search: search };
|
|
1585
|
-
else throw createError(400, `No text index found for ${this.model}. Cannot perform text search. Configure Repository with searchMode: 'regex' (and searchFields) or 'auto' to enable index-free search.`);
|
|
1586
|
-
}
|
|
1559
|
+
const useKeyset = this._detectPaginationMode(mode, page, after, sort, context, params);
|
|
1560
|
+
const query = this._buildSearchQuery(filters, search);
|
|
1587
1561
|
const populateSpec = options.populateOptions || params.populateOptions || context.populate || options.populate;
|
|
1588
1562
|
const paginationOptions = {
|
|
1589
1563
|
filters: query,
|
|
@@ -2238,6 +2212,56 @@ var Repository = class {
|
|
|
2238
2212
|
return context;
|
|
2239
2213
|
}
|
|
2240
2214
|
/**
|
|
2215
|
+
* Resolve the effective sort for getAll. Handles $near conflict detection
|
|
2216
|
+
* (warns + drops explicit sort when $near is in filters).
|
|
2217
|
+
*/
|
|
2218
|
+
_resolveSort(context, params, isNearQuery) {
|
|
2219
|
+
const explicitSort = context.sort ?? params.sort;
|
|
2220
|
+
if (isNearQuery) {
|
|
2221
|
+
if (explicitSort !== void 0) warn(`[mongokit] Repository "${this.model}" dropping explicit sort (${JSON.stringify(explicitSort)}) because the filter contains $near / $nearSphere. MongoDB forbids explicit sort with $near. Use [withinRadius] instead of [near] if you need a custom sort.`);
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
2224
|
+
return explicitSort ?? "-createdAt";
|
|
2225
|
+
}
|
|
2226
|
+
/**
|
|
2227
|
+
* Detect whether to use keyset (cursor) or offset (page) pagination.
|
|
2228
|
+
*/
|
|
2229
|
+
_detectPaginationMode(mode, page, after, sort, context, params) {
|
|
2230
|
+
if (mode) return mode === "keyset";
|
|
2231
|
+
return !page && !!(after || sort !== "-createdAt" && (context.sort ?? params.sort));
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Build the MongoDB query filter with search merged in. Handles text
|
|
2235
|
+
* search ($text), regex search ($or of $regex), and the search-resolver
|
|
2236
|
+
* plugin contract (search already cleared by a before:getAll hook).
|
|
2237
|
+
*/
|
|
2238
|
+
_buildSearchQuery(filters, search) {
|
|
2239
|
+
const query = { ...filters };
|
|
2240
|
+
if (!search) return query;
|
|
2241
|
+
if (this._hasTextIndex === null) this._hasTextIndex = this.Model.schema.indexes().some((idx) => idx[0] && Object.values(idx[0]).includes("text"));
|
|
2242
|
+
let effectiveMode = this.searchMode === "regex" ? "regex" : "text";
|
|
2243
|
+
if (this.searchMode === "auto") effectiveMode = this._hasTextIndex ? "text" : "regex";
|
|
2244
|
+
if (effectiveMode === "regex") {
|
|
2245
|
+
if (!this.searchFields || this.searchFields.length === 0) throw createError(400, `Repository "${this.model}" configured with searchMode: '${this.searchMode}' but no searchFields provided.`);
|
|
2246
|
+
const escaped = String(search).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2247
|
+
const orConds = this.searchFields.map((field) => ({ [field]: {
|
|
2248
|
+
$regex: escaped,
|
|
2249
|
+
$options: "i"
|
|
2250
|
+
} }));
|
|
2251
|
+
if (Array.isArray(query.$or)) {
|
|
2252
|
+
const existingOr = query.$or;
|
|
2253
|
+
delete query.$or;
|
|
2254
|
+
query.$and = [
|
|
2255
|
+
...Array.isArray(query.$and) ? query.$and : [],
|
|
2256
|
+
{ $or: existingOr },
|
|
2257
|
+
{ $or: orConds }
|
|
2258
|
+
];
|
|
2259
|
+
} else query.$or = orConds;
|
|
2260
|
+
} else if (this._hasTextIndex) query.$text = { $search: search };
|
|
2261
|
+
else throw createError(400, `No text index found for ${this.model}. Cannot perform text search. Configure Repository with searchMode: 'regex' (and searchFields) or 'auto' to enable index-free search.`);
|
|
2262
|
+
return query;
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2241
2265
|
* Parse sort string or object
|
|
2242
2266
|
*/
|
|
2243
2267
|
_parseSort(sort) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/mongokit",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.6",
|
|
4
4
|
"description": "Production-grade MongoDB repositories with zero dependencies - smart pagination, events, and plugins",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
},
|
|
94
94
|
"homepage": "https://github.com/classytic/mongokit#readme",
|
|
95
95
|
"peerDependencies": {
|
|
96
|
-
"mongoose": "
|
|
96
|
+
"mongoose": ">=9.4.1"
|
|
97
97
|
},
|
|
98
98
|
"engines": {
|
|
99
99
|
"node": ">=22"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|