@zodmon/core 0.10.0 → 0.11.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/dist/index.cjs +124 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -125
- package/dist/index.d.ts +524 -125
- package/dist/index.js +122 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,30 +11,24 @@ var $avg = (field) => ({
|
|
|
11
11
|
__accum: true,
|
|
12
12
|
expr: { $avg: field }
|
|
13
13
|
});
|
|
14
|
-
|
|
15
|
-
__accum: true,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
expr: { $
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
__accum: true,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
expr: { $push: field }
|
|
33
|
-
});
|
|
34
|
-
var $addToSet = (field) => ({
|
|
35
|
-
__accum: true,
|
|
36
|
-
expr: { $addToSet: field }
|
|
37
|
-
});
|
|
14
|
+
function $min(field) {
|
|
15
|
+
return { __accum: true, expr: { $min: field } };
|
|
16
|
+
}
|
|
17
|
+
function $max(field) {
|
|
18
|
+
return { __accum: true, expr: { $max: field } };
|
|
19
|
+
}
|
|
20
|
+
function $first(field) {
|
|
21
|
+
return { __accum: true, expr: { $first: field } };
|
|
22
|
+
}
|
|
23
|
+
function $last(field) {
|
|
24
|
+
return { __accum: true, expr: { $last: field } };
|
|
25
|
+
}
|
|
26
|
+
function $push(field) {
|
|
27
|
+
return { __accum: true, expr: { $push: field } };
|
|
28
|
+
}
|
|
29
|
+
function $addToSet(field) {
|
|
30
|
+
return { __accum: true, expr: { $addToSet: field } };
|
|
31
|
+
}
|
|
38
32
|
function createAccumulatorBuilder() {
|
|
39
33
|
return {
|
|
40
34
|
count: () => ({ __accum: true, expr: { $sum: 1 } }),
|
|
@@ -110,9 +104,9 @@ var ZodmonError = class extends Error {
|
|
|
110
104
|
/** The underlying error that caused this error, if any. */
|
|
111
105
|
cause;
|
|
112
106
|
constructor(message, collection2, options) {
|
|
113
|
-
super(message);
|
|
107
|
+
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
114
108
|
this.collection = collection2;
|
|
115
|
-
if (options?.cause) {
|
|
109
|
+
if (options?.cause !== void 0) {
|
|
116
110
|
this.cause = options.cause;
|
|
117
111
|
}
|
|
118
112
|
}
|
|
@@ -1240,6 +1234,54 @@ function resolveSortKeys(sortSpec) {
|
|
|
1240
1234
|
return entries;
|
|
1241
1235
|
}
|
|
1242
1236
|
|
|
1237
|
+
// src/query/projection.ts
|
|
1238
|
+
function isIncludeValue(value) {
|
|
1239
|
+
return value === 1 || value === true;
|
|
1240
|
+
}
|
|
1241
|
+
function isExcludeValue(value) {
|
|
1242
|
+
return value === 0 || value === false;
|
|
1243
|
+
}
|
|
1244
|
+
function isInclusionProjection(projection) {
|
|
1245
|
+
for (const key of Object.keys(projection)) {
|
|
1246
|
+
if (key === "_id") continue;
|
|
1247
|
+
const value = projection[key];
|
|
1248
|
+
if (value !== void 0 && isIncludeValue(value)) return true;
|
|
1249
|
+
}
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
function buildPickMask(projection, schemaKeys) {
|
|
1253
|
+
const mask = {};
|
|
1254
|
+
const idValue = projection._id;
|
|
1255
|
+
if (!(idValue !== void 0 && isExcludeValue(idValue)) && schemaKeys.has("_id")) {
|
|
1256
|
+
mask._id = true;
|
|
1257
|
+
}
|
|
1258
|
+
for (const key of Object.keys(projection)) {
|
|
1259
|
+
if (key === "_id") continue;
|
|
1260
|
+
const value = projection[key];
|
|
1261
|
+
if (value !== void 0 && isIncludeValue(value) && schemaKeys.has(key)) {
|
|
1262
|
+
mask[key] = true;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
return mask;
|
|
1266
|
+
}
|
|
1267
|
+
function buildOmitMask(projection, schemaKeys) {
|
|
1268
|
+
const mask = {};
|
|
1269
|
+
for (const key of Object.keys(projection)) {
|
|
1270
|
+
const value = projection[key];
|
|
1271
|
+
if (value !== void 0 && isExcludeValue(value) && schemaKeys.has(key)) {
|
|
1272
|
+
mask[key] = true;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
return mask;
|
|
1276
|
+
}
|
|
1277
|
+
function deriveProjectedSchema(schema, projection) {
|
|
1278
|
+
const schemaKeys = new Set(Object.keys(schema.shape));
|
|
1279
|
+
if (isInclusionProjection(projection)) {
|
|
1280
|
+
return schema.pick(buildPickMask(projection, schemaKeys));
|
|
1281
|
+
}
|
|
1282
|
+
return schema.omit(buildOmitMask(projection, schemaKeys));
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1243
1285
|
// src/query/cursor.ts
|
|
1244
1286
|
var TypedFindCursor = class {
|
|
1245
1287
|
/** @internal */
|
|
@@ -1258,6 +1300,8 @@ var TypedFindCursor = class {
|
|
|
1258
1300
|
/** @internal */
|
|
1259
1301
|
sortSpec;
|
|
1260
1302
|
/** @internal */
|
|
1303
|
+
projectedSchema;
|
|
1304
|
+
/** @internal */
|
|
1261
1305
|
constructor(cursor, definition, mode, nativeCollection, filter) {
|
|
1262
1306
|
this.cursor = cursor;
|
|
1263
1307
|
this.schema = definition.schema;
|
|
@@ -1266,6 +1310,7 @@ var TypedFindCursor = class {
|
|
|
1266
1310
|
this.nativeCollection = nativeCollection;
|
|
1267
1311
|
this.filter = filter;
|
|
1268
1312
|
this.sortSpec = null;
|
|
1313
|
+
this.projectedSchema = null;
|
|
1269
1314
|
}
|
|
1270
1315
|
/**
|
|
1271
1316
|
* Set the sort order for the query.
|
|
@@ -1339,6 +1384,40 @@ var TypedFindCursor = class {
|
|
|
1339
1384
|
this.cursor.hint(indexName);
|
|
1340
1385
|
return this;
|
|
1341
1386
|
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Apply a projection to narrow the returned fields.
|
|
1389
|
+
*
|
|
1390
|
+
* Inclusion projections (`{ name: 1 }`) return only the specified fields
|
|
1391
|
+
* plus `_id` (unless `_id: 0`). Exclusion projections (`{ email: 0 }`)
|
|
1392
|
+
* return all fields except those excluded.
|
|
1393
|
+
*
|
|
1394
|
+
* The cursor's output type is narrowed at compile time. A derived Zod
|
|
1395
|
+
* schema is built for runtime validation of the projected fields.
|
|
1396
|
+
*
|
|
1397
|
+
* Projects from the original document type, not from a previous projection.
|
|
1398
|
+
* Calling `.project()` twice overrides the previous projection.
|
|
1399
|
+
*
|
|
1400
|
+
* @param spec - Type-safe projection document.
|
|
1401
|
+
* @returns A new cursor with the narrowed output type.
|
|
1402
|
+
*
|
|
1403
|
+
* @example
|
|
1404
|
+
* ```ts
|
|
1405
|
+
* const names = await find(users, {})
|
|
1406
|
+
* .project({ name: 1 })
|
|
1407
|
+
* .sort({ name: 1 })
|
|
1408
|
+
* .toArray()
|
|
1409
|
+
* // names[0].name ✓
|
|
1410
|
+
* // names[0].email TS error
|
|
1411
|
+
* ```
|
|
1412
|
+
*/
|
|
1413
|
+
project(spec) {
|
|
1414
|
+
this.cursor.project(spec);
|
|
1415
|
+
this.projectedSchema = deriveProjectedSchema(
|
|
1416
|
+
this.schema,
|
|
1417
|
+
spec
|
|
1418
|
+
);
|
|
1419
|
+
return this;
|
|
1420
|
+
}
|
|
1342
1421
|
async paginate(opts) {
|
|
1343
1422
|
const sortRecord = this.sortSpec ? this.sortSpec : null;
|
|
1344
1423
|
const sortKeys2 = resolveSortKeys(sortRecord);
|
|
@@ -1455,8 +1534,9 @@ var TypedFindCursor = class {
|
|
|
1455
1534
|
if (this.mode === false || this.mode === "passthrough") {
|
|
1456
1535
|
return raw2;
|
|
1457
1536
|
}
|
|
1537
|
+
const schema = this.projectedSchema ?? this.schema;
|
|
1458
1538
|
try {
|
|
1459
|
-
return
|
|
1539
|
+
return schema.parse(raw2);
|
|
1460
1540
|
} catch (err) {
|
|
1461
1541
|
if (err instanceof z3.ZodError) {
|
|
1462
1542
|
throw new ZodmonValidationError(this.collectionName, err, raw2);
|
|
@@ -1469,7 +1549,8 @@ var TypedFindCursor = class {
|
|
|
1469
1549
|
// src/crud/find.ts
|
|
1470
1550
|
async function findOne(handle, filter, options) {
|
|
1471
1551
|
checkUnindexedFields(handle.definition, filter);
|
|
1472
|
-
const
|
|
1552
|
+
const project = options && "project" in options ? options.project : void 0;
|
|
1553
|
+
const findOptions = project ? { projection: project } : void 0;
|
|
1473
1554
|
let raw2;
|
|
1474
1555
|
try {
|
|
1475
1556
|
raw2 = await handle.native.findOne(filter, findOptions);
|
|
@@ -1481,8 +1562,12 @@ async function findOne(handle, filter, options) {
|
|
|
1481
1562
|
if (mode === false || mode === "passthrough") {
|
|
1482
1563
|
return raw2;
|
|
1483
1564
|
}
|
|
1565
|
+
const schema = project ? deriveProjectedSchema(
|
|
1566
|
+
handle.definition.schema,
|
|
1567
|
+
project
|
|
1568
|
+
) : handle.definition.schema;
|
|
1484
1569
|
try {
|
|
1485
|
-
return
|
|
1570
|
+
return schema.parse(raw2);
|
|
1486
1571
|
} catch (err) {
|
|
1487
1572
|
if (err instanceof z4.ZodError) {
|
|
1488
1573
|
throw new ZodmonValidationError(handle.definition.name, err, raw2);
|
|
@@ -1502,7 +1587,12 @@ function find(handle, filter, options) {
|
|
|
1502
1587
|
const raw2 = handle.native.find(filter);
|
|
1503
1588
|
const cursor = raw2;
|
|
1504
1589
|
const mode = options?.validate !== void 0 ? options.validate : handle.definition.options.validation;
|
|
1505
|
-
|
|
1590
|
+
const typedCursor = new TypedFindCursor(cursor, handle.definition, mode, handle.native, filter);
|
|
1591
|
+
const project = options && "project" in options ? options.project : void 0;
|
|
1592
|
+
if (project) {
|
|
1593
|
+
return typedCursor.project(project);
|
|
1594
|
+
}
|
|
1595
|
+
return typedCursor;
|
|
1506
1596
|
}
|
|
1507
1597
|
|
|
1508
1598
|
// src/crud/insert.ts
|
|
@@ -1651,70 +1741,12 @@ var CollectionHandle = class {
|
|
|
1651
1741
|
async insertMany(docs) {
|
|
1652
1742
|
return await insertMany(this, docs);
|
|
1653
1743
|
}
|
|
1654
|
-
/**
|
|
1655
|
-
* Find a single document matching the filter.
|
|
1656
|
-
*
|
|
1657
|
-
* Queries MongoDB, then validates the fetched document against the collection's
|
|
1658
|
-
* Zod schema. Validation mode is resolved from the per-query option, falling
|
|
1659
|
-
* back to the collection-level default (which defaults to `'strict'`).
|
|
1660
|
-
*
|
|
1661
|
-
* @param filter - Type-safe filter to match documents.
|
|
1662
|
-
* @param options - Optional projection and validation overrides.
|
|
1663
|
-
* @returns The matched document, or `null` if no document matches.
|
|
1664
|
-
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1665
|
-
*
|
|
1666
|
-
* @example
|
|
1667
|
-
* ```ts
|
|
1668
|
-
* const users = db.use(Users)
|
|
1669
|
-
* const user = await users.findOne({ name: 'Ada' })
|
|
1670
|
-
* if (user) console.log(user.role)
|
|
1671
|
-
* ```
|
|
1672
|
-
*/
|
|
1673
1744
|
async findOne(filter, options) {
|
|
1674
1745
|
return await findOne(this, filter, options);
|
|
1675
1746
|
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Find a single document matching the filter, or throw if none exists.
|
|
1678
|
-
*
|
|
1679
|
-
* Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}
|
|
1680
|
-
* instead of returning `null` when no document matches the filter.
|
|
1681
|
-
*
|
|
1682
|
-
* @param filter - Type-safe filter to match documents.
|
|
1683
|
-
* @param options - Optional projection and validation overrides.
|
|
1684
|
-
* @returns The matched document (never null).
|
|
1685
|
-
* @throws {ZodmonNotFoundError} When no document matches the filter.
|
|
1686
|
-
* @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.
|
|
1687
|
-
*
|
|
1688
|
-
* @example
|
|
1689
|
-
* ```ts
|
|
1690
|
-
* const users = db.use(Users)
|
|
1691
|
-
* const user = await users.findOneOrThrow({ name: 'Ada' })
|
|
1692
|
-
* console.log(user.role) // guaranteed non-null
|
|
1693
|
-
* ```
|
|
1694
|
-
*/
|
|
1695
1747
|
async findOneOrThrow(filter, options) {
|
|
1696
1748
|
return await findOneOrThrow(this, filter, options);
|
|
1697
1749
|
}
|
|
1698
|
-
/**
|
|
1699
|
-
* Find all documents matching the filter, returning a chainable typed cursor.
|
|
1700
|
-
*
|
|
1701
|
-
* The cursor is lazy — no query is executed until a terminal method
|
|
1702
|
-
* (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
|
|
1703
|
-
* to shape the query before executing.
|
|
1704
|
-
*
|
|
1705
|
-
* @param filter - Type-safe filter to match documents.
|
|
1706
|
-
* @param options - Optional validation overrides.
|
|
1707
|
-
* @returns A typed cursor for chaining query modifiers.
|
|
1708
|
-
*
|
|
1709
|
-
* @example
|
|
1710
|
-
* ```ts
|
|
1711
|
-
* const users = db.use(Users)
|
|
1712
|
-
* const admins = await users.find({ role: 'admin' })
|
|
1713
|
-
* .sort({ name: 1 })
|
|
1714
|
-
* .limit(10)
|
|
1715
|
-
* .toArray()
|
|
1716
|
-
* ```
|
|
1717
|
-
*/
|
|
1718
1750
|
find(filter, options) {
|
|
1719
1751
|
return find(this, filter, options);
|
|
1720
1752
|
}
|
|
@@ -2302,6 +2334,7 @@ export {
|
|
|
2302
2334
|
createExpressionBuilder,
|
|
2303
2335
|
deleteMany,
|
|
2304
2336
|
deleteOne,
|
|
2337
|
+
deriveProjectedSchema,
|
|
2305
2338
|
extractComparableOptions,
|
|
2306
2339
|
extractDbName,
|
|
2307
2340
|
extractFieldIndexes,
|
|
@@ -2316,6 +2349,7 @@ export {
|
|
|
2316
2349
|
index,
|
|
2317
2350
|
insertMany,
|
|
2318
2351
|
insertOne,
|
|
2352
|
+
isInclusionProjection,
|
|
2319
2353
|
isOid,
|
|
2320
2354
|
objectId,
|
|
2321
2355
|
oid,
|