@zodmon/core 0.4.0 → 0.5.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.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ObjectId, Collection, MongoClientOptions } from 'mongodb';
1
+ import { ObjectId, FindCursor, Collection, MongoClientOptions } from 'mongodb';
2
2
  import { ZodPipe, ZodCustom, ZodTransform, z, ZodDefault } from 'zod';
3
3
 
4
4
  /**
@@ -290,6 +290,124 @@ type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape> =
290
290
  /** Erased collection type for use in generic contexts. */
291
291
  type AnyCollection = CollectionDefinition<z.core.$ZodShape>;
292
292
 
293
+ /**
294
+ * Type-safe sort specification for a document type.
295
+ *
296
+ * Constrains sort keys to top-level fields of `T` with direction `1` (ascending)
297
+ * or `-1` (descending). Dot-path sorts deferred to v1.0.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * const sort: TypedSort<User> = { name: 1, createdAt: -1 }
302
+ * ```
303
+ */
304
+ type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
305
+ /**
306
+ * Type-safe cursor wrapping MongoDB's `FindCursor`.
307
+ *
308
+ * Provides chainable query modifiers (`sort`, `skip`, `limit`) that return
309
+ * `this` for fluent chaining, and terminal methods (`toArray`,
310
+ * `[Symbol.asyncIterator]`) that validate each document against the
311
+ * collection's Zod schema before returning.
312
+ *
313
+ * Created by {@link find} — do not construct directly.
314
+ *
315
+ * @typeParam TDef - The collection definition type, used to infer the document type.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const docs = await find(users, { role: 'admin' })
320
+ * .sort({ name: 1 })
321
+ * .limit(10)
322
+ * .toArray()
323
+ * ```
324
+ */
325
+ declare class TypedFindCursor<TDef extends AnyCollection> {
326
+ /** @internal */
327
+ private cursor;
328
+ /** @internal */
329
+ private schema;
330
+ /** @internal */
331
+ private collectionName;
332
+ /** @internal */
333
+ private mode;
334
+ /** @internal */
335
+ constructor(cursor: FindCursor<InferDocument<TDef>>, definition: TDef, mode: ValidationMode | false);
336
+ /**
337
+ * Set the sort order for the query.
338
+ *
339
+ * Only top-level document fields are accepted as sort keys.
340
+ * Values must be `1` (ascending) or `-1` (descending).
341
+ *
342
+ * @param spec - Sort specification mapping field names to sort direction.
343
+ * @returns `this` for chaining.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * find(users, {}).sort({ name: 1, age: -1 }).toArray()
348
+ * ```
349
+ */
350
+ sort(spec: TypedSort<InferDocument<TDef>>): this;
351
+ /**
352
+ * Skip the first `n` documents in the result set.
353
+ *
354
+ * @param n - Number of documents to skip.
355
+ * @returns `this` for chaining.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * find(users, {}).skip(10).limit(10).toArray() // page 2
360
+ * ```
361
+ */
362
+ skip(n: number): this;
363
+ /**
364
+ * Limit the number of documents returned.
365
+ *
366
+ * @param n - Maximum number of documents to return.
367
+ * @returns `this` for chaining.
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * find(users, {}).limit(10).toArray() // at most 10 docs
372
+ * ```
373
+ */
374
+ limit(n: number): this;
375
+ /**
376
+ * Execute the query and return all matching documents as an array.
377
+ *
378
+ * Each document is validated against the collection's Zod schema
379
+ * according to the resolved validation mode.
380
+ *
381
+ * @returns Array of validated documents.
382
+ * @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * const admins = await find(users, { role: 'admin' }).toArray()
387
+ * ```
388
+ */
389
+ toArray(): Promise<InferDocument<TDef>[]>;
390
+ /**
391
+ * Async iterator for streaming documents one at a time.
392
+ *
393
+ * Each yielded document is validated against the collection's Zod schema.
394
+ * Memory-efficient for large result sets.
395
+ *
396
+ * @yields Validated documents one at a time.
397
+ * @throws {ZodmonValidationError} When a document fails schema validation.
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * for await (const user of find(users, {})) {
402
+ * console.log(user.name)
403
+ * }
404
+ * ```
405
+ */
406
+ [Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>>;
407
+ /** @internal Validate a single raw document against the schema. */
408
+ private validateDoc;
409
+ }
410
+
293
411
  /**
294
412
  * Comparison operators for a field value of type `V`.
295
413
  *
@@ -464,6 +582,44 @@ declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TD
464
582
  * ```
465
583
  */
466
584
  declare function findOneOrThrow<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
585
+ /**
586
+ * Options for {@link find}.
587
+ */
588
+ type FindOptions = {
589
+ /** Override the collection-level validation mode, or `false` to skip validation entirely. */
590
+ validate?: ValidationMode | false;
591
+ };
592
+ /**
593
+ * Find all documents matching the filter, returning a chainable typed cursor.
594
+ *
595
+ * The cursor is lazy — no query is executed until a terminal method
596
+ * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
597
+ * to shape the query before executing.
598
+ *
599
+ * Each document is validated against the collection's Zod schema when
600
+ * a terminal method consumes it.
601
+ *
602
+ * @param handle - The collection handle to query.
603
+ * @param filter - Type-safe filter to match documents.
604
+ * @param options - Optional validation overrides.
605
+ * @returns A typed cursor for chaining query modifiers.
606
+ *
607
+ * @example
608
+ * ```ts
609
+ * const admins = await find(users, { role: 'admin' })
610
+ * .sort({ name: 1 })
611
+ * .limit(10)
612
+ * .toArray()
613
+ * ```
614
+ *
615
+ * @example
616
+ * ```ts
617
+ * for await (const user of find(users, {})) {
618
+ * console.log(user.name)
619
+ * }
620
+ * ```
621
+ */
622
+ declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef>;
467
623
 
468
624
  /**
469
625
  * Typed wrapper around a MongoDB driver `Collection`.
@@ -561,6 +717,27 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
561
717
  * ```
562
718
  */
563
719
  findOneOrThrow(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
720
+ /**
721
+ * Find all documents matching the filter, returning a chainable typed cursor.
722
+ *
723
+ * The cursor is lazy — no query is executed until a terminal method
724
+ * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
725
+ * to shape the query before executing.
726
+ *
727
+ * @param filter - Type-safe filter to match documents.
728
+ * @param options - Optional validation overrides.
729
+ * @returns A typed cursor for chaining query modifiers.
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * const users = db.use(Users)
734
+ * const admins = await users.find({ role: 'admin' })
735
+ * .sort({ name: 1 })
736
+ * .limit(10)
737
+ * .toArray()
738
+ * ```
739
+ */
740
+ find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef>;
564
741
  }
565
742
 
566
743
  /**
@@ -1144,4 +1321,4 @@ declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
1144
1321
  */
1145
1322
  declare function installRefExtension(): void;
1146
1323
 
1147
- export { $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AnyCollection, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FindOneOptions, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type RefMarker, type RefMetadata, type ResolvedShape, type TypedFilter, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, extractDbName, extractFieldIndexes, findOne, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, installExtensions, installRefExtension, isOid, objectId, oid, raw };
1324
+ export { $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AnyCollection, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FindOneOptions, type FindOptions, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type RefMarker, type RefMetadata, type ResolvedShape, type TypedFilter, TypedFindCursor, type TypedSort, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, extractDbName, extractFieldIndexes, find, findOne, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, installExtensions, installRefExtension, isOid, objectId, oid, raw };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ObjectId, Collection, MongoClientOptions } from 'mongodb';
1
+ import { ObjectId, FindCursor, Collection, MongoClientOptions } from 'mongodb';
2
2
  import { ZodPipe, ZodCustom, ZodTransform, z, ZodDefault } from 'zod';
3
3
 
4
4
  /**
@@ -290,6 +290,124 @@ type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape> =
290
290
  /** Erased collection type for use in generic contexts. */
291
291
  type AnyCollection = CollectionDefinition<z.core.$ZodShape>;
292
292
 
293
+ /**
294
+ * Type-safe sort specification for a document type.
295
+ *
296
+ * Constrains sort keys to top-level fields of `T` with direction `1` (ascending)
297
+ * or `-1` (descending). Dot-path sorts deferred to v1.0.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * const sort: TypedSort<User> = { name: 1, createdAt: -1 }
302
+ * ```
303
+ */
304
+ type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
305
+ /**
306
+ * Type-safe cursor wrapping MongoDB's `FindCursor`.
307
+ *
308
+ * Provides chainable query modifiers (`sort`, `skip`, `limit`) that return
309
+ * `this` for fluent chaining, and terminal methods (`toArray`,
310
+ * `[Symbol.asyncIterator]`) that validate each document against the
311
+ * collection's Zod schema before returning.
312
+ *
313
+ * Created by {@link find} — do not construct directly.
314
+ *
315
+ * @typeParam TDef - The collection definition type, used to infer the document type.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const docs = await find(users, { role: 'admin' })
320
+ * .sort({ name: 1 })
321
+ * .limit(10)
322
+ * .toArray()
323
+ * ```
324
+ */
325
+ declare class TypedFindCursor<TDef extends AnyCollection> {
326
+ /** @internal */
327
+ private cursor;
328
+ /** @internal */
329
+ private schema;
330
+ /** @internal */
331
+ private collectionName;
332
+ /** @internal */
333
+ private mode;
334
+ /** @internal */
335
+ constructor(cursor: FindCursor<InferDocument<TDef>>, definition: TDef, mode: ValidationMode | false);
336
+ /**
337
+ * Set the sort order for the query.
338
+ *
339
+ * Only top-level document fields are accepted as sort keys.
340
+ * Values must be `1` (ascending) or `-1` (descending).
341
+ *
342
+ * @param spec - Sort specification mapping field names to sort direction.
343
+ * @returns `this` for chaining.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * find(users, {}).sort({ name: 1, age: -1 }).toArray()
348
+ * ```
349
+ */
350
+ sort(spec: TypedSort<InferDocument<TDef>>): this;
351
+ /**
352
+ * Skip the first `n` documents in the result set.
353
+ *
354
+ * @param n - Number of documents to skip.
355
+ * @returns `this` for chaining.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * find(users, {}).skip(10).limit(10).toArray() // page 2
360
+ * ```
361
+ */
362
+ skip(n: number): this;
363
+ /**
364
+ * Limit the number of documents returned.
365
+ *
366
+ * @param n - Maximum number of documents to return.
367
+ * @returns `this` for chaining.
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * find(users, {}).limit(10).toArray() // at most 10 docs
372
+ * ```
373
+ */
374
+ limit(n: number): this;
375
+ /**
376
+ * Execute the query and return all matching documents as an array.
377
+ *
378
+ * Each document is validated against the collection's Zod schema
379
+ * according to the resolved validation mode.
380
+ *
381
+ * @returns Array of validated documents.
382
+ * @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * const admins = await find(users, { role: 'admin' }).toArray()
387
+ * ```
388
+ */
389
+ toArray(): Promise<InferDocument<TDef>[]>;
390
+ /**
391
+ * Async iterator for streaming documents one at a time.
392
+ *
393
+ * Each yielded document is validated against the collection's Zod schema.
394
+ * Memory-efficient for large result sets.
395
+ *
396
+ * @yields Validated documents one at a time.
397
+ * @throws {ZodmonValidationError} When a document fails schema validation.
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * for await (const user of find(users, {})) {
402
+ * console.log(user.name)
403
+ * }
404
+ * ```
405
+ */
406
+ [Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>>;
407
+ /** @internal Validate a single raw document against the schema. */
408
+ private validateDoc;
409
+ }
410
+
293
411
  /**
294
412
  * Comparison operators for a field value of type `V`.
295
413
  *
@@ -464,6 +582,44 @@ declare function findOne<TDef extends AnyCollection>(handle: CollectionHandle<TD
464
582
  * ```
465
583
  */
466
584
  declare function findOneOrThrow<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
585
+ /**
586
+ * Options for {@link find}.
587
+ */
588
+ type FindOptions = {
589
+ /** Override the collection-level validation mode, or `false` to skip validation entirely. */
590
+ validate?: ValidationMode | false;
591
+ };
592
+ /**
593
+ * Find all documents matching the filter, returning a chainable typed cursor.
594
+ *
595
+ * The cursor is lazy — no query is executed until a terminal method
596
+ * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
597
+ * to shape the query before executing.
598
+ *
599
+ * Each document is validated against the collection's Zod schema when
600
+ * a terminal method consumes it.
601
+ *
602
+ * @param handle - The collection handle to query.
603
+ * @param filter - Type-safe filter to match documents.
604
+ * @param options - Optional validation overrides.
605
+ * @returns A typed cursor for chaining query modifiers.
606
+ *
607
+ * @example
608
+ * ```ts
609
+ * const admins = await find(users, { role: 'admin' })
610
+ * .sort({ name: 1 })
611
+ * .limit(10)
612
+ * .toArray()
613
+ * ```
614
+ *
615
+ * @example
616
+ * ```ts
617
+ * for await (const user of find(users, {})) {
618
+ * console.log(user.name)
619
+ * }
620
+ * ```
621
+ */
622
+ declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef>;
467
623
 
468
624
  /**
469
625
  * Typed wrapper around a MongoDB driver `Collection`.
@@ -561,6 +717,27 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
561
717
  * ```
562
718
  */
563
719
  findOneOrThrow(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneOptions): Promise<InferDocument<TDef>>;
720
+ /**
721
+ * Find all documents matching the filter, returning a chainable typed cursor.
722
+ *
723
+ * The cursor is lazy — no query is executed until a terminal method
724
+ * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
725
+ * to shape the query before executing.
726
+ *
727
+ * @param filter - Type-safe filter to match documents.
728
+ * @param options - Optional validation overrides.
729
+ * @returns A typed cursor for chaining query modifiers.
730
+ *
731
+ * @example
732
+ * ```ts
733
+ * const users = db.use(Users)
734
+ * const admins = await users.find({ role: 'admin' })
735
+ * .sort({ name: 1 })
736
+ * .limit(10)
737
+ * .toArray()
738
+ * ```
739
+ */
740
+ find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef>;
564
741
  }
565
742
 
566
743
  /**
@@ -1144,4 +1321,4 @@ declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
1144
1321
  */
1145
1322
  declare function installRefExtension(): void;
1146
1323
 
1147
- export { $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AnyCollection, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FindOneOptions, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type RefMarker, type RefMetadata, type ResolvedShape, type TypedFilter, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, extractDbName, extractFieldIndexes, findOne, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, installExtensions, installRefExtension, isOid, objectId, oid, raw };
1324
+ export { $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AnyCollection, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FindOneOptions, type FindOptions, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type RefMarker, type RefMetadata, type ResolvedShape, type TypedFilter, TypedFindCursor, type TypedSort, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, extractDbName, extractFieldIndexes, find, findOne, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, installExtensions, installRefExtension, isOid, objectId, oid, raw };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { MongoClient } from "mongodb";
3
3
 
4
4
  // src/crud/find.ts
5
- import { z } from "zod";
5
+ import { z as z2 } from "zod";
6
6
 
7
7
  // src/errors/not-found.ts
8
8
  var ZodmonNotFoundError = class extends Error {
@@ -33,6 +33,127 @@ var ZodmonValidationError = class extends Error {
33
33
  }
34
34
  };
35
35
 
36
+ // src/query/cursor.ts
37
+ import { z } from "zod";
38
+ var TypedFindCursor = class {
39
+ /** @internal */
40
+ cursor;
41
+ /** @internal */
42
+ schema;
43
+ /** @internal */
44
+ collectionName;
45
+ /** @internal */
46
+ mode;
47
+ /** @internal */
48
+ constructor(cursor, definition, mode) {
49
+ this.cursor = cursor;
50
+ this.schema = definition.schema;
51
+ this.collectionName = definition.name;
52
+ this.mode = mode;
53
+ }
54
+ /**
55
+ * Set the sort order for the query.
56
+ *
57
+ * Only top-level document fields are accepted as sort keys.
58
+ * Values must be `1` (ascending) or `-1` (descending).
59
+ *
60
+ * @param spec - Sort specification mapping field names to sort direction.
61
+ * @returns `this` for chaining.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * find(users, {}).sort({ name: 1, age: -1 }).toArray()
66
+ * ```
67
+ */
68
+ sort(spec) {
69
+ this.cursor.sort(spec);
70
+ return this;
71
+ }
72
+ /**
73
+ * Skip the first `n` documents in the result set.
74
+ *
75
+ * @param n - Number of documents to skip.
76
+ * @returns `this` for chaining.
77
+ *
78
+ * @example
79
+ * ```ts
80
+ * find(users, {}).skip(10).limit(10).toArray() // page 2
81
+ * ```
82
+ */
83
+ skip(n) {
84
+ this.cursor.skip(n);
85
+ return this;
86
+ }
87
+ /**
88
+ * Limit the number of documents returned.
89
+ *
90
+ * @param n - Maximum number of documents to return.
91
+ * @returns `this` for chaining.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * find(users, {}).limit(10).toArray() // at most 10 docs
96
+ * ```
97
+ */
98
+ limit(n) {
99
+ this.cursor.limit(n);
100
+ return this;
101
+ }
102
+ /**
103
+ * Execute the query and return all matching documents as an array.
104
+ *
105
+ * Each document is validated against the collection's Zod schema
106
+ * according to the resolved validation mode.
107
+ *
108
+ * @returns Array of validated documents.
109
+ * @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const admins = await find(users, { role: 'admin' }).toArray()
114
+ * ```
115
+ */
116
+ async toArray() {
117
+ const raw2 = await this.cursor.toArray();
118
+ return raw2.map((doc) => this.validateDoc(doc));
119
+ }
120
+ /**
121
+ * Async iterator for streaming documents one at a time.
122
+ *
123
+ * Each yielded document is validated against the collection's Zod schema.
124
+ * Memory-efficient for large result sets.
125
+ *
126
+ * @yields Validated documents one at a time.
127
+ * @throws {ZodmonValidationError} When a document fails schema validation.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * for await (const user of find(users, {})) {
132
+ * console.log(user.name)
133
+ * }
134
+ * ```
135
+ */
136
+ async *[Symbol.asyncIterator]() {
137
+ for await (const doc of this.cursor) {
138
+ yield this.validateDoc(doc);
139
+ }
140
+ }
141
+ /** @internal Validate a single raw document against the schema. */
142
+ validateDoc(raw2) {
143
+ if (this.mode === false || this.mode === "passthrough") {
144
+ return raw2;
145
+ }
146
+ try {
147
+ return this.schema.parse(raw2);
148
+ } catch (err) {
149
+ if (err instanceof z.ZodError) {
150
+ throw new ZodmonValidationError(this.collectionName, err);
151
+ }
152
+ throw err;
153
+ }
154
+ }
155
+ };
156
+
36
157
  // src/crud/find.ts
37
158
  async function findOne(handle, filter, options) {
38
159
  const findOptions = options?.project ? { projection: options.project } : void 0;
@@ -45,7 +166,7 @@ async function findOne(handle, filter, options) {
45
166
  try {
46
167
  return handle.definition.schema.parse(raw2);
47
168
  } catch (err) {
48
- if (err instanceof z.ZodError) {
169
+ if (err instanceof z2.ZodError) {
49
170
  throw new ZodmonValidationError(handle.definition.name, err);
50
171
  }
51
172
  throw err;
@@ -58,15 +179,21 @@ async function findOneOrThrow(handle, filter, options) {
58
179
  }
59
180
  return doc;
60
181
  }
182
+ function find(handle, filter, options) {
183
+ const raw2 = handle.native.find(filter);
184
+ const cursor = raw2;
185
+ const mode = options?.validate !== void 0 ? options.validate : handle.definition.options.validation;
186
+ return new TypedFindCursor(cursor, handle.definition, mode);
187
+ }
61
188
 
62
189
  // src/crud/insert.ts
63
- import { z as z2 } from "zod";
190
+ import { z as z3 } from "zod";
64
191
  async function insertOne(handle, doc) {
65
192
  let parsed;
66
193
  try {
67
194
  parsed = handle.definition.schema.parse(doc);
68
195
  } catch (err) {
69
- if (err instanceof z2.ZodError) {
196
+ if (err instanceof z3.ZodError) {
70
197
  throw new ZodmonValidationError(handle.definition.name, err);
71
198
  }
72
199
  throw err;
@@ -81,7 +208,7 @@ async function insertMany(handle, docs) {
81
208
  try {
82
209
  parsed.push(handle.definition.schema.parse(doc));
83
210
  } catch (err) {
84
- if (err instanceof z2.ZodError) {
211
+ if (err instanceof z3.ZodError) {
85
212
  throw new ZodmonValidationError(handle.definition.name, err);
86
213
  }
87
214
  throw err;
@@ -189,6 +316,29 @@ var CollectionHandle = class {
189
316
  async findOneOrThrow(filter, options) {
190
317
  return await findOneOrThrow(this, filter, options);
191
318
  }
319
+ /**
320
+ * Find all documents matching the filter, returning a chainable typed cursor.
321
+ *
322
+ * The cursor is lazy — no query is executed until a terminal method
323
+ * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`
324
+ * to shape the query before executing.
325
+ *
326
+ * @param filter - Type-safe filter to match documents.
327
+ * @param options - Optional validation overrides.
328
+ * @returns A typed cursor for chaining query modifiers.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * const users = db.use(Users)
333
+ * const admins = await users.find({ role: 'admin' })
334
+ * .sort({ name: 1 })
335
+ * .limit(10)
336
+ * .toArray()
337
+ * ```
338
+ */
339
+ find(filter, options) {
340
+ return find(this, filter, options);
341
+ }
192
342
  };
193
343
 
194
344
  // src/client/client.ts
@@ -267,13 +417,13 @@ function createClient(uri, dbNameOrOptions, maybeOptions) {
267
417
 
268
418
  // src/collection/collection.ts
269
419
  import { ObjectId as ObjectId2 } from "mongodb";
270
- import { z as z6 } from "zod";
420
+ import { z as z7 } from "zod";
271
421
 
272
422
  // src/schema/extensions.ts
273
- import { z as z4 } from "zod";
423
+ import { z as z5 } from "zod";
274
424
 
275
425
  // src/schema/ref.ts
276
- import { z as z3 } from "zod";
426
+ import { z as z4 } from "zod";
277
427
  var refMetadata = /* @__PURE__ */ new WeakMap();
278
428
  function getRefMetadata(schema) {
279
429
  if (typeof schema !== "object" || schema === null) return void 0;
@@ -281,7 +431,7 @@ function getRefMetadata(schema) {
281
431
  }
282
432
  var REF_GUARD = /* @__PURE__ */ Symbol.for("zodmon_ref");
283
433
  function installRefExtension() {
284
- const proto = z3.ZodType.prototype;
434
+ const proto = z4.ZodType.prototype;
285
435
  if (REF_GUARD in proto) return;
286
436
  Object.defineProperty(proto, "ref", {
287
437
  value(collection2) {
@@ -308,7 +458,7 @@ function getIndexMetadata(schema) {
308
458
  }
309
459
  var GUARD = /* @__PURE__ */ Symbol.for("zodmon_extensions");
310
460
  function installExtensions() {
311
- const proto = z4.ZodType.prototype;
461
+ const proto = z5.ZodType.prototype;
312
462
  if (GUARD in proto) return;
313
463
  Object.defineProperty(proto, "index", {
314
464
  /**
@@ -407,10 +557,10 @@ installExtensions();
407
557
 
408
558
  // src/schema/object-id.ts
409
559
  import { ObjectId } from "mongodb";
410
- import { z as z5 } from "zod";
560
+ import { z as z6 } from "zod";
411
561
  var OBJECT_ID_HEX = /^[a-f\d]{24}$/i;
412
562
  function objectId() {
413
- return z5.custom((val) => {
563
+ return z6.custom((val) => {
414
564
  if (val instanceof ObjectId) return true;
415
565
  return typeof val === "string" && OBJECT_ID_HEX.test(val);
416
566
  }, "Invalid ObjectId").transform((val) => val instanceof ObjectId ? val : ObjectId.createFromHexString(val));
@@ -429,7 +579,7 @@ function extractFieldIndexes(shape) {
429
579
  }
430
580
  function collection(name, shape, options) {
431
581
  const resolvedShape = "_id" in shape ? shape : { _id: objectId().default(() => new ObjectId2()), ...shape };
432
- const schema = z6.object(resolvedShape);
582
+ const schema = z7.object(resolvedShape);
433
583
  const fieldIndexes = extractFieldIndexes(shape);
434
584
  const { indexes: compoundIndexes, validation, ...rest } = options ?? {};
435
585
  return {
@@ -528,12 +678,14 @@ export {
528
678
  CollectionHandle,
529
679
  Database,
530
680
  IndexBuilder,
681
+ TypedFindCursor,
531
682
  ZodmonNotFoundError,
532
683
  ZodmonValidationError,
533
684
  collection,
534
685
  createClient,
535
686
  extractDbName,
536
687
  extractFieldIndexes,
688
+ find,
537
689
  findOne,
538
690
  findOneOrThrow,
539
691
  getIndexMetadata,