@zodmon/core 0.6.0 → 0.8.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 +703 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1375 -444
- package/dist/index.d.ts +1375 -444
- package/dist/index.js +686 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client/client.ts","../src/crud/find.ts","../src/errors/not-found.ts","../src/errors/validation.ts","../src/query/cursor.ts","../src/crud/insert.ts","../src/client/handle.ts","../src/collection/collection.ts","../src/schema/extensions.ts","../src/schema/ref.ts","../src/schema/object-id.ts","../src/collection/index-def.ts","../src/helpers/oid.ts","../src/query/operators.ts"],"sourcesContent":["export { CollectionHandle, createClient, Database, extractDbName } from './client'\nexport type {\n\tAnyCollection,\n\tCollectionDefinition,\n\tCollectionOptions,\n\tCompoundIndexDefinition,\n\tFieldIndexDefinition,\n\tInferDocument,\n\tInferInsert,\n\tResolvedShape,\n\tValidationMode,\n} from './collection'\nexport { collection, extractFieldIndexes, IndexBuilder, index } from './collection'\nexport type { FindOneOptions, FindOptions } from './crud'\nexport { find, findOne, findOneOrThrow, insertMany, insertOne } from './crud'\nexport { ZodmonNotFoundError, ZodmonValidationError } from './errors'\nexport { isOid, oid } from './helpers/oid'\nexport type {\n\tAddToSetEach,\n\tAddToSetFields,\n\tArrayElement,\n\tComparisonOperators,\n\tCurrentDateFields,\n\tDotPaths,\n\tDotPathType,\n\tIncFields,\n\tPopFields,\n\tPullFields,\n\tPushFields,\n\tPushModifiers,\n\tRenameFields,\n\tSetFields,\n\tTypedFilter,\n\tTypedSort,\n\tTypedUpdateFilter,\n\tUnsetFields,\n} from './query'\nexport {\n\t$and,\n\t$eq,\n\t$exists,\n\t$gt,\n\t$gte,\n\t$in,\n\t$lt,\n\t$lte,\n\t$ne,\n\t$nin,\n\t$nor,\n\t$not,\n\t$or,\n\t$regex,\n\traw,\n\tTypedFindCursor,\n} from './query'\nexport {\n\tgetIndexMetadata,\n\tgetRefMetadata,\n\ttype IndexMetadata,\n\ttype IndexOptions,\n\tinstallExtensions,\n\tinstallRefExtension,\n\tobjectId,\n\ttype RefMarker,\n\ttype RefMetadata,\n\ttype ZodObjectId,\n} from './schema'\n","import type { Db, MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\nimport type { z } from 'zod'\nimport type { CollectionDefinition, InferDocument } from '../collection/types'\nimport { CollectionHandle } from './handle'\n\n/**\n * Wraps a MongoDB `MongoClient` and `Db`, providing typed collection access\n * through {@link CollectionHandle}s.\n *\n * Connection is lazy — the driver connects on the first operation, not at\n * construction time. Call {@link close} for graceful shutdown.\n *\n * @example\n * ```ts\n * const db = createClient('mongodb://localhost:27017', 'myapp')\n * const users = db.use(UsersCollection)\n * await users.native.insertOne({ _id: oid(), name: 'Ada' })\n * await db.close()\n * ```\n */\nexport class Database {\n\tprivate readonly _client: MongoClient\n\tprivate readonly _db: Db\n\t/** Registered collection definitions, keyed by name. Used by syncIndexes(). */\n\tprivate readonly _collections = new Map<string, CollectionDefinition>()\n\n\tconstructor(uri: string, dbName: string, options?: MongoClientOptions) {\n\t\tthis._client = new MongoClient(uri, options)\n\t\tthis._db = this._client.db(dbName)\n\t}\n\n\t/**\n\t * Register a collection definition and return a typed {@link CollectionHandle}.\n\t *\n\t * The handle's `native` property is a MongoDB `Collection<TDoc>` where `TDoc`\n\t * is the document type inferred from the definition's Zod schema. Calling\n\t * `use()` multiple times with the same definition is safe — each call returns\n\t * a new lightweight handle backed by the same underlying driver collection.\n\t *\n\t * @param def - A collection definition created by `collection()`.\n\t * @returns A typed collection handle for CRUD operations.\n\t */\n\tuse<TShape extends z.core.$ZodShape>(\n\t\tdef: CollectionDefinition<TShape>,\n\t): CollectionHandle<CollectionDefinition<TShape>> {\n\t\t// Safe cast: erasing TShape for internal collection tracking.\n\t\t// Stored definitions are only iterated in syncIndexes() where\n\t\t// index metadata is accessed structurally.\n\t\tthis._collections.set(def.name, def as unknown as CollectionDefinition)\n\t\tconst native = this._db.collection<InferDocument<CollectionDefinition<TShape>>>(def.name)\n\t\t// Safe cast: CollectionDefinition<TShape> → AnyCollection for the constructor.\n\t\t// The generic is preserved through the return type annotation.\n\t\treturn new CollectionHandle(\n\t\t\tdef as unknown as CollectionDefinition<TShape>,\n\t\t\tnative,\n\t\t) as CollectionHandle<CollectionDefinition<TShape>>\n\t}\n\n\t/**\n\t * Synchronize indexes defined in registered collections with MongoDB.\n\t *\n\t * Stub — full implementation in TASK-92.\n\t */\n\tsyncIndexes(): Promise<void> {\n\t\treturn Promise.resolve()\n\t}\n\n\t/**\n\t * Execute a function within a MongoDB transaction with auto-commit/rollback.\n\t *\n\t * Stub — full implementation in TASK-106.\n\t */\n\ttransaction<T>(_fn: () => Promise<T>): Promise<T> {\n\t\tthrow new Error('Not implemented')\n\t}\n\n\t/**\n\t * Close the underlying `MongoClient` connection. Safe to call even if\n\t * no connection was established (the driver handles this gracefully).\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this._client.close()\n\t}\n}\n\n/**\n * Extract the database name from a MongoDB connection URI.\n *\n * Handles standard URIs, multi-host/replica set, SRV (`mongodb+srv://`),\n * auth credentials, query parameters, and percent-encoded database names.\n * Returns `undefined` when no database name is present.\n */\nexport function extractDbName(uri: string): string | undefined {\n\tconst withoutProtocol = uri.replace(/^mongodb(?:\\+srv)?:\\/\\//, '')\n\t// Safe cast: split() always returns at least one element, but noUncheckedIndexedAccess\n\t// types [0] as string | undefined.\n\tconst withoutQuery = withoutProtocol.split('?')[0] as string\n\t// Skip past auth credentials (user:pass@) before searching for the path separator\n\tconst atIndex = withoutQuery.lastIndexOf('@')\n\tconst hostAndPath = atIndex === -1 ? withoutQuery : withoutQuery.slice(atIndex + 1)\n\tconst slashIndex = hostAndPath.indexOf('/')\n\tif (slashIndex === -1) return undefined\n\tconst dbName = decodeURIComponent(hostAndPath.slice(slashIndex + 1))\n\treturn dbName || undefined\n}\n\n/**\n * Create a new {@link Database} instance wrapping a MongoDB connection.\n *\n * The connection is lazy — the driver connects on the first operation.\n * Pass any `MongoClientOptions` to configure connection pooling, timeouts, etc.\n *\n * When `dbName` is omitted, the database name is extracted from the URI path\n * (e.g. `mongodb://localhost:27017/myapp` → `'myapp'`). If no database name\n * is found in either the arguments or the URI, a warning is logged and\n * MongoDB's default `'test'` database is used.\n *\n * @param uri - MongoDB connection string (e.g. `mongodb://localhost:27017`).\n * @param dbName - The database name to use.\n * @param options - Optional MongoDB driver client options.\n * @returns A new `Database` instance.\n */\nexport function createClient(uri: string, dbName: string, options?: MongoClientOptions): Database\nexport function createClient(uri: string, options?: MongoClientOptions): Database\nexport function createClient(\n\turi: string,\n\tdbNameOrOptions?: string | MongoClientOptions,\n\tmaybeOptions?: MongoClientOptions,\n): Database {\n\tif (typeof dbNameOrOptions === 'string') {\n\t\treturn new Database(uri, dbNameOrOptions, maybeOptions)\n\t}\n\tconst parsed = extractDbName(uri)\n\tif (!parsed) {\n\t\tconsole.warn('[zodmon] No database name provided — using MongoDB default \"test\"')\n\t}\n\treturn new Database(uri, parsed ?? 'test', dbNameOrOptions)\n}\n","import type { FindCursor } from 'mongodb'\nimport { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, InferDocument, ValidationMode } from '../collection/types'\nimport { ZodmonNotFoundError } from '../errors/not-found'\nimport { ZodmonValidationError } from '../errors/validation'\nimport { TypedFindCursor } from '../query/cursor'\nimport type { TypedFilter } from '../query/filter'\n\n/**\n * Options for {@link findOne} and {@link findOneOrThrow}.\n */\nexport type FindOneOptions = {\n\t/** MongoDB projection — include (`1`) or exclude (`0`) fields. Typed projections deferred to v1.0. */\n\tproject?: Record<string, 0 | 1>\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Find a single document matching the filter.\n *\n * Queries MongoDB, then validates the fetched document against the collection's\n * Zod schema. Validation mode is resolved from the per-query option, falling\n * back to the collection-level default (which defaults to `'strict'`).\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional projection and validation overrides.\n * @returns The matched document, or `null` if no document matches.\n * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOne(users, { name: 'Ada' })\n * if (user) console.log(user.role) // typed as 'admin' | 'user'\n * ```\n */\nexport async function findOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOneOptions,\n): Promise<InferDocument<TDef> | null> {\n\tconst findOptions = options?.project ? { projection: options.project } : undefined\n\t// Safe cast: TypedFilter<InferDocument<TDef>> is a strict subset of\n\t// MongoDB's Filter<T>, but the intersection-based mapped type cannot\n\t// be structurally matched by the driver's looser Filter type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\tconst raw = await handle.native.findOne(filter as any, findOptions)\n\tif (!raw) return null\n\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\n\tif (mode === false || mode === 'passthrough') {\n\t\t// Safe cast: raw document from MongoDB matches the collection's document\n\t\t// shape at runtime. Skipping validation per user request.\n\t\treturn raw as InferDocument<TDef>\n\t}\n\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\treturn handle.definition.schema.parse(raw) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n}\n\n/**\n * Find a single document matching the filter, or throw if none exists.\n *\n * Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}\n * instead of returning `null` when no document matches the filter.\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional projection and validation overrides.\n * @returns The matched document (never null).\n * @throws {ZodmonNotFoundError} When no document matches the filter.\n * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOneOrThrow(users, { name: 'Ada' })\n * console.log(user.role) // typed as 'admin' | 'user', guaranteed non-null\n * ```\n */\nexport async function findOneOrThrow<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOneOptions,\n): Promise<InferDocument<TDef>> {\n\tconst doc = await findOne(handle, filter, options)\n\tif (!doc) {\n\t\tthrow new ZodmonNotFoundError(handle.definition.name)\n\t}\n\treturn doc\n}\n\n/**\n * Options for {@link find}.\n */\nexport type FindOptions = {\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Find all documents matching the filter, returning a chainable typed cursor.\n *\n * The cursor is lazy — no query is executed until a terminal method\n * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`\n * to shape the query before executing.\n *\n * Each document is validated against the collection's Zod schema when\n * a terminal method consumes it.\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional validation overrides.\n * @returns A typed cursor for chaining query modifiers.\n *\n * @example\n * ```ts\n * const admins = await find(users, { role: 'admin' })\n * .sort({ name: 1 })\n * .limit(10)\n * .toArray()\n * ```\n *\n * @example\n * ```ts\n * for await (const user of find(users, {})) {\n * console.log(user.name)\n * }\n * ```\n */\nexport function find<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOptions,\n): TypedFindCursor<TDef> {\n\t// Safe cast: TypedFilter<InferDocument<TDef>> is a strict subset of\n\t// MongoDB's Filter<T>, but the intersection-based mapped type cannot\n\t// be structurally matched by the driver's looser Filter type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\tconst raw = handle.native.find(filter as any)\n\t// Safe cast: Collection.find() returns FindCursor<WithId<T>>, but our schemas\n\t// always include _id so WithId<InferDocument<TDef>> is structurally identical\n\t// to InferDocument<TDef>. TypeScript cannot prove this with exactOptionalPropertyTypes.\n\tconst cursor = raw as unknown as FindCursor<InferDocument<TDef>>\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\treturn new TypedFindCursor(cursor, handle.definition, mode)\n}\n","/**\n * Thrown when a query expected to find a document returns no results.\n *\n * Used by {@link findOneOrThrow} when no document matches the provided filter.\n * Callers can inspect `.collection` to identify which collection the query targeted.\n *\n * @example\n * ```ts\n * try {\n * await users.findOneOrThrow({ name: 'nonexistent' })\n * } catch (err) {\n * if (err instanceof ZodmonNotFoundError) {\n * console.log(err.message) // => 'Document not found in \"users\"'\n * console.log(err.collection) // => 'users'\n * }\n * }\n * ```\n */\nexport class ZodmonNotFoundError extends Error {\n\toverride readonly name = 'ZodmonNotFoundError'\n\n\t/** The MongoDB collection name where the query found no results. */\n\treadonly collection: string\n\n\tconstructor(collection: string) {\n\t\tsuper(`Document not found in \"${collection}\"`)\n\t\tthis.collection = collection\n\t}\n}\n","import type { z } from 'zod'\n\n/**\n * Thrown when a document fails Zod schema validation before a MongoDB write.\n *\n * Wraps the original `ZodError` with the collection name and a human-readable\n * message listing each invalid field and its error. Callers can inspect\n * `.zodError.issues` for programmatic access to individual failures.\n *\n * @example\n * ```ts\n * try {\n * await users.insertOne({ name: 123 })\n * } catch (err) {\n * if (err instanceof ZodmonValidationError) {\n * console.log(err.message)\n * // => 'Validation failed for \"users\": name (Expected string, received number)'\n * console.log(err.collection) // => 'users'\n * console.log(err.zodError) // => ZodError with .issues array\n * }\n * }\n * ```\n */\nexport class ZodmonValidationError extends Error {\n\toverride readonly name = 'ZodmonValidationError'\n\n\t/** The MongoDB collection name where the validation failed. */\n\treadonly collection: string\n\n\t/** The original Zod validation error with detailed issue information. */\n\treadonly zodError: z.ZodError\n\n\tconstructor(collection: string, zodError: z.ZodError) {\n\t\tconst fields = zodError.issues\n\t\t\t.map((issue) => {\n\t\t\t\tconst path = issue.path.join('.') || '(root)'\n\t\t\t\treturn `${path} (${issue.message})`\n\t\t\t})\n\t\t\t.join(', ')\n\t\tsuper(`Validation failed for \"${collection}\": ${fields}`)\n\t\tthis.collection = collection\n\t\tthis.zodError = zodError\n\t}\n}\n","import type { FindCursor, Sort } from 'mongodb'\nimport { z } from 'zod'\nimport type { AnyCollection, InferDocument, ValidationMode } from '../collection/types'\nimport { ZodmonValidationError } from '../errors/validation'\n\n/**\n * Type-safe sort specification for a document type.\n *\n * Constrains sort keys to top-level fields of `T` with direction `1` (ascending)\n * or `-1` (descending). Dot-path sorts deferred to v1.0.\n *\n * @example\n * ```ts\n * const sort: TypedSort<User> = { name: 1, createdAt: -1 }\n * ```\n */\nexport type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>\n\n/**\n * Type-safe cursor wrapping MongoDB's `FindCursor`.\n *\n * Provides chainable query modifiers (`sort`, `skip`, `limit`) that return\n * `this` for fluent chaining, and terminal methods (`toArray`,\n * `[Symbol.asyncIterator]`) that validate each document against the\n * collection's Zod schema before returning.\n *\n * Created by {@link find} — do not construct directly.\n *\n * @typeParam TDef - The collection definition type, used to infer the document type.\n *\n * @example\n * ```ts\n * const docs = await find(users, { role: 'admin' })\n * .sort({ name: 1 })\n * .limit(10)\n * .toArray()\n * ```\n */\nexport class TypedFindCursor<TDef extends AnyCollection> {\n\t/** @internal */\n\tprivate cursor: FindCursor<InferDocument<TDef>>\n\t/** @internal */\n\tprivate schema: z.ZodType\n\t/** @internal */\n\tprivate collectionName: string\n\t/** @internal */\n\tprivate mode: ValidationMode | false\n\n\t/** @internal */\n\tconstructor(\n\t\tcursor: FindCursor<InferDocument<TDef>>,\n\t\tdefinition: TDef,\n\t\tmode: ValidationMode | false,\n\t) {\n\t\tthis.cursor = cursor\n\t\tthis.schema = definition.schema\n\t\tthis.collectionName = definition.name\n\t\tthis.mode = mode\n\t}\n\n\t/**\n\t * Set the sort order for the query.\n\t *\n\t * Only top-level document fields are accepted as sort keys.\n\t * Values must be `1` (ascending) or `-1` (descending).\n\t *\n\t * @param spec - Sort specification mapping field names to sort direction.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).sort({ name: 1, age: -1 }).toArray()\n\t * ```\n\t */\n\tsort(spec: TypedSort<InferDocument<TDef>>): this {\n\t\t// Safe cast: TypedSort is a strict subset of MongoDB's Sort type,\n\t\t// but Record<keyof T & string, 1 | -1> is not assignable to Sort\n\t\t// due to index signature differences.\n\t\tthis.cursor.sort(spec as Sort)\n\t\treturn this\n\t}\n\n\t/**\n\t * Skip the first `n` documents in the result set.\n\t *\n\t * @param n - Number of documents to skip.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).skip(10).limit(10).toArray() // page 2\n\t * ```\n\t */\n\tskip(n: number): this {\n\t\tthis.cursor.skip(n)\n\t\treturn this\n\t}\n\n\t/**\n\t * Limit the number of documents returned.\n\t *\n\t * @param n - Maximum number of documents to return.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).limit(10).toArray() // at most 10 docs\n\t * ```\n\t */\n\tlimit(n: number): this {\n\t\tthis.cursor.limit(n)\n\t\treturn this\n\t}\n\n\t/**\n\t * Execute the query and return all matching documents as an array.\n\t *\n\t * Each document is validated against the collection's Zod schema\n\t * according to the resolved validation mode.\n\t *\n\t * @returns Array of validated documents.\n\t * @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const admins = await find(users, { role: 'admin' }).toArray()\n\t * ```\n\t */\n\tasync toArray(): Promise<InferDocument<TDef>[]> {\n\t\tconst raw = await this.cursor.toArray()\n\t\treturn raw.map((doc) => this.validateDoc(doc))\n\t}\n\n\t/**\n\t * Async iterator for streaming documents one at a time.\n\t *\n\t * Each yielded document is validated against the collection's Zod schema.\n\t * Memory-efficient for large result sets.\n\t *\n\t * @yields Validated documents one at a time.\n\t * @throws {ZodmonValidationError} When a document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * for await (const user of find(users, {})) {\n\t * console.log(user.name)\n\t * }\n\t * ```\n\t */\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>> {\n\t\tfor await (const doc of this.cursor) {\n\t\t\tyield this.validateDoc(doc)\n\t\t}\n\t}\n\n\t/** @internal Validate a single raw document against the schema. */\n\tprivate validateDoc(raw: InferDocument<TDef>): InferDocument<TDef> {\n\t\tif (this.mode === false || this.mode === 'passthrough') {\n\t\t\treturn raw\n\t\t}\n\n\t\ttry {\n\t\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\t\treturn this.schema.parse(raw) as InferDocument<TDef>\n\t\t} catch (err) {\n\t\t\tif (err instanceof z.ZodError) {\n\t\t\t\tthrow new ZodmonValidationError(this.collectionName, err)\n\t\t\t}\n\t\t\tthrow err\n\t\t}\n\t}\n}\n","import { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, InferDocument, InferInsert } from '../collection/types'\nimport { ZodmonValidationError } from '../errors/validation'\n\n/**\n * Insert a single document into the collection.\n *\n * Validates the input against the collection's Zod schema before writing.\n * Schema defaults (including auto-generated `_id`) are applied during\n * validation. Returns the full document with all defaults filled in.\n *\n * @param handle - The collection handle to insert into.\n * @param doc - The document to insert. Fields with `.default()` are optional.\n * @returns The inserted document with `_id` and all defaults applied.\n * @throws {ZodmonValidationError} When the document fails schema validation.\n *\n * @example\n * ```ts\n * const user = await insertOne(users, { name: 'Ada' })\n * console.log(user._id) // ObjectId (auto-generated)\n * console.log(user.role) // 'user' (schema default)\n * ```\n */\nexport async function insertOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tdoc: InferInsert<TDef>,\n): Promise<InferDocument<TDef>> {\n\tlet parsed: InferDocument<TDef>\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\tparsed = handle.definition.schema.parse(doc) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n\t// Safe cast: parsed is a full document with _id, matching the collection's\n\t// document type. MongoDB driver's OptionalUnlessRequiredId makes _id optional\n\t// for generic types, but we always have _id after parse.\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB driver's insertOne parameter type uses OptionalUnlessRequiredId which is not directly assignable from our generic InferDocument\n\tawait handle.native.insertOne(parsed as any)\n\treturn parsed\n}\n\n/**\n * Insert multiple documents into the collection.\n *\n * Validates every document against the collection's Zod schema before\n * writing any to MongoDB. If any document fails validation, none are\n * inserted (fail-fast before the driver call).\n *\n * @param handle - The collection handle to insert into.\n * @param docs - The documents to insert.\n * @returns The inserted documents with `_id` and all defaults applied.\n * @throws {ZodmonValidationError} When any document fails schema validation.\n *\n * @example\n * ```ts\n * const users = await insertMany(handle, [\n * { name: 'Ada' },\n * { name: 'Bob', role: 'admin' },\n * ])\n * ```\n */\nexport async function insertMany<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tdocs: InferInsert<TDef>[],\n): Promise<InferDocument<TDef>[]> {\n\tif (docs.length === 0) return []\n\tconst parsed: InferDocument<TDef>[] = []\n\tfor (const doc of docs) {\n\t\ttry {\n\t\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\t\tparsed.push(handle.definition.schema.parse(doc) as InferDocument<TDef>)\n\t\t} catch (err) {\n\t\t\tif (err instanceof z.ZodError) {\n\t\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t\t}\n\t\t\tthrow err\n\t\t}\n\t}\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB driver's insertMany parameter type is not directly assignable from our generic InferDocument\n\tawait handle.native.insertMany(parsed as any)\n\treturn parsed\n}\n","import type { Collection } from 'mongodb'\nimport type { AnyCollection, InferDocument, InferInsert } from '../collection/types'\nimport type { FindOneOptions, FindOptions } from '../crud/find'\nimport { find as _find, findOne as _findOne, findOneOrThrow as _findOneOrThrow } from '../crud/find'\nimport { insertMany as _insertMany, insertOne as _insertOne } from '../crud/insert'\nimport type { TypedFindCursor } from '../query/cursor'\nimport type { TypedFilter } from '../query/filter'\n\n/**\n * Typed wrapper around a MongoDB driver `Collection`.\n *\n * Created by {@link Database.use}. Holds the original `CollectionDefinition`\n * (for runtime schema validation and index metadata) alongside the native\n * driver collection parameterized with the inferred document type.\n *\n * @typeParam TDef - The collection definition type. Used to derive both\n * the document type (`InferDocument`) and the insert type (`InferInsert`).\n */\nexport class CollectionHandle<TDef extends AnyCollection = AnyCollection> {\n\t/** The collection definition containing schema, name, and index metadata. */\n\treadonly definition: TDef\n\n\t/** The underlying MongoDB driver collection, typed to the inferred document type. */\n\treadonly native: Collection<InferDocument<TDef>>\n\n\tconstructor(definition: TDef, native: Collection<InferDocument<TDef>>) {\n\t\tthis.definition = definition\n\t\tthis.native = native\n\t}\n\n\t/**\n\t * Insert a single document into the collection.\n\t *\n\t * Validates the input against the collection's Zod schema before writing.\n\t * Schema defaults (including auto-generated `_id`) are applied during\n\t * validation. Returns the full document with all defaults filled in.\n\t *\n\t * @param doc - The document to insert. Fields with `.default()` are optional.\n\t * @returns The inserted document with `_id` and all defaults applied.\n\t * @throws {ZodmonValidationError} When the document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.insertOne({ name: 'Ada' })\n\t * console.log(user._id) // ObjectId (auto-generated)\n\t * console.log(user.role) // 'user' (schema default)\n\t * ```\n\t */\n\tasync insertOne(doc: InferInsert<TDef>): Promise<InferDocument<TDef>> {\n\t\treturn await _insertOne(this, doc)\n\t}\n\n\t/**\n\t * Insert multiple documents into the collection.\n\t *\n\t * Validates every document against the collection's Zod schema before\n\t * writing any to MongoDB. If any document fails validation, none are\n\t * inserted (fail-fast before the driver call).\n\t *\n\t * @param docs - The documents to insert.\n\t * @returns The inserted documents with `_id` and all defaults applied.\n\t * @throws {ZodmonValidationError} When any document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * const created = await users.insertMany([\n\t * { name: 'Ada' },\n\t * { name: 'Bob', role: 'admin' },\n\t * ])\n\t * ```\n\t */\n\tasync insertMany(docs: InferInsert<TDef>[]): Promise<InferDocument<TDef>[]> {\n\t\treturn await _insertMany(this, docs)\n\t}\n\n\t/**\n\t * Find a single document matching the filter.\n\t *\n\t * Queries MongoDB, then validates the fetched document against the collection's\n\t * Zod schema. Validation mode is resolved from the per-query option, falling\n\t * back to the collection-level default (which defaults to `'strict'`).\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional projection and validation overrides.\n\t * @returns The matched document, or `null` if no document matches.\n\t * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOne({ name: 'Ada' })\n\t * if (user) console.log(user.role)\n\t * ```\n\t */\n\tasync findOne(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneOptions,\n\t): Promise<InferDocument<TDef> | null> {\n\t\treturn await _findOne(this, filter, options)\n\t}\n\n\t/**\n\t * Find a single document matching the filter, or throw if none exists.\n\t *\n\t * Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}\n\t * instead of returning `null` when no document matches the filter.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional projection and validation overrides.\n\t * @returns The matched document (never null).\n\t * @throws {ZodmonNotFoundError} When no document matches the filter.\n\t * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOneOrThrow({ name: 'Ada' })\n\t * console.log(user.role) // guaranteed non-null\n\t * ```\n\t */\n\tasync findOneOrThrow(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneOptions,\n\t): Promise<InferDocument<TDef>> {\n\t\treturn await _findOneOrThrow(this, filter, options)\n\t}\n\n\t/**\n\t * Find all documents matching the filter, returning a chainable typed cursor.\n\t *\n\t * The cursor is lazy — no query is executed until a terminal method\n\t * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`\n\t * to shape the query before executing.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional validation overrides.\n\t * @returns A typed cursor for chaining query modifiers.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const admins = await users.find({ role: 'admin' })\n\t * .sort({ name: 1 })\n\t * .limit(10)\n\t * .toArray()\n\t * ```\n\t */\n\tfind(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef> {\n\t\treturn _find(this, filter, options)\n\t}\n}\n","import { ObjectId } from 'mongodb'\nimport { z } from 'zod'\nimport { getIndexMetadata } from '../schema/extensions'\nimport { objectId } from '../schema/object-id'\nimport type {\n\tCollectionDefinition,\n\tCollectionOptions,\n\tFieldIndexDefinition,\n\tResolvedShape,\n} from './types'\n\n/**\n * Walk a Zod shape and extract field-level index metadata from each field.\n *\n * Returns an array of {@link FieldIndexDefinition} for every field that has\n * been marked with `.index()`, `.unique()`, `.text()`, or `.expireAfter()`.\n * Fields without index metadata are silently skipped.\n *\n * @param shape - A Zod shape object (the value passed to `z.object()`).\n * @returns An array of field index definitions with the field name attached.\n */\nexport function extractFieldIndexes(shape: z.core.$ZodShape): FieldIndexDefinition[] {\n\tconst result: FieldIndexDefinition[] = []\n\tfor (const [field, schema] of Object.entries(shape)) {\n\t\tconst meta = getIndexMetadata(schema)\n\t\tif (meta) {\n\t\t\tresult.push({ field, ...meta })\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Define a MongoDB collection with a Zod schema.\n *\n * Creates a {@link CollectionDefinition} that:\n * - Adds `_id: objectId()` if the shape doesn't already include `_id`\n * - Uses the user-provided `_id` schema if one is present (e.g. nanoid, UUID)\n * - Extracts field-level index metadata from the shape\n * - Separates compound indexes from the rest of the options\n * - Returns an immutable definition object\n *\n * @param name - The MongoDB collection name.\n * @param shape - A Zod shape object defining the document fields. May include a custom `_id`.\n * @param options - Optional collection-level configuration including compound indexes.\n * @returns A {@link CollectionDefinition} ready for use with `createClient()`.\n *\n * @example\n * ```ts\n * const Users = collection('users', {\n * email: z.string().unique(),\n * name: z.string().index(),\n * age: z.number().optional(),\n * })\n * ```\n */\nexport function collection<TShape extends z.core.$ZodShape>(\n\tname: string,\n\tshape: TShape,\n\toptions?: CollectionOptions<Extract<keyof TShape, string>>,\n): CollectionDefinition<TShape> {\n\t// TypeScript cannot narrow generic conditional types through control flow,\n\t// so the assertion is needed here. Both branches are provably correct:\n\t// - when _id is in shape: ResolvedShape<TShape> = TShape\n\t// - when _id is absent: ResolvedShape<TShape> = { _id: ZodObjectId } & TShape\n\tconst resolvedShape = (\n\t\t'_id' in shape ? shape : { _id: objectId().default(() => new ObjectId()), ...shape }\n\t) as ResolvedShape<TShape>\n\tconst schema = z.object(resolvedShape)\n\n\tconst fieldIndexes = extractFieldIndexes(shape)\n\n\tconst { indexes: compoundIndexes, validation, ...rest } = options ?? {}\n\n\treturn {\n\t\tname,\n\t\t// Zod v4's z.object() returns ZodObject<{ -readonly [P in keyof T]: T[P] }> which\n\t\t// strips readonly modifiers. With exactOptionalPropertyTypes this mapped type is\n\t\t// not assignable to ZodObject<ResolvedShape<TShape>>. The cast is safe because\n\t\t// the runtime shape is correct — only the readonly modifier differs.\n\t\tschema: schema as CollectionDefinition<TShape>['schema'],\n\t\tshape,\n\t\tfieldIndexes,\n\t\tcompoundIndexes: compoundIndexes ?? [],\n\t\toptions: {\n\t\t\tvalidation: validation ?? 'strict',\n\t\t\t...rest,\n\t\t},\n\t}\n}\n","import { z } from 'zod'\nimport { installRefExtension } from './ref'\n\n/**\n * Options controlling how a field-level MongoDB index is created.\n *\n * Passed to the `.index()` Zod extension method. Every property is optional;\n * omitting all of them creates a standard ascending, non-unique index.\n */\nexport type IndexOptions = {\n\t/** When `true`, MongoDB enforces a unique constraint on this field. */\n\tunique?: boolean\n\t/**\n\t * When `true`, the index skips documents where the field is `null` or missing.\n\t * Useful for optional fields that should be indexed only when present.\n\t */\n\tsparse?: boolean\n\t/** When `true`, creates a MongoDB text index for full-text search on this field. */\n\ttext?: boolean\n\t/**\n\t * When `true`, the index is created in descending order (`-1`).\n\t * Defaults to ascending (`1`) when omitted.\n\t */\n\tdescending?: boolean\n\t/**\n\t * TTL in seconds. MongoDB will automatically delete documents once the\n\t * indexed `Date` field is older than this many seconds. Only valid on\n\t * fields whose runtime type is `Date`.\n\t */\n\texpireAfter?: number\n\t/**\n\t * A partial filter expression. Only documents matching this filter are\n\t * included in the index. Maps directly to MongoDB's `partialFilterExpression`.\n\t */\n\tpartial?: Record<string, unknown>\n}\n\n/**\n * Metadata stored in the WeakMap sidecar for every schema that has been\n * marked with `.index()`. Always contains `indexed: true` plus any\n * {@link IndexOptions} the caller provided.\n */\nexport type IndexMetadata = {\n\t/** Always `true` — acts as a discriminator for \"this field is indexed\". */\n\tindexed: true\n} & IndexOptions\n\ndeclare module 'zod' {\n\tinterface ZodType {\n\t\t/**\n\t\t * Mark this field for indexing when `syncIndexes()` is called.\n\t\t *\n\t\t * Stores {@link IndexOptions} in a WeakMap sidecar so the collection\n\t\t * factory can later introspect each field and build the appropriate\n\t\t * MongoDB index specification.\n\t\t *\n\t\t * @param options - Optional index configuration (unique, sparse, etc.).\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const UserSchema = z.object({\n\t\t * email: z.string().index({ unique: true }),\n\t\t * age: z.number().index({ sparse: true }),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tindex(options?: IndexOptions): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ unique: true })`.\n\t\t *\n\t\t * Creates a unique index on this field, causing MongoDB to reject\n\t\t * duplicate values.\n\t\t *\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const UserSchema = z.object({\n\t\t * email: z.string().unique(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tunique(): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ text: true })`.\n\t\t *\n\t\t * Creates a MongoDB text index on this field, enabling `$text` queries\n\t\t * for full-text search.\n\t\t *\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const PostSchema = z.object({\n\t\t * body: z.string().text(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\ttext(): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ expireAfter: seconds })`.\n\t\t *\n\t\t * Creates a TTL (Time-To-Live) index. MongoDB will automatically\n\t\t * remove documents once the indexed `Date` field is older than\n\t\t * the specified number of seconds.\n\t\t *\n\t\t * @param seconds - Number of seconds after which documents expire.\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const SessionSchema = z.object({\n\t\t * createdAt: z.date().expireAfter(3600), // 1 hour TTL\n\t\t * })\n\t\t * ```\n\t\t */\n\t\texpireAfter(seconds: number): this\n\t}\n}\n\n/**\n * WeakMap sidecar that associates a Zod schema instance with its\n * {@link IndexMetadata}.\n *\n * A WeakMap is used so that index metadata does not prevent garbage collection\n * of schema instances. The keys are the Zod schema objects themselves, and\n * the values are the corresponding `IndexMetadata` descriptors.\n */\nconst indexMetadata = new WeakMap<object, IndexMetadata>()\n\n/**\n * Retrieve the index metadata attached to a Zod schema, if any.\n *\n * Returns `undefined` when the schema was never marked with an index\n * extension (`.index()`, `.unique()`, `.text()`, or `.expireAfter()`).\n *\n * @param schema - The Zod schema to inspect. Accepts `unknown` for\n * convenience; non-object values safely return `undefined`.\n * @returns The {@link IndexMetadata} for the schema, or `undefined`.\n *\n * @example\n * ```ts\n * const email = z.string().index({ unique: true })\n * const meta = getIndexMetadata(email)\n * // => { indexed: true, unique: true }\n * ```\n */\nexport function getIndexMetadata(schema: unknown): IndexMetadata | undefined {\n\tif (typeof schema !== 'object' || schema === null) return undefined\n\treturn indexMetadata.get(schema)\n}\n\n/**\n * Symbol used as a guard property on `ZodType.prototype` to prevent\n * double-registration of Zodmon extension methods. The symbol is created\n * with `Symbol.for` so it is shared across realms / duplicate module loads.\n */\nconst GUARD = Symbol.for('zodmon_extensions')\n\n/**\n * Monkey-patch Zod's `ZodType.prototype` with Zodmon extension methods\n * (`.index()`, `.unique()`, `.text()`, `.expireAfter()`).\n *\n * In Zod v4, methods are copied from `ZodType.prototype` to each instance\n * during construction via the internal `init` loop (`Object.keys(proto)` ->\n * copy to instance). Extension methods use `enumerable: true` so they are\n * picked up by this loop for every schema created after installation.\n *\n * The function is idempotent: calling it more than once is a safe no-op,\n * guarded by a non-enumerable `Symbol.for('zodmon_extensions')` property\n * on the prototype.\n *\n * This function is called at module level when `extensions.ts` is first\n * imported, so consumers never need to call it manually. It is exported\n * primarily for use in tests.\n *\n * @example\n * ```ts\n * import { installExtensions } from '@zodmon/core/schema/extensions'\n * installExtensions() // safe to call multiple times\n *\n * const indexed = z.string().index({ unique: true })\n * ```\n */\nexport function installExtensions(): void {\n\tconst proto = z.ZodType.prototype\n\tif (GUARD in proto) return\n\n\tObject.defineProperty(proto, 'index', {\n\t\t/**\n\t\t * Declares a MongoDB index on this field. Accepts optional\n\t\t * {@link IndexOptions} to configure uniqueness, sparseness, text search,\n\t\t * sort direction, TTL, or partial filters.\n\t\t *\n\t\t * @param options - Index configuration. Omit for a standard ascending index.\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const name = z.string().index()\n\t\t * const email = z.string().index({ unique: true, sparse: true })\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto, options?: IndexOptions): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, ...options })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'unique', {\n\t\t/**\n\t\t * Shorthand for `.index({ unique: true })`. Marks this field as requiring\n\t\t * a unique index in MongoDB, preventing duplicate values.\n\t\t *\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const email = z.string().unique()\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, unique: true })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'text', {\n\t\t/**\n\t\t * Shorthand for `.index({ text: true })`. Creates a MongoDB text index on\n\t\t * this field, enabling full-text search queries with `$text`.\n\t\t *\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const bio = z.string().text()\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, text: true })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'expireAfter', {\n\t\t/**\n\t\t * Shorthand for `.index({ expireAfter: seconds })`. Creates a TTL index on\n\t\t * a `Date` field. MongoDB will automatically remove documents once the field\n\t\t * value is older than the specified number of seconds.\n\t\t *\n\t\t * @param seconds - TTL in seconds after which documents expire.\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const expiresAt = z.date().expireAfter(86400) // 24 hours\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto, seconds: number): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, expireAfter: seconds })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tinstallRefExtension()\n\n\tObject.defineProperty(proto, GUARD, {\n\t\tvalue: true,\n\t\tenumerable: false,\n\t\tconfigurable: false,\n\t\twritable: false,\n\t})\n}\n\ninstallExtensions()\n","import { z } from 'zod'\nimport type { AnyCollection, InferDocument } from '../collection/types'\n\n/**\n * Type-level marker that carries the target collection type through the\n * type system. Intersected with the schema return type by `.ref()` so\n * that `RefFields<T>` (future) can extract ref relationships.\n *\n * This is a phantom brand — no runtime value has this property.\n */\nexport type RefMarker<TCollection extends AnyCollection = AnyCollection> = {\n\treadonly _ref: TCollection\n}\n\n/**\n * Metadata stored in the WeakMap sidecar for schemas marked with `.ref()`.\n * Holds a reference to the target collection definition object.\n */\nexport type RefMetadata = {\n\treadonly collection: AnyCollection\n}\n\n/**\n * Module augmentation: adds `.ref()` to all `ZodType` schemas.\n *\n * The intersection constraint `this['_zod']['output'] extends InferDocument<TCollection>['_id']`\n * ensures compile-time type safety: the field's output type must match the\n * target collection's `_id` type. Mismatches produce a type error.\n *\n * Supports both default ObjectId `_id` and custom `_id` types (string, nanoid, etc.):\n * - `objectId().ref(Users)` compiles when Users has ObjectId `_id`\n * - `z.string().ref(Orgs)` compiles when Orgs has string `_id`\n * - `z.string().ref(Users)` is a type error (string ≠ ObjectId)\n * - `objectId().ref(Orgs)` is a type error (ObjectId ≠ string)\n */\ndeclare module 'zod' {\n\tinterface ZodType {\n\t\t/**\n\t\t * Declare a typed foreign key reference to another collection.\n\t\t *\n\t\t * Stores the target collection definition in metadata for runtime\n\t\t * populate resolution, and brands the return type with\n\t\t * `RefMarker<TCollection>` so `RefFields<T>` can extract refs\n\t\t * at the type level.\n\t\t *\n\t\t * The field's output type must match the target collection's `_id` type.\n\t\t * Mismatched types produce a compile error.\n\t\t *\n\t\t * Apply `.ref()` before wrapper methods like `.optional()` or `.nullable()`:\n\t\t * `objectId().ref(Users).optional()` — not `objectId().optional().ref(Users)`.\n\t\t *\n\t\t * @param collection - The target collection definition object.\n\t\t * @returns The same schema instance, branded with the ref marker.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const Posts = collection('posts', {\n\t\t * authorId: objectId().ref(Users),\n\t\t * title: z.string(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tref<TCollection extends AnyCollection>(\n\t\t\tcollection: TCollection &\n\t\t\t\t(this['_zod']['output'] extends InferDocument<TCollection>['_id'] ? unknown : never),\n\t\t): this & RefMarker<TCollection>\n\t}\n}\n\n/**\n * WeakMap sidecar that associates a Zod schema instance with its\n * {@link RefMetadata}. Uses WeakMap so ref metadata does not prevent\n * garbage collection of schema instances.\n */\nconst refMetadata = new WeakMap<object, RefMetadata>()\n\n/**\n * Retrieve the ref metadata attached to a Zod schema, if any.\n *\n * Returns `undefined` when the schema was never marked with `.ref()`.\n *\n * @param schema - The Zod schema to inspect. Accepts `unknown` for\n * convenience; non-object values safely return `undefined`.\n * @returns The {@link RefMetadata} for the schema, or `undefined`.\n *\n * @example\n * ```ts\n * const authorId = objectId().ref(Users)\n * const meta = getRefMetadata(authorId)\n * // => { collection: Users }\n * ```\n */\nexport function getRefMetadata(schema: unknown): RefMetadata | undefined {\n\tif (typeof schema !== 'object' || schema === null) return undefined\n\treturn refMetadata.get(schema)\n}\n\n/**\n * Symbol guard to prevent double-registration of the `.ref()` extension.\n * Uses `Symbol.for` so it is shared across realms / duplicate module loads.\n */\nconst REF_GUARD = Symbol.for('zodmon_ref')\n\n/**\n * Install the `.ref()` extension method on `ZodType.prototype`.\n *\n * Idempotent — safe to call multiple times.\n */\nexport function installRefExtension(): void {\n\tconst proto = z.ZodType.prototype\n\tif (REF_GUARD in proto) return\n\n\tObject.defineProperty(proto, 'ref', {\n\t\tvalue(this: typeof proto, collection: AnyCollection): typeof proto {\n\t\t\trefMetadata.set(this, { collection })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, REF_GUARD, {\n\t\tvalue: true,\n\t\tenumerable: false,\n\t\tconfigurable: false,\n\t\twritable: false,\n\t})\n}\n","import { ObjectId } from 'mongodb'\nimport { type ZodCustom, type ZodPipe, type ZodTransform, z } from 'zod'\n\n/** Matches a 24-character hexadecimal string (case-insensitive). */\nconst OBJECT_ID_HEX = /^[a-f\\d]{24}$/i\n\n/**\n * The Zod type produced by {@link objectId}. A pipeline that validates an\n * input as either a `string` (24-char hex) or an `ObjectId` instance, then\n * transforms it into a concrete `ObjectId`.\n *\n * Use `z.infer<ZodObjectId>` to extract the output type (`ObjectId`) and\n * `z.input<ZodObjectId>` for the input type (`string | ObjectId`).\n */\nexport type ZodObjectId = ZodPipe<\n\tZodCustom<string | ObjectId, string | ObjectId>,\n\tZodTransform<ObjectId, string | ObjectId>\n>\n\n/**\n * Create a Zod schema that validates and coerces values into MongoDB\n * `ObjectId` instances.\n *\n * Accepts either:\n * - An existing `ObjectId` instance (passed through unchanged).\n * - A 24-character hexadecimal string (coerced to `ObjectId`).\n *\n * All other inputs are rejected with the message `\"Invalid ObjectId\"`.\n *\n * @returns A {@link ZodObjectId} schema.\n *\n * @example\n * ```ts\n * const schema = objectId()\n *\n * schema.parse(new ObjectId()) // OK — pass-through\n * schema.parse('64f1a2b3c4d5e6f7a8b9c0d1') // OK — coerced to ObjectId\n * schema.parse('not-valid') // throws ZodError\n * ```\n *\n * @example Inside a z.object() shape:\n * ```ts\n * const UserSchema = z.object({\n * _id: objectId(),\n * name: z.string(),\n * })\n * ```\n */\nexport function objectId(): ZodObjectId {\n\treturn z\n\t\t.custom<string | ObjectId>((val): val is string | ObjectId => {\n\t\t\tif (val instanceof ObjectId) return true\n\t\t\treturn typeof val === 'string' && OBJECT_ID_HEX.test(val)\n\t\t}, 'Invalid ObjectId')\n\t\t.transform((val) => (val instanceof ObjectId ? val : ObjectId.createFromHexString(val)))\n}\n","import type { CompoundIndexDefinition } from './types'\n\ntype IndexDirection = 1 | -1\n\ntype CompoundIndexOptions = NonNullable<CompoundIndexDefinition['options']>\n\n/**\n * A builder for compound index definitions.\n *\n * Provides a fluent API for declaring compound indexes with options like\n * `unique`, `sparse`, and custom `name`. Each method returns a new\n * IndexBuilder instance (immutable pattern — the original is never mutated).\n *\n * IndexBuilder is structurally compatible with {@link CompoundIndexDefinition}\n * so instances can be used directly in `CollectionOptions.indexes`.\n *\n * Dot-notation paths like `'address.city'` are accepted at the value level\n * (any string satisfies `TKeys`), but type-level validation against nested\n * schema paths is deferred to a future release.\n *\n * @example\n * ```ts\n * index({ email: 1, role: -1 }).unique().name('email_role_idx')\n * ```\n */\nexport class IndexBuilder<TKeys extends string> {\n\t// Typed as Partial for structural compatibility with CompoundIndexDefinition.\n\t// The constructor guarantees all keys are present at runtime.\n\treadonly fields: Partial<Record<TKeys, IndexDirection>>\n\treadonly options: CompoundIndexOptions\n\n\tconstructor(fields: Record<TKeys, IndexDirection>) {\n\t\tthis.fields = fields\n\t\tthis.options = {}\n\t}\n\n\tprivate _clone(options: CompoundIndexOptions): IndexBuilder<TKeys> {\n\t\t// Object.create returns `any`; cast is safe because we assign the correct shape\n\t\treturn Object.assign(Object.create(IndexBuilder.prototype) as IndexBuilder<TKeys>, {\n\t\t\tfields: this.fields,\n\t\t\toptions,\n\t\t})\n\t}\n\n\tunique(): IndexBuilder<TKeys> {\n\t\treturn this._clone({ ...this.options, unique: true })\n\t}\n\n\tsparse(): IndexBuilder<TKeys> {\n\t\treturn this._clone({ ...this.options, sparse: true })\n\t}\n\n\tname(name: string): IndexBuilder<TKeys> {\n\t\treturn this._clone({ ...this.options, name })\n\t}\n}\n\n/**\n * Create a compound index definition with a fluent builder API.\n *\n * Returns an {@link IndexBuilder} that is structurally compatible with\n * `CompoundIndexDefinition`, so it can be used directly in\n * `CollectionOptions.indexes` alongside plain objects.\n *\n * @param fields - An object mapping field names to sort direction (1 or -1).\n * @returns An {@link IndexBuilder} instance.\n *\n * @example\n * ```ts\n * collection('users', { email: z.string(), role: z.string() }, {\n * indexes: [\n * index({ email: 1, role: -1 }).unique(),\n * { fields: { role: 1 } },\n * ],\n * })\n * ```\n */\nexport function index<TKeys extends string>(\n\tfields: Record<TKeys, IndexDirection>,\n): IndexBuilder<TKeys> {\n\treturn new IndexBuilder(fields)\n}\n","import { ObjectId } from 'mongodb'\n\n/**\n * Create or coerce a MongoDB `ObjectId`.\n *\n * - Called with **no arguments**: generates a brand-new `ObjectId`.\n * - Called with a **hex string**: coerces it to an `ObjectId` via\n * `ObjectId.createFromHexString`.\n * - Called with an **existing `ObjectId`**: returns it unchanged.\n *\n * This is a convenience wrapper that removes the need for `new ObjectId()`\n * boilerplate throughout application code.\n *\n * @param value - Optional hex string or `ObjectId` to coerce. Omit to\n * generate a new `ObjectId`.\n * @returns An `ObjectId` instance.\n *\n * @example\n * ```ts\n * oid() // new random ObjectId\n * oid('64f1a2b3c4d5e6f7a8b9c0d1') // coerce hex string\n * oid(existingId) // pass-through\n * ```\n */\nexport function oid(): ObjectId\nexport function oid(value: string): ObjectId\nexport function oid(value: ObjectId): ObjectId\nexport function oid(value?: string | ObjectId): ObjectId {\n\tif (value === undefined) return new ObjectId()\n\tif (value instanceof ObjectId) return value\n\treturn ObjectId.createFromHexString(value)\n}\n\n/**\n * Type guard that narrows an `unknown` value to `ObjectId`.\n *\n * Uses `instanceof` internally, so it works with any value without risk\n * of throwing.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an `ObjectId` instance.\n *\n * @example\n * ```ts\n * const raw: unknown = getFromDb()\n * if (isOid(raw)) {\n * console.log(raw.toHexString()) // raw is narrowed to ObjectId\n * }\n * ```\n */\nexport function isOid(value: unknown): value is ObjectId {\n\treturn value instanceof ObjectId\n}\n","import type { TypedFilter } from './filter'\n\n// ── Value operators ──────────────────────────────────────────────────────────\n\n/**\n * Matches values equal to the specified value.\n *\n * @example\n * ```ts\n * // Explicit equality (equivalent to { name: 'Alice' })\n * users.find({ name: $eq('Alice') })\n * ```\n */\nexport const $eq = <V>(value: V): { $eq: V } => ({ $eq: value })\n\n/**\n * Matches values not equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ role: $ne('banned') })\n * ```\n */\nexport const $ne = <V>(value: V): { $ne: V } => ({ $ne: value })\n\n/**\n * Matches values greater than the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $gt(18) })\n * ```\n */\nexport const $gt = <V>(value: V): { $gt: V } => ({ $gt: value })\n\n/**\n * Matches values greater than or equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $gte(18) })\n * ```\n */\nexport const $gte = <V>(value: V): { $gte: V } => ({ $gte: value })\n\n/**\n * Matches values less than the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $lt(65) })\n * ```\n */\nexport const $lt = <V>(value: V): { $lt: V } => ({ $lt: value })\n\n/**\n * Matches values less than or equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $lte(65) })\n * ```\n */\nexport const $lte = <V>(value: V): { $lte: V } => ({ $lte: value })\n\n/**\n * Matches any value in the specified array.\n *\n * @example\n * ```ts\n * users.find({ role: $in(['admin', 'moderator']) })\n * ```\n */\nexport const $in = <V>(values: V[]): { $in: V[] } => ({ $in: values })\n\n/**\n * Matches none of the values in the specified array.\n *\n * @example\n * ```ts\n * users.find({ role: $nin(['banned', 'suspended']) })\n * ```\n */\nexport const $nin = <V>(values: V[]): { $nin: V[] } => ({ $nin: values })\n\n/**\n * Matches documents where the field exists (or does not exist).\n * Defaults to `true` when called with no arguments.\n *\n * @example\n * ```ts\n * // Field must exist\n * users.find({ email: $exists() })\n *\n * // Field must not exist\n * users.find({ deletedAt: $exists(false) })\n * ```\n */\nexport const $exists = (flag = true): { $exists: boolean } => ({ $exists: flag })\n\n/**\n * Matches string values against a regular expression pattern.\n * Only valid on string fields.\n *\n * @example\n * ```ts\n * users.find({ name: $regex(/^A/i) })\n * users.find({ email: $regex('^admin@') })\n * ```\n */\nexport const $regex = (pattern: RegExp | string): { $regex: RegExp | string } => ({\n\t$regex: pattern,\n})\n\n/**\n * Negates a comparison operator. Wraps the given operator object\n * in a `$not` condition.\n *\n * @example\n * ```ts\n * // Age is NOT greater than 65\n * users.find({ age: $not($gt(65)) })\n *\n * // Name does NOT match pattern\n * users.find({ name: $not($regex(/^test/)) })\n * ```\n */\nexport const $not = <O extends Record<string, unknown>>(op: O): { $not: O } => ({\n\t$not: op,\n})\n\n// ── Logical operators ────────────────────────────────────────────────────────\n\n/**\n * Joins filter clauses with a logical OR. Matches documents that satisfy\n * at least one of the provided filters.\n *\n * @example\n * ```ts\n * // T inferred from collection's find() context\n * users.find($or({ role: 'admin' }, { age: $gte(18) }))\n *\n * // Explicit generic for standalone usage\n * const filter = $or<User>({ role: 'admin' }, { age: $gte(18) })\n * ```\n */\nexport const $or = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $or: filters }) as TypedFilter<T>\n\n/**\n * Joins filter clauses with a logical AND. Matches documents that satisfy\n * all of the provided filters. Useful for dynamic filter building where\n * multiple conditions on the same field would conflict in an object literal.\n *\n * @example\n * ```ts\n * // T inferred from collection's find() context\n * users.find($and(\n * $or({ role: 'admin' }, { role: 'moderator' }),\n * { age: $gte(18) },\n * { email: $exists() },\n * ))\n * ```\n */\nexport const $and = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $and: filters }) as TypedFilter<T>\n\n/**\n * Joins filter clauses with a logical NOR. Matches documents that fail\n * all of the provided filters.\n *\n * @example\n * ```ts\n * // Exclude banned and suspended users\n * users.find($nor({ role: 'banned' }, { role: 'suspended' }))\n * ```\n */\nexport const $nor = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $nor: filters }) as TypedFilter<T>\n\n// ── Escape hatch ─────────────────────────────────────────────────────────────\n\n/**\n * Escape hatch for unsupported or raw MongoDB filter operators.\n * Wraps an untyped filter object so it can be passed where `TypedFilter<T>` is expected.\n * Use when you need operators not covered by the type system (e.g., `$text`, `$geoNear`).\n *\n * @example\n * ```ts\n * users.find(raw({ $text: { $search: 'mongodb tutorial' } }))\n * ```\n */\n// biome-ignore lint/suspicious/noExplicitAny: intentional escape hatch for raw MongoDB filters\nexport const raw = <T = any>(filter: Record<string, unknown>): TypedFilter<T> =>\n\tfilter as TypedFilter<T>\n"],"mappings":"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","names":["import_zod","collection","collection","raw","raw","import_zod","import_mongodb","import_zod","import_zod","import_zod","collection","import_mongodb","import_zod","import_mongodb"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client/client.ts","../src/indexes/spec.ts","../src/indexes/sync.ts","../src/crud/delete.ts","../src/errors/validation.ts","../src/crud/find.ts","../src/errors/not-found.ts","../src/indexes/warn.ts","../src/query/cursor.ts","../src/crud/paginate.ts","../src/crud/insert.ts","../src/crud/update.ts","../src/client/handle.ts","../src/collection/collection.ts","../src/schema/extensions.ts","../src/schema/ref.ts","../src/schema/object-id.ts","../src/collection/index-def.ts","../src/helpers/oid.ts","../src/query/operators.ts","../src/query/namespace.ts"],"sourcesContent":["export { CollectionHandle, createClient, Database, extractDbName } from './client'\nexport type {\n\tAnyCollection,\n\tCollectionDefinition,\n\tCollectionOptions,\n\tCompoundIndexDefinition,\n\tFieldIndexDefinition,\n\tIndexNames,\n\tInferDocument,\n\tInferInsert,\n\tResolvedShape,\n\tValidationMode,\n} from './collection'\nexport { collection, extractFieldIndexes, IndexBuilder, index } from './collection'\nexport type {\n\tCursorPage,\n\tCursorPaginateOptions,\n\tFindOneAndDeleteOptions,\n\tFindOneAndUpdateOptions,\n\tFindOneOptions,\n\tFindOptions,\n\tOffsetPage,\n\tOffsetPaginateOptions,\n\tUpdateOptions,\n} from './crud'\nexport {\n\tdeleteMany,\n\tdeleteOne,\n\tfind,\n\tfindOne,\n\tfindOneAndDelete,\n\tfindOneAndUpdate,\n\tfindOneOrThrow,\n\tinsertMany,\n\tinsertOne,\n\tupdateMany,\n\tupdateOne,\n} from './crud'\nexport { ZodmonNotFoundError, ZodmonValidationError } from './errors'\nexport { isOid, oid } from './helpers/oid'\nexport type { IndexSpec, StaleIndex, SyncIndexesOptions, SyncIndexesResult } from './indexes'\nexport {\n\tcheckUnindexedFields,\n\textractComparableOptions,\n\tgenerateIndexName,\n\tserializeIndexKey,\n\tsyncIndexes,\n\ttoCompoundIndexSpec,\n\ttoFieldIndexSpec,\n} from './indexes'\nexport type {\n\tAddToSetEach,\n\tAddToSetFields,\n\tArrayElement,\n\tComparisonOperators,\n\tCurrentDateFields,\n\tDotPaths,\n\tDotPathType,\n\tIncFields,\n\tPopFields,\n\tPullFields,\n\tPushFields,\n\tPushModifiers,\n\tRenameFields,\n\tSetFields,\n\tTypedFilter,\n\tTypedSort,\n\tTypedUpdateFilter,\n\tUnsetFields,\n} from './query'\nexport {\n\t$,\n\t$and,\n\t$eq,\n\t$exists,\n\t$gt,\n\t$gte,\n\t$in,\n\t$lt,\n\t$lte,\n\t$ne,\n\t$nin,\n\t$nor,\n\t$not,\n\t$or,\n\t$regex,\n\traw,\n\tTypedFindCursor,\n} from './query'\nexport {\n\tgetIndexMetadata,\n\tgetRefMetadata,\n\ttype IndexMetadata,\n\ttype IndexOptions,\n\tobjectId,\n\ttype RefMarker,\n\ttype RefMetadata,\n\ttype ZodObjectId,\n} from './schema'\nexport type { FilterOf, HandleOf, SortOf, UpdateFilterOf } from './types'\n","import type { Db, MongoClientOptions } from 'mongodb'\nimport { MongoClient } from 'mongodb'\nimport type { z } from 'zod'\nimport type {\n\tCollectionDefinition,\n\tCompoundIndexDefinition,\n\tInferDocument,\n} from '../collection/types'\nimport { syncIndexes as _syncCollectionIndexes } from '../indexes/sync'\nimport type { SyncIndexesOptions, SyncIndexesResult } from '../indexes/types'\nimport { CollectionHandle } from './handle'\n\n/**\n * Wraps a MongoDB `MongoClient` and `Db`, providing typed collection access\n * through {@link CollectionHandle}s.\n *\n * Connection is lazy — the driver connects on the first operation, not at\n * construction time. Call {@link close} for graceful shutdown.\n *\n * @example\n * ```ts\n * const db = createClient('mongodb://localhost:27017', 'myapp')\n * const users = db.use(UsersCollection)\n * await users.native.insertOne({ _id: oid(), name: 'Ada' })\n * await db.close()\n * ```\n */\nexport class Database {\n\tprivate readonly _client: MongoClient\n\tprivate readonly _db: Db\n\t/** Registered collection definitions, keyed by name. Used by syncIndexes(). */\n\tprivate readonly _collections = new Map<string, CollectionDefinition>()\n\n\tconstructor(uri: string, dbName: string, options?: MongoClientOptions) {\n\t\tthis._client = new MongoClient(uri, options)\n\t\tthis._db = this._client.db(dbName)\n\t}\n\n\t/**\n\t * Register a collection definition and return a typed {@link CollectionHandle}.\n\t *\n\t * The handle's `native` property is a MongoDB `Collection<TDoc>` where `TDoc`\n\t * is the document type inferred from the definition's Zod schema. Calling\n\t * `use()` multiple times with the same definition is safe — each call returns\n\t * a new lightweight handle backed by the same underlying driver collection.\n\t *\n\t * @param def - A collection definition created by `collection()`.\n\t * @returns A typed collection handle for CRUD operations.\n\t */\n\tuse<\n\t\tTShape extends z.core.$ZodShape,\n\t\tTIndexes extends readonly CompoundIndexDefinition<\n\t\t\tExtract<keyof TShape, string>\n\t\t>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[],\n\t>(\n\t\tdef: CollectionDefinition<TShape, TIndexes>,\n\t): CollectionHandle<CollectionDefinition<TShape, TIndexes>> {\n\t\t// Safe cast: erasing TShape for internal collection tracking.\n\t\t// Stored definitions are only iterated in syncIndexes() where\n\t\t// index metadata is accessed structurally.\n\t\tthis._collections.set(def.name, def as unknown as CollectionDefinition)\n\t\tconst native = this._db.collection<InferDocument<CollectionDefinition<TShape, TIndexes>>>(\n\t\t\tdef.name,\n\t\t)\n\t\t// Safe cast: CollectionDefinition<TShape, TIndexes> → AnyCollection for the constructor.\n\t\t// The generic is preserved through the return type annotation.\n\t\treturn new CollectionHandle(\n\t\t\tdef as unknown as CollectionDefinition<TShape, TIndexes>,\n\t\t\tnative,\n\t\t) as CollectionHandle<CollectionDefinition<TShape, TIndexes>>\n\t}\n\n\t/**\n\t * Synchronize indexes for all registered collections with MongoDB.\n\t *\n\t * Iterates every collection registered via {@link use} and calls\n\t * {@link syncIndexes} on each one. Returns a record keyed by collection\n\t * name with the sync result for each.\n\t *\n\t * @param options - Optional sync behavior (dryRun, dropOrphaned).\n\t * @returns A record mapping collection names to their sync results.\n\t *\n\t * @example\n\t * ```ts\n\t * const db = createClient('mongodb://localhost:27017', 'myapp')\n\t * db.use(Users)\n\t * db.use(Posts)\n\t * const results = await db.syncIndexes()\n\t * console.log(results['users'].created) // ['email_1']\n\t * console.log(results['posts'].created) // ['title_1']\n\t * ```\n\t */\n\tasync syncIndexes(options?: SyncIndexesOptions): Promise<Record<string, SyncIndexesResult>> {\n\t\tconst results: Record<string, SyncIndexesResult> = {}\n\t\tfor (const [name, def] of this._collections) {\n\t\t\tconst native = this._db.collection(name)\n\t\t\tconst handle = new CollectionHandle(def, native)\n\t\t\tresults[name] = await _syncCollectionIndexes(handle, options)\n\t\t}\n\t\treturn results\n\t}\n\n\t/**\n\t * Execute a function within a MongoDB transaction with auto-commit/rollback.\n\t *\n\t * Stub — full implementation in TASK-106.\n\t */\n\ttransaction<T>(_fn: () => Promise<T>): Promise<T> {\n\t\tthrow new Error('Not implemented')\n\t}\n\n\t/**\n\t * Close the underlying `MongoClient` connection. Safe to call even if\n\t * no connection was established (the driver handles this gracefully).\n\t */\n\tasync close(): Promise<void> {\n\t\tawait this._client.close()\n\t}\n}\n\n/**\n * Extract the database name from a MongoDB connection URI.\n *\n * Handles standard URIs, multi-host/replica set, SRV (`mongodb+srv://`),\n * auth credentials, query parameters, and percent-encoded database names.\n * Returns `undefined` when no database name is present.\n */\nexport function extractDbName(uri: string): string | undefined {\n\tconst withoutProtocol = uri.replace(/^mongodb(?:\\+srv)?:\\/\\//, '')\n\t// Safe cast: split() always returns at least one element, but noUncheckedIndexedAccess\n\t// types [0] as string | undefined.\n\tconst withoutQuery = withoutProtocol.split('?')[0] as string\n\t// Skip past auth credentials (user:pass@) before searching for the path separator\n\tconst atIndex = withoutQuery.lastIndexOf('@')\n\tconst hostAndPath = atIndex === -1 ? withoutQuery : withoutQuery.slice(atIndex + 1)\n\tconst slashIndex = hostAndPath.indexOf('/')\n\tif (slashIndex === -1) return undefined\n\tconst dbName = decodeURIComponent(hostAndPath.slice(slashIndex + 1))\n\treturn dbName || undefined\n}\n\n/**\n * Create a new {@link Database} instance wrapping a MongoDB connection.\n *\n * The connection is lazy — the driver connects on the first operation.\n * Pass any `MongoClientOptions` to configure connection pooling, timeouts, etc.\n *\n * When `dbName` is omitted, the database name is extracted from the URI path\n * (e.g. `mongodb://localhost:27017/myapp` → `'myapp'`). If no database name\n * is found in either the arguments or the URI, a warning is logged and\n * MongoDB's default `'test'` database is used.\n *\n * @param uri - MongoDB connection string (e.g. `mongodb://localhost:27017`).\n * @param dbName - The database name to use.\n * @param options - Optional MongoDB driver client options.\n * @returns A new `Database` instance.\n */\nexport function createClient(uri: string, dbName: string, options?: MongoClientOptions): Database\nexport function createClient(uri: string, options?: MongoClientOptions): Database\nexport function createClient(\n\turi: string,\n\tdbNameOrOptions?: string | MongoClientOptions,\n\tmaybeOptions?: MongoClientOptions,\n): Database {\n\tif (typeof dbNameOrOptions === 'string') {\n\t\treturn new Database(uri, dbNameOrOptions, maybeOptions)\n\t}\n\tconst parsed = extractDbName(uri)\n\tif (!parsed) {\n\t\tconsole.warn('[zodmon] No database name provided — using MongoDB default \"test\"')\n\t}\n\treturn new Database(uri, parsed ?? 'test', dbNameOrOptions)\n}\n","import type { CompoundIndexDefinition, FieldIndexDefinition } from '../collection/types'\n\n/**\n * A normalized index specification ready for comparison and creation.\n *\n * `key` maps field paths to direction (`1`, `-1`, or `'text'`).\n * `options` holds MongoDB index options (`unique`, `sparse`, etc.).\n *\n * @example\n * ```ts\n * const spec: IndexSpec = {\n * key: { email: 1 },\n * options: { unique: true },\n * }\n * ```\n */\nexport type IndexSpec = {\n\tkey: Record<string, 1 | -1 | 'text'>\n\toptions: Record<string, unknown>\n}\n\n/**\n * Convert a field-level index definition to a normalized {@link IndexSpec}.\n *\n * Maps schema metadata (`text`, `descending`, `unique`, `sparse`, `expireAfter`,\n * `partial`) to their MongoDB driver equivalents.\n *\n * @param def - A field index definition extracted from schema metadata.\n * @returns A normalized index spec with key and options.\n *\n * @example\n * ```ts\n * const spec = toFieldIndexSpec({ field: 'email', indexed: true, unique: true })\n * // => { key: { email: 1 }, options: { unique: true } }\n *\n * const ttl = toFieldIndexSpec({ field: 'expiresAt', indexed: true, expireAfter: 3600 })\n * // => { key: { expiresAt: 1 }, options: { expireAfterSeconds: 3600 } }\n * ```\n */\nexport function toFieldIndexSpec(def: FieldIndexDefinition): IndexSpec {\n\tconst direction: 1 | -1 | 'text' = def.text ? 'text' : def.descending ? -1 : 1\n\tconst key: Record<string, 1 | -1 | 'text'> = { [def.field]: direction }\n\n\tconst options: Record<string, unknown> = {}\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.unique) options['unique'] = true\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.sparse) options['sparse'] = true\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.expireAfter !== undefined) options['expireAfterSeconds'] = def.expireAfter\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.partial) options['partialFilterExpression'] = def.partial\n\n\treturn { key, options }\n}\n\n/**\n * Convert a compound index definition to a normalized {@link IndexSpec}.\n *\n * Copies the `fields` map directly to `key` and maps builder options\n * (`unique`, `sparse`, `name`, `partial`) to MongoDB driver equivalents.\n *\n * @param def - A compound index definition from the collection options.\n * @returns A normalized index spec with key and options.\n *\n * @example\n * ```ts\n * const spec = toCompoundIndexSpec({\n * fields: { email: 1, role: -1 },\n * options: { unique: true, name: 'email_role_idx' },\n * })\n * // => { key: { email: 1, role: -1 }, options: { unique: true, name: 'email_role_idx' } }\n * ```\n */\nexport function toCompoundIndexSpec(def: CompoundIndexDefinition): IndexSpec {\n\t// Compound fields are Partial<Record<K, 1|-1>> structurally, but all keys\n\t// are present at runtime. Cast to the concrete key type for IndexSpec.\n\tconst key = { ...def.fields } as Record<string, 1 | -1 | 'text'>\n\n\tconst options: Record<string, unknown> = {}\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.options?.unique) options['unique'] = true\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.options?.sparse) options['sparse'] = true\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.options?.name) options['name'] = def.options.name\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tif (def.options?.partial) options['partialFilterExpression'] = def.options.partial\n\n\treturn { key, options }\n}\n\n/**\n * Produce a stable, deterministic string from an index key for comparison.\n *\n * Entries are sorted alphabetically by field name and formatted as\n * `field:direction` pairs joined by commas. Two index keys that should be\n * considered the same will always produce the same string.\n *\n * @param key - An index key mapping field names to direction.\n * @returns A string like `'email:1,role:-1'`.\n *\n * @example\n * ```ts\n * serializeIndexKey({ email: 1, role: -1 })\n * // => 'email:1,role:-1'\n *\n * serializeIndexKey({ name: 'text' })\n * // => 'name:text'\n * ```\n */\nexport function serializeIndexKey(key: Record<string, 1 | -1 | 'text'>): string {\n\t// Field order is preserved intentionally — in MongoDB, { a: 1, b: 1 } and\n\t// { b: 1, a: 1 } are distinct compound indexes (order determines prefix matching).\n\treturn Object.entries(key)\n\t\t.map(([field, dir]) => `${field}:${dir}`)\n\t\t.join(',')\n}\n","import type { Collection } from 'mongodb'\nimport type { CompoundIndexDefinition, FieldIndexDefinition } from '../collection/types'\nimport type { IndexSpec } from './spec'\nimport { serializeIndexKey, toCompoundIndexSpec, toFieldIndexSpec } from './spec'\nimport type { StaleIndex, SyncIndexesOptions, SyncIndexesResult } from './types'\n\n/**\n * Structural constraint for the handle argument of {@link syncIndexes}.\n *\n * Uses a structural type rather than `CollectionHandle<AnyCollection>` to avoid\n * TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection\n * handle returned by `db.use()` satisfies this constraint.\n */\ntype SyncableHandle = {\n\treadonly definition: {\n\t\treadonly fieldIndexes: FieldIndexDefinition[]\n\t\treadonly compoundIndexes: readonly CompoundIndexDefinition[]\n\t}\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB native collection type variance requires escape hatch\n\treadonly native: Collection<any>\n}\n\n/**\n * Relevant option keys extracted from MongoDB index info for comparison.\n *\n * When checking whether an existing index matches a desired spec, only these\n * keys are compared — other MongoDB-internal properties (v, ns, etc.) are\n * ignored.\n */\nconst COMPARABLE_OPTION_KEYS = [\n\t'unique',\n\t'sparse',\n\t'expireAfterSeconds',\n\t'partialFilterExpression',\n] as const\n\n/**\n * Extract the comparable options from a MongoDB index info object.\n *\n * Pulls only the keys listed in {@link COMPARABLE_OPTION_KEYS} from the raw\n * index info returned by `listIndexes()`. Keys whose value is `undefined`\n * are omitted so that JSON comparison works correctly.\n *\n * @param info - A raw MongoDB index info object.\n * @returns A plain object with only the relevant option keys.\n *\n * @example\n * ```ts\n * const opts = extractComparableOptions({ v: 2, unique: true, key: { email: 1 } })\n * // => { unique: true }\n * ```\n */\nexport function extractComparableOptions(info: Record<string, unknown>): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {}\n\tfor (const key of COMPARABLE_OPTION_KEYS) {\n\t\tif (info[key] !== undefined) {\n\t\t\tresult[key] = info[key]\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Generate the default MongoDB index name from a key spec.\n *\n * MongoDB names indexes by joining `field_direction` pairs with underscores.\n * For example, `{ email: 1, role: -1 }` becomes `'email_1_role_-1'`.\n *\n * @param key - An index key mapping field names to direction.\n * @returns The generated index name string.\n *\n * @example\n * ```ts\n * generateIndexName({ email: 1 })\n * // => 'email_1'\n *\n * generateIndexName({ email: 1, role: -1 })\n * // => 'email_1_role_-1'\n * ```\n */\nexport function generateIndexName(key: Record<string, 1 | -1 | 'text'>): string {\n\treturn Object.entries(key)\n\t\t.map(([field, dir]) => `${field}_${dir}`)\n\t\t.join('_')\n}\n\n/**\n * Sort an object's keys alphabetically for deterministic JSON comparison.\n *\n * @param obj - A plain object.\n * @returns A new object with keys sorted alphabetically.\n */\nfunction sortKeys(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst sorted: Record<string, unknown> = {}\n\tfor (const key of Object.keys(obj).sort()) {\n\t\tsorted[key] = obj[key]\n\t}\n\treturn sorted\n}\n\n/** Resolve the name for an index spec, preferring an explicit name. */\nfunction resolveSpecName(spec: IndexSpec): string {\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tconst specName = spec.options['name']\n\treturn typeof specName === 'string' ? specName : generateIndexName(spec.key)\n}\n\n/** Resolve the name from a raw MongoDB index info object. */\nfunction resolveExistingName(\n\tinfo: Record<string, unknown>,\n\tkey: Record<string, 1 | -1 | 'text'>,\n): string {\n\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\tconst infoName = info['name']\n\tif (typeof infoName === 'string') return infoName\n\treturn generateIndexName(key)\n}\n\n/**\n * Check whether two option objects are equivalent via sorted JSON.\n * The `name` key is excluded from comparison since it identifies\n * the index rather than defining its behavior.\n */\nfunction optionsMatch(a: Record<string, unknown>, b: Record<string, unknown>): boolean {\n\treturn JSON.stringify(sortKeys(stripName(a))) === JSON.stringify(sortKeys(stripName(b)))\n}\n\n/** Remove the `name` key from an options object for comparison purposes. */\nfunction stripName(obj: Record<string, unknown>): Record<string, unknown> {\n\tconst { name: _, ...rest } = obj\n\treturn rest\n}\n\n/** Mutable accumulator for sync results. */\ntype SyncAccumulator = {\n\tcreated: string[]\n\tdropped: string[]\n\tskipped: string[]\n\tstale: StaleIndex[]\n\tmatchedKeys: Set<string>\n}\n\n/** Process a single desired spec against the existing index map. */\nasync function processDesiredSpec(\n\tspec: IndexSpec,\n\texistingByKey: Map<string, Record<string, unknown>>,\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB native collection type variance requires escape hatch\n\tnative: Collection<any>,\n\tdryRun: boolean,\n\tdropOrphaned: boolean,\n\tacc: SyncAccumulator,\n): Promise<void> {\n\tconst serialized = serializeIndexKey(spec.key)\n\tconst existing = existingByKey.get(serialized)\n\n\tif (!existing) {\n\t\tif (!dryRun) await native.createIndex(spec.key, spec.options)\n\t\tacc.created.push(resolveSpecName(spec))\n\t\treturn\n\t}\n\n\tacc.matchedKeys.add(serialized)\n\tconst existingName = resolveExistingName(existing, spec.key)\n\tconst existingOpts = extractComparableOptions(existing)\n\n\tif (optionsMatch(existingOpts, spec.options)) {\n\t\tacc.skipped.push(existingName)\n\t\treturn\n\t}\n\n\tif (dropOrphaned) {\n\t\tif (!dryRun) {\n\t\t\tawait native.dropIndex(existingName)\n\t\t\tawait native.createIndex(spec.key, spec.options)\n\t\t}\n\t\tacc.dropped.push(existingName)\n\t\tacc.created.push(resolveSpecName(spec))\n\t\treturn\n\t}\n\n\tacc.stale.push({\n\t\tname: existingName,\n\t\tkey: spec.key,\n\t\texisting: existingOpts,\n\t\tdesired: spec.options,\n\t})\n}\n\n/** Collect orphaned indexes that exist in MongoDB but are not in the desired set. */\nasync function processOrphanedIndexes(\n\texistingIndexes: Record<string, unknown>[],\n\tdesiredKeys: Set<string>,\n\tmatchedKeys: Set<string>,\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB native collection type variance requires escape hatch\n\tnative: Collection<any>,\n\tdryRun: boolean,\n\tdropOrphaned: boolean,\n\tdropped: string[],\n): Promise<void> {\n\tfor (const idx of existingIndexes) {\n\t\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\t\tconst rawName = idx['name']\n\t\tconst name = typeof rawName === 'string' ? rawName : ''\n\t\tif (name === '_id_') continue\n\n\t\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\t\tconst serialized = serializeIndexKey(idx['key'] as Record<string, 1 | -1 | 'text'>)\n\t\tif (matchedKeys.has(serialized) || desiredKeys.has(serialized)) continue\n\n\t\tif (dropOrphaned) {\n\t\t\tif (!dryRun) await native.dropIndex(name)\n\t\t\tdropped.push(name)\n\t\t}\n\t}\n}\n\n/**\n * Fetch existing indexes, returning an empty array when the collection\n * does not exist yet (MongoDB throws \"ns does not exist\" in that case).\n */\nasync function listIndexesSafe(\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB native collection type variance requires escape hatch\n\tnative: Collection<any>,\n): Promise<Record<string, unknown>[]> {\n\ttry {\n\t\treturn (await native.listIndexes().toArray()) as unknown as Record<string, unknown>[]\n\t} catch (err) {\n\t\t// MongoDB 7 throws when the namespace (collection) doesn't exist\n\t\tif (err instanceof Error && err.message.includes('ns does not exist')) {\n\t\t\treturn []\n\t\t}\n\t\tthrow err\n\t}\n}\n\n/**\n * Synchronize the indexes declared in a collection's schema with MongoDB.\n *\n * Compares the desired indexes (from field-level and compound index definitions)\n * with the indexes that currently exist in MongoDB, then creates, drops, or\n * reports differences depending on the options.\n *\n * **Algorithm:**\n * 1. Build desired specs from field indexes and compound indexes.\n * 2. Fetch existing indexes from MongoDB via `listIndexes()`.\n * 3. For each desired spec, compare against existing:\n * - Missing → create (unless `dryRun`).\n * - Present with same options → skip.\n * - Present with different options → stale (or drop+recreate if `dropOrphaned`).\n * 4. For each existing index not in desired set (excluding `_id_`):\n * - If `dropOrphaned` → drop (unless `dryRun`).\n * 5. Return a summary of all actions taken.\n *\n * @param handle - A collection handle created by `db.use()`.\n * @param options - Optional sync behavior (dryRun, dropOrphaned).\n * @returns A summary of created, dropped, skipped, and stale indexes.\n *\n * @example\n * ```ts\n * import { syncIndexes } from '@zodmon/core'\n *\n * const users = db.use(Users)\n * const result = await syncIndexes(users)\n * console.log('Created:', result.created)\n * console.log('Stale:', result.stale.map(s => s.name))\n * ```\n *\n * @example\n * ```ts\n * // Dry run — no changes made\n * const diff = await syncIndexes(users, { dryRun: true })\n * console.log('Would create:', diff.created)\n * console.log('Would drop:', diff.dropped)\n * ```\n */\nexport async function syncIndexes(\n\thandle: SyncableHandle,\n\toptions?: SyncIndexesOptions,\n): Promise<SyncIndexesResult> {\n\tconst { dryRun = false, dropOrphaned = false } = options ?? {}\n\tconst native = handle.native\n\tconst def = handle.definition\n\n\t// 1. Build desired specs\n\tconst desiredSpecs: IndexSpec[] = [\n\t\t...def.fieldIndexes.map(toFieldIndexSpec),\n\t\t...def.compoundIndexes.map(toCompoundIndexSpec),\n\t]\n\n\t// 2. Fetch existing indexes (empty array when collection doesn't exist yet)\n\tconst existingIndexes = await listIndexesSafe(native)\n\n\t// 3. Build map of existing indexes keyed by serialized key\n\tconst existingByKey = new Map<string, Record<string, unknown>>()\n\tfor (const idx of existingIndexes) {\n\t\t// biome-ignore lint/complexity/useLiteralKeys: index signature requires bracket notation for TS noUncheckedIndexedAccess\n\t\tconst serialized = serializeIndexKey(idx['key'] as Record<string, 1 | -1 | 'text'>)\n\t\texistingByKey.set(serialized, idx)\n\t}\n\n\tconst acc: SyncAccumulator = {\n\t\tcreated: [],\n\t\tdropped: [],\n\t\tskipped: [],\n\t\tstale: [],\n\t\tmatchedKeys: new Set<string>(),\n\t}\n\n\t// 4. Process desired specs\n\tfor (const spec of desiredSpecs) {\n\t\tawait processDesiredSpec(spec, existingByKey, native, dryRun, dropOrphaned, acc)\n\t}\n\n\t// 5. Handle orphaned indexes\n\tconst desiredKeys = new Set(desiredSpecs.map((s) => serializeIndexKey(s.key)))\n\tawait processOrphanedIndexes(\n\t\texistingIndexes,\n\t\tdesiredKeys,\n\t\tacc.matchedKeys,\n\t\tnative,\n\t\tdryRun,\n\t\tdropOrphaned,\n\t\tacc.dropped,\n\t)\n\n\treturn {\n\t\tcreated: acc.created,\n\t\tdropped: acc.dropped,\n\t\tskipped: acc.skipped,\n\t\tstale: acc.stale,\n\t}\n}\n","import type { DeleteResult } from 'mongodb'\nimport { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, InferDocument, ValidationMode } from '../collection/types'\nimport { ZodmonValidationError } from '../errors/validation'\nimport type { TypedFilter } from '../query/filter'\n\n/**\n * Options for {@link findOneAndDelete}.\n */\nexport type FindOneAndDeleteOptions = {\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Delete a single document matching the filter.\n *\n * Removes the first document that matches the filter from the collection.\n * No validation is performed — the document is deleted directly through\n * the MongoDB driver.\n *\n * @param handle - The collection handle to delete from.\n * @param filter - Type-safe filter to match documents.\n * @returns The MongoDB `DeleteResult` with the deleted count.\n *\n * @example\n * ```ts\n * const result = await deleteOne(users, { name: 'Ada' })\n * console.log(result.deletedCount) // 1\n * ```\n */\nexport async function deleteOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n): Promise<DeleteResult> {\n\t// Safe cast: TypedFilter is a strict subset of MongoDB's Filter<T>,\n\t// but the intersection-based mapped type cannot be structurally\n\t// matched by the driver's looser type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\treturn await handle.native.deleteOne(filter as any)\n}\n\n/**\n * Delete all documents matching the filter.\n *\n * Removes every document that matches the filter from the collection.\n * No validation is performed — documents are deleted directly through\n * the MongoDB driver.\n *\n * @param handle - The collection handle to delete from.\n * @param filter - Type-safe filter to match documents.\n * @returns The MongoDB `DeleteResult` with the deleted count.\n *\n * @example\n * ```ts\n * const result = await deleteMany(users, { role: 'guest' })\n * console.log(result.deletedCount) // number of guests removed\n * ```\n */\nexport async function deleteMany<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n): Promise<DeleteResult> {\n\t// Safe cast: TypedFilter is a strict subset of MongoDB's Filter<T>,\n\t// but the intersection-based mapped type cannot be structurally\n\t// matched by the driver's looser type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\treturn await handle.native.deleteMany(filter as any)\n}\n\n/**\n * Find a single document matching the filter, delete it, and return the document.\n *\n * Returns the deleted document, or `null` if no document matches the filter.\n * The returned document is validated against the collection's Zod schema\n * using the same resolution logic as {@link findOne}.\n *\n * @param handle - The collection handle to delete from.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional settings: `validate`.\n * @returns The deleted document, or `null` if no document matches.\n * @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOneAndDelete(users, { name: 'Ada' })\n * if (user) console.log(user.name) // 'Ada' (the deleted document)\n * ```\n *\n * @example\n * ```ts\n * const user = await findOneAndDelete(\n * users,\n * { role: 'guest' },\n * { validate: false },\n * )\n * ```\n */\nexport async function findOneAndDelete<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOneAndDeleteOptions,\n): Promise<InferDocument<TDef> | null> {\n\t// Safe cast: TypedFilter is a strict subset of MongoDB's Filter<T>,\n\t// but the intersection-based mapped type cannot be structurally\n\t// matched by the driver's looser type.\n\t// The options object uses includeResultMetadata: false to return the\n\t// document directly instead of a ModifyResult wrapper.\n\tconst result = await handle.native.findOneAndDelete(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\t\tfilter as any,\n\t\t{ includeResultMetadata: false },\n\t)\n\tif (!result) return null\n\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\n\tif (mode === false || mode === 'passthrough') {\n\t\t// Safe cast: raw document from MongoDB matches the collection's document\n\t\t// shape at runtime. Skipping validation per user request.\n\t\treturn result as InferDocument<TDef>\n\t}\n\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\treturn handle.definition.schema.parse(result) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n}\n","import type { z } from 'zod'\n\n/**\n * Thrown when a document fails Zod schema validation before a MongoDB write.\n *\n * Wraps the original `ZodError` with the collection name and a human-readable\n * message listing each invalid field and its error. Callers can inspect\n * `.zodError.issues` for programmatic access to individual failures.\n *\n * @example\n * ```ts\n * try {\n * await users.insertOne({ name: 123 })\n * } catch (err) {\n * if (err instanceof ZodmonValidationError) {\n * console.log(err.message)\n * // => 'Validation failed for \"users\": name (Expected string, received number)'\n * console.log(err.collection) // => 'users'\n * console.log(err.zodError) // => ZodError with .issues array\n * }\n * }\n * ```\n */\nexport class ZodmonValidationError extends Error {\n\toverride readonly name = 'ZodmonValidationError'\n\n\t/** The MongoDB collection name where the validation failed. */\n\treadonly collection: string\n\n\t/** The original Zod validation error with detailed issue information. */\n\treadonly zodError: z.ZodError\n\n\tconstructor(collection: string, zodError: z.ZodError) {\n\t\tconst fields = zodError.issues\n\t\t\t.map((issue) => {\n\t\t\t\tconst path = issue.path.join('.') || '(root)'\n\t\t\t\treturn `${path} (${issue.message})`\n\t\t\t})\n\t\t\t.join(', ')\n\t\tsuper(`Validation failed for \"${collection}\": ${fields}`)\n\t\tthis.collection = collection\n\t\tthis.zodError = zodError\n\t}\n}\n","import type { FindCursor } from 'mongodb'\nimport { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, IndexNames, InferDocument, ValidationMode } from '../collection/types'\nimport { ZodmonNotFoundError } from '../errors/not-found'\nimport { ZodmonValidationError } from '../errors/validation'\nimport { checkUnindexedFields } from '../indexes/warn'\nimport { TypedFindCursor } from '../query/cursor'\nimport type { TypedFilter } from '../query/filter'\n\n/**\n * Options for {@link findOne} and {@link findOneOrThrow}.\n */\nexport type FindOneOptions = {\n\t/** MongoDB projection — include (`1`) or exclude (`0`) fields. Typed projections deferred to v1.0. */\n\tproject?: Record<string, 0 | 1>\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Find a single document matching the filter.\n *\n * Queries MongoDB, then validates the fetched document against the collection's\n * Zod schema. Validation mode is resolved from the per-query option, falling\n * back to the collection-level default (which defaults to `'strict'`).\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional projection and validation overrides.\n * @returns The matched document, or `null` if no document matches.\n * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOne(users, { name: 'Ada' })\n * if (user) console.log(user.role) // typed as 'admin' | 'user'\n * ```\n */\nexport async function findOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOneOptions,\n): Promise<InferDocument<TDef> | null> {\n\t// Safe cast: TypedFilter's top-level keys are string field names and $-operators,\n\t// both of which are captured by Record<string, unknown>.\n\tcheckUnindexedFields(handle.definition, filter as Record<string, unknown>)\n\tconst findOptions = options?.project ? { projection: options.project } : undefined\n\t// Safe cast: TypedFilter<InferDocument<TDef>> is a strict subset of\n\t// MongoDB's Filter<T>, but the intersection-based mapped type cannot\n\t// be structurally matched by the driver's looser Filter type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\tconst raw = await handle.native.findOne(filter as any, findOptions)\n\tif (!raw) return null\n\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\n\tif (mode === false || mode === 'passthrough') {\n\t\t// Safe cast: raw document from MongoDB matches the collection's document\n\t\t// shape at runtime. Skipping validation per user request.\n\t\treturn raw as InferDocument<TDef>\n\t}\n\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\treturn handle.definition.schema.parse(raw) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n}\n\n/**\n * Find a single document matching the filter, or throw if none exists.\n *\n * Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}\n * instead of returning `null` when no document matches the filter.\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional projection and validation overrides.\n * @returns The matched document (never null).\n * @throws {ZodmonNotFoundError} When no document matches the filter.\n * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOneOrThrow(users, { name: 'Ada' })\n * console.log(user.role) // typed as 'admin' | 'user', guaranteed non-null\n * ```\n */\nexport async function findOneOrThrow<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOneOptions,\n): Promise<InferDocument<TDef>> {\n\tconst doc = await findOne(handle, filter, options)\n\tif (!doc) {\n\t\tthrow new ZodmonNotFoundError(handle.definition.name)\n\t}\n\treturn doc\n}\n\n/**\n * Options for {@link find}.\n */\nexport type FindOptions = {\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Find all documents matching the filter, returning a chainable typed cursor.\n *\n * The cursor is lazy — no query is executed until a terminal method\n * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`\n * to shape the query before executing.\n *\n * Each document is validated against the collection's Zod schema when\n * a terminal method consumes it.\n *\n * @param handle - The collection handle to query.\n * @param filter - Type-safe filter to match documents.\n * @param options - Optional validation overrides.\n * @returns A typed cursor for chaining query modifiers.\n *\n * @example\n * ```ts\n * const admins = await find(users, { role: 'admin' })\n * .sort({ name: 1 })\n * .limit(10)\n * .toArray()\n * ```\n *\n * @example\n * ```ts\n * for await (const user of find(users, {})) {\n * console.log(user.name)\n * }\n * ```\n */\nexport function find<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\toptions?: FindOptions,\n): TypedFindCursor<TDef, IndexNames<TDef>> {\n\t// Safe cast: TypedFilter's top-level keys are string field names and $-operators,\n\t// both of which are captured by Record<string, unknown>.\n\tcheckUnindexedFields(handle.definition, filter as Record<string, unknown>)\n\t// Safe cast: TypedFilter<InferDocument<TDef>> is a strict subset of\n\t// MongoDB's Filter<T>, but the intersection-based mapped type cannot\n\t// be structurally matched by the driver's looser Filter type.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\tconst raw = handle.native.find(filter as any)\n\t// Safe cast: Collection.find() returns FindCursor<WithId<T>>, but our schemas\n\t// always include _id so WithId<InferDocument<TDef>> is structurally identical\n\t// to InferDocument<TDef>. TypeScript cannot prove this with exactOptionalPropertyTypes.\n\tconst cursor = raw as unknown as FindCursor<InferDocument<TDef>>\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\treturn new TypedFindCursor(cursor, handle.definition, mode, handle.native, filter)\n}\n","/**\n * Thrown when a query expected to find a document returns no results.\n *\n * Used by {@link findOneOrThrow} when no document matches the provided filter.\n * Callers can inspect `.collection` to identify which collection the query targeted.\n *\n * @example\n * ```ts\n * try {\n * await users.findOneOrThrow({ name: 'nonexistent' })\n * } catch (err) {\n * if (err instanceof ZodmonNotFoundError) {\n * console.log(err.message) // => 'Document not found in \"users\"'\n * console.log(err.collection) // => 'users'\n * }\n * }\n * ```\n */\nexport class ZodmonNotFoundError extends Error {\n\toverride readonly name = 'ZodmonNotFoundError'\n\n\t/** The MongoDB collection name where the query found no results. */\n\treadonly collection: string\n\n\tconstructor(collection: string) {\n\t\tsuper(`Document not found in \"${collection}\"`)\n\t\tthis.collection = collection\n\t}\n}\n","import type { CompoundIndexDefinition, FieldIndexDefinition } from '../collection/types'\n\n/**\n * Structural constraint for the definition argument of {@link checkUnindexedFields}.\n *\n * Uses a structural type rather than `CollectionDefinition<z.core.$ZodShape>` to avoid\n * TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection\n * definition returned by `collection()` satisfies this constraint.\n */\ntype WarnableDefinition = {\n\treadonly name: string\n\treadonly fieldIndexes: FieldIndexDefinition[]\n\treadonly compoundIndexes: readonly CompoundIndexDefinition[]\n\treadonly options: {\n\t\twarnUnindexedQueries?: boolean\n\t}\n}\n\n/** MongoDB query operators that should be skipped during unindexed field checks. */\nconst SKIP_OPERATORS = new Set(['$or', '$and', '$nor', '$text', '$where', '$expr', '$comment'])\n\n/**\n * Warn about unindexed fields used in a query filter.\n *\n * When `warnUnindexedQueries` is enabled on a collection definition, this\n * function checks each top-level field in the filter against the collection's\n * declared indexes. Fields that are not covered by any index produce a\n * `console.warn` message to help identify queries that may cause full\n * collection scans in development.\n *\n * **Covered fields:**\n * - `_id` (always indexed by MongoDB)\n * - Fields with `.index()`, `.unique()`, `.text()`, or `.expireAfter()` (field-level indexes)\n * - The **first** field of each compound index (prefix matching)\n *\n * **Skipped keys:**\n * - MongoDB operators: `$or`, `$and`, `$nor`, `$text`, `$where`, `$expr`, `$comment`\n *\n * This function is a no-op (zero overhead) when `warnUnindexedQueries` is not\n * explicitly set to `true`.\n *\n * @param definition - The collection definition containing index metadata.\n * @param filter - The query filter to check for unindexed fields.\n *\n * @example\n * ```ts\n * import { collection, checkUnindexedFields } from '@zodmon/core'\n *\n * const Users = collection('users', {\n * email: z.string().unique(),\n * name: z.string(),\n * }, { warnUnindexedQueries: true })\n *\n * // No warning — email is indexed\n * checkUnindexedFields(Users, { email: 'ada@example.com' })\n *\n * // Warns: \"[zodmon] warn: query on 'users' uses unindexed field 'name'\"\n * checkUnindexedFields(Users, { name: 'Ada' })\n * ```\n */\nexport function checkUnindexedFields(\n\tdefinition: WarnableDefinition,\n\tfilter: Record<string, unknown>,\n): void {\n\tif (definition.options.warnUnindexedQueries !== true) return\n\n\tconst covered = new Set<string>()\n\n\tfor (const fi of definition.fieldIndexes) {\n\t\tcovered.add(fi.field)\n\t}\n\n\tfor (const ci of definition.compoundIndexes) {\n\t\tconst firstField = Object.keys(ci.fields)[0]\n\t\tif (firstField !== undefined) {\n\t\t\tcovered.add(firstField)\n\t\t}\n\t}\n\n\tfor (const key of Object.keys(filter)) {\n\t\tif (key === '_id') continue\n\t\tif (SKIP_OPERATORS.has(key)) continue\n\t\tif (!covered.has(key)) {\n\t\t\tconsole.warn(`[zodmon] warn: query on '${definition.name}' uses unindexed field '${key}'`)\n\t\t}\n\t}\n}\n","import type { Collection, FindCursor, Sort } from 'mongodb'\nimport { z } from 'zod'\nimport type { AnyCollection, InferDocument, ValidationMode } from '../collection/types'\nimport {\n\tbuildCursorFilter,\n\ttype CursorPage,\n\ttype CursorPaginateOptions,\n\tdecodeCursor,\n\tencodeCursor,\n\ttype OffsetPage,\n\ttype OffsetPaginateOptions,\n\tresolveSortKeys,\n\ttype SortEntry,\n} from '../crud/paginate'\nimport { ZodmonValidationError } from '../errors/validation'\n\n/**\n * Type-safe sort specification for a document type.\n *\n * Constrains sort keys to top-level fields of `T` with direction `1` (ascending)\n * or `-1` (descending). Dot-path sorts deferred to v1.0.\n *\n * @example\n * ```ts\n * const sort: TypedSort<User> = { name: 1, createdAt: -1 }\n * ```\n */\nexport type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>\n\n/**\n * Type-safe cursor wrapping MongoDB's `FindCursor`.\n *\n * Provides chainable query modifiers (`sort`, `skip`, `limit`, `hint`) that return\n * `this` for fluent chaining, and terminal methods (`toArray`,\n * `[Symbol.asyncIterator]`) that validate each document against the\n * collection's Zod schema before returning.\n *\n * Created by {@link find} — do not construct directly.\n *\n * @typeParam TDef - The collection definition type, used to infer the document type.\n * @typeParam TIndexNames - Union of declared index names accepted by `.hint()`.\n *\n * @example\n * ```ts\n * const docs = await find(users, { role: 'admin' })\n * .sort({ name: 1 })\n * .limit(10)\n * .toArray()\n * ```\n */\nexport class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends string = string> {\n\t/** @internal */\n\tprivate cursor: FindCursor<InferDocument<TDef>>\n\t/** @internal */\n\tprivate schema: z.ZodType\n\t/** @internal */\n\tprivate collectionName: string\n\t/** @internal */\n\tprivate mode: ValidationMode | false\n\t/** @internal */\n\tprivate readonly nativeCollection: Collection<InferDocument<TDef>>\n\t/** @internal */\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter is not assignable to MongoDB's Filter; stored opaquely for paginate\n\tprivate readonly filter: any\n\t/** @internal */\n\tprivate sortSpec: TypedSort<InferDocument<TDef>> | null\n\n\t/** @internal */\n\tconstructor(\n\t\tcursor: FindCursor<InferDocument<TDef>>,\n\t\tdefinition: TDef,\n\t\tmode: ValidationMode | false,\n\t\tnativeCollection: Collection<InferDocument<TDef>>,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter is not assignable to MongoDB's Filter; stored opaquely for paginate\n\t\tfilter: any,\n\t) {\n\t\tthis.cursor = cursor\n\t\tthis.schema = definition.schema\n\t\tthis.collectionName = definition.name\n\t\tthis.mode = mode\n\t\tthis.nativeCollection = nativeCollection\n\t\tthis.filter = filter\n\t\tthis.sortSpec = null\n\t}\n\n\t/**\n\t * Set the sort order for the query.\n\t *\n\t * Only top-level document fields are accepted as sort keys.\n\t * Values must be `1` (ascending) or `-1` (descending).\n\t *\n\t * @param spec - Sort specification mapping field names to sort direction.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).sort({ name: 1, age: -1 }).toArray()\n\t * ```\n\t */\n\tsort(spec: TypedSort<InferDocument<TDef>>): this {\n\t\tthis.sortSpec = spec\n\t\t// Safe cast: TypedSort is a strict subset of MongoDB's Sort type,\n\t\t// but Record<keyof T & string, 1 | -1> is not assignable to Sort\n\t\t// due to index signature differences.\n\t\tthis.cursor.sort(spec as Sort)\n\t\treturn this\n\t}\n\n\t/**\n\t * Skip the first `n` documents in the result set.\n\t *\n\t * @param n - Number of documents to skip.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).skip(10).limit(10).toArray() // page 2\n\t * ```\n\t */\n\tskip(n: number): this {\n\t\tthis.cursor.skip(n)\n\t\treturn this\n\t}\n\n\t/**\n\t * Limit the number of documents returned.\n\t *\n\t * @param n - Maximum number of documents to return.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * find(users, {}).limit(10).toArray() // at most 10 docs\n\t * ```\n\t */\n\tlimit(n: number): this {\n\t\tthis.cursor.limit(n)\n\t\treturn this\n\t}\n\n\t/**\n\t * Force the query optimizer to use the specified index.\n\t *\n\t * Only accepts index names that were declared via `.name()` in the\n\t * collection definition. If no named indexes exist, any string is accepted.\n\t *\n\t * @param indexName - The name of a declared compound index.\n\t * @returns `this` for chaining.\n\t *\n\t * @example\n\t * ```ts\n\t * const Users = collection('users', { email: z.string(), role: z.string() }, {\n\t * indexes: [index({ email: 1, role: -1 }).name('email_role_idx')],\n\t * })\n\t * const admins = await users.find({ role: 'admin' })\n\t * .hint('email_role_idx')\n\t * .toArray()\n\t * ```\n\t */\n\thint(indexName: TIndexNames): this {\n\t\tthis.cursor.hint(indexName)\n\t\treturn this\n\t}\n\n\t/**\n\t * Execute the query with offset-based pagination, returning a page of documents\n\t * with total count and navigation metadata.\n\t *\n\t * Runs `countDocuments` and `find` in parallel for performance. Ignores any\n\t * `.skip()` or `.limit()` already set on the cursor — issues a fresh query.\n\t *\n\t * @param opts - Offset pagination options: `page` (1-indexed) and `perPage`.\n\t * @returns A page with `docs`, `total`, `totalPages`, `hasNext`, `hasPrev`.\n\t * @throws {ZodmonValidationError} When a document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * const page = await users.find({ role: 'admin' })\n\t * .sort({ createdAt: -1 })\n\t * .paginate({ page: 2, perPage: 10 })\n\t * console.log(page.total, page.totalPages, page.hasNext)\n\t * ```\n\t */\n\tpaginate(opts: OffsetPaginateOptions): Promise<OffsetPage<InferDocument<TDef>>>\n\t/**\n\t * Execute the query with cursor-based pagination, returning a page of documents\n\t * with opaque cursors for forward/backward navigation.\n\t *\n\t * Uses the `limit + 1` trick to determine `hasNext`/`hasPrev` without extra queries.\n\t * Direction is encoded in the cursor — pass `endCursor` to go forward, `startCursor`\n\t * to go backward.\n\t *\n\t * @param opts - Cursor pagination options: `limit` and optional `cursor`.\n\t * @returns A page with `docs`, `hasNext`, `hasPrev`, `startCursor`, `endCursor`.\n\t * @throws {ZodmonValidationError} When a document fails schema validation.\n\t * @throws {Error} When the cursor string is malformed.\n\t *\n\t * @example\n\t * ```ts\n\t * const first = await users.find({}).sort({ name: 1 }).paginate({ limit: 10 })\n\t * const next = await users.find({}).sort({ name: 1 })\n\t * .paginate({ cursor: first.endCursor, limit: 10 })\n\t * ```\n\t */\n\tpaginate(opts: CursorPaginateOptions): Promise<CursorPage<InferDocument<TDef>>>\n\tasync paginate(\n\t\topts: OffsetPaginateOptions | CursorPaginateOptions,\n\t): Promise<OffsetPage<InferDocument<TDef>> | CursorPage<InferDocument<TDef>>> {\n\t\tconst sortRecord = this.sortSpec ? (this.sortSpec as Record<string, 1 | -1>) : null\n\t\tconst sortKeys = resolveSortKeys(sortRecord)\n\t\tconst sort = Object.fromEntries(sortKeys) as Sort\n\n\t\tif ('page' in opts) {\n\t\t\treturn await this.offsetPaginate(sortKeys, sort, opts)\n\t\t}\n\t\treturn await this.cursorPaginate(sortKeys, sort, opts)\n\t}\n\n\t/** @internal Offset pagination implementation. */\n\tprivate async offsetPaginate(\n\t\t_sortKeys: SortEntry[],\n\t\tsort: Sort,\n\t\topts: OffsetPaginateOptions,\n\t): Promise<OffsetPage<InferDocument<TDef>>> {\n\t\tconst [total, raw] = await Promise.all([\n\t\t\tthis.nativeCollection.countDocuments(this.filter),\n\t\t\tthis.nativeCollection\n\t\t\t\t.find(this.filter)\n\t\t\t\t.sort(sort)\n\t\t\t\t.skip((opts.page - 1) * opts.perPage)\n\t\t\t\t.limit(opts.perPage)\n\t\t\t\t.toArray(),\n\t\t])\n\n\t\t// Safe cast: our schemas always include _id, so WithId<T> is structurally\n\t\t// identical to InferDocument<TDef>. TypeScript can't prove this under\n\t\t// exactOptionalPropertyTypes.\n\t\tconst docs = (raw as unknown as InferDocument<TDef>[]).map((doc) => this.validateDoc(doc))\n\t\tconst totalPages = Math.ceil(total / opts.perPage)\n\n\t\treturn {\n\t\t\tdocs,\n\t\t\ttotal,\n\t\t\tpage: opts.page,\n\t\t\tperPage: opts.perPage,\n\t\t\ttotalPages,\n\t\t\thasNext: opts.page < totalPages,\n\t\t\thasPrev: opts.page > 1,\n\t\t}\n\t}\n\n\t/** @internal Cursor pagination implementation. */\n\tprivate async cursorPaginate(\n\t\tsortKeys: SortEntry[],\n\t\tsort: Sort,\n\t\topts: CursorPaginateOptions,\n\t): Promise<CursorPage<InferDocument<TDef>>> {\n\t\tlet isBackward = false\n\t\tlet combinedFilter = this.filter\n\n\t\tif (opts.cursor) {\n\t\t\tconst decoded = decodeCursor(opts.cursor)\n\t\t\tisBackward = decoded.direction === 'b'\n\t\t\tconst cursorFilter = buildCursorFilter(sortKeys, decoded.values, isBackward)\n\t\t\tcombinedFilter =\n\t\t\t\tthis.filter && Object.keys(this.filter).length > 0\n\t\t\t\t\t? { $and: [this.filter, cursorFilter] }\n\t\t\t\t\t: cursorFilter\n\t\t}\n\n\t\t// For backward pagination, reverse all sort directions\n\t\tconst effectiveSort = isBackward\n\t\t\t? (Object.fromEntries(sortKeys.map(([f, d]) => [f, d === 1 ? -1 : 1])) as Sort)\n\t\t\t: sort\n\n\t\tconst raw = await this.nativeCollection\n\t\t\t.find(combinedFilter)\n\t\t\t.sort(effectiveSort)\n\t\t\t.limit(opts.limit + 1)\n\t\t\t.toArray()\n\n\t\tconst hasMore = raw.length > opts.limit\n\t\tif (hasMore) raw.pop()\n\n\t\t// Reverse back to original sort order for backward pagination\n\t\tif (isBackward) raw.reverse()\n\n\t\t// Safe cast: same WithId<T> → InferDocument<TDef> cast as offsetPaginate\n\t\tconst docs = (raw as unknown as InferDocument<TDef>[]).map((doc) => this.validateDoc(doc))\n\n\t\treturn {\n\t\t\tdocs,\n\t\t\thasNext: isBackward ? true : hasMore,\n\t\t\thasPrev: isBackward ? hasMore : opts.cursor != null,\n\t\t\tstartCursor:\n\t\t\t\tdocs.length > 0 ? encodeCursor(docs[0] as Record<string, unknown>, sortKeys, 'b') : null,\n\t\t\tendCursor:\n\t\t\t\tdocs.length > 0\n\t\t\t\t\t? encodeCursor(docs[docs.length - 1] as Record<string, unknown>, sortKeys, 'f')\n\t\t\t\t\t: null,\n\t\t}\n\t}\n\n\t/**\n\t * Execute the query and return all matching documents as an array.\n\t *\n\t * Each document is validated against the collection's Zod schema\n\t * according to the resolved validation mode.\n\t *\n\t * @returns Array of validated documents.\n\t * @throws {ZodmonValidationError} When a document fails schema validation in strict/strip mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const admins = await find(users, { role: 'admin' }).toArray()\n\t * ```\n\t */\n\tasync toArray(): Promise<InferDocument<TDef>[]> {\n\t\tconst raw = await this.cursor.toArray()\n\t\treturn raw.map((doc) => this.validateDoc(doc))\n\t}\n\n\t/**\n\t * Async iterator for streaming documents one at a time.\n\t *\n\t * Each yielded document is validated against the collection's Zod schema.\n\t * Memory-efficient for large result sets.\n\t *\n\t * @yields Validated documents one at a time.\n\t * @throws {ZodmonValidationError} When a document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * for await (const user of find(users, {})) {\n\t * console.log(user.name)\n\t * }\n\t * ```\n\t */\n\tasync *[Symbol.asyncIterator](): AsyncGenerator<InferDocument<TDef>> {\n\t\tfor await (const doc of this.cursor) {\n\t\t\tyield this.validateDoc(doc)\n\t\t}\n\t}\n\n\t/** @internal Validate a single raw document against the schema. */\n\tprivate validateDoc(raw: InferDocument<TDef>): InferDocument<TDef> {\n\t\tif (this.mode === false || this.mode === 'passthrough') {\n\t\t\treturn raw\n\t\t}\n\n\t\ttry {\n\t\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\t\treturn this.schema.parse(raw) as InferDocument<TDef>\n\t\t} catch (err) {\n\t\t\tif (err instanceof z.ZodError) {\n\t\t\t\tthrow new ZodmonValidationError(this.collectionName, err)\n\t\t\t}\n\t\t\tthrow err\n\t\t}\n\t}\n}\n","import { ObjectId } from 'mongodb'\n\n// ── Public types ────────────────────────────────────────────────────────\n\n/**\n * Options for offset-based pagination.\n *\n * @example\n * ```ts\n * await users.find({}).sort({ name: 1 }).paginate({ page: 2, perPage: 10 })\n * ```\n */\nexport type OffsetPaginateOptions = {\n\t/** The page number to retrieve (1-indexed). */\n\tpage: number\n\t/** The number of documents per page. */\n\tperPage: number\n}\n\n/**\n * Options for cursor-based pagination.\n *\n * @example\n * ```ts\n * const first = await users.find({}).sort({ name: 1 }).paginate({ limit: 10 })\n * const next = await users.find({}).sort({ name: 1 }).paginate({ cursor: first.endCursor, limit: 10 })\n * ```\n */\nexport type CursorPaginateOptions = {\n\t/** Maximum number of documents to return. */\n\tlimit: number\n\t/** Opaque cursor string from a previous `startCursor` or `endCursor`. */\n\tcursor?: string | null\n}\n\n/**\n * Result of offset-based pagination.\n *\n * @example\n * ```ts\n * const page = await users.find({}).paginate({ page: 1, perPage: 10 })\n * console.log(page.total, page.totalPages, page.hasNext)\n * ```\n */\nexport type OffsetPage<TDoc> = {\n\t/** The documents for this page. */\n\tdocs: TDoc[]\n\t/** Total number of matching documents. */\n\ttotal: number\n\t/** Current page number (1-indexed). */\n\tpage: number\n\t/** Number of documents per page. */\n\tperPage: number\n\t/** Total number of pages (`Math.ceil(total / perPage)`). */\n\ttotalPages: number\n\t/** Whether there is a next page. */\n\thasNext: boolean\n\t/** Whether there is a previous page. */\n\thasPrev: boolean\n}\n\n/**\n * Result of cursor-based pagination.\n *\n * @example\n * ```ts\n * const page = await users.find({}).paginate({ limit: 10 })\n * if (page.hasNext) {\n * const next = await users.find({}).paginate({ cursor: page.endCursor, limit: 10 })\n * }\n * ```\n */\nexport type CursorPage<TDoc> = {\n\t/** The documents for this page. */\n\tdocs: TDoc[]\n\t/** Whether there are more documents after this page. */\n\thasNext: boolean\n\t/** Whether there are documents before this page. */\n\thasPrev: boolean\n\t/** Cursor for the first document (pass to `cursor` to go backward). `null` when empty. */\n\tstartCursor: string | null\n\t/** Cursor for the last document (pass to `cursor` to go forward). `null` when empty. */\n\tendCursor: string | null\n}\n\n// ── Sort key type ───────────────────────────────────────────────────────\n\n/** A [field, direction] tuple used internally for sort resolution. */\nexport type SortEntry = [field: string, direction: 1 | -1]\n\n// ── Cursor value serialization ──────────────────────────────────────────\n\n/**\n * Serialize a value for cursor encoding, preserving ObjectId and Date types.\n *\n * @example\n * ```ts\n * serializeValue(new ObjectId('...')) // { $oid: '...' }\n * serializeValue(new Date('2024-01-01')) // { $date: 1704067200000 }\n * serializeValue('hello') // 'hello'\n * ```\n */\nexport function serializeValue(value: unknown): unknown {\n\tif (value instanceof ObjectId) return { $oid: value.toHexString() }\n\tif (value instanceof Date) return { $date: value.getTime() }\n\treturn value\n}\n\n/**\n * Deserialize a cursor value, restoring ObjectId and Date types.\n *\n * @example\n * ```ts\n * deserializeValue({ $oid: '507f1f...' }) // ObjectId('507f1f...')\n * deserializeValue({ $date: 1704067200000 }) // Date('2024-01-01')\n * deserializeValue('hello') // 'hello'\n * ```\n */\nexport function deserializeValue(value: unknown): unknown {\n\tif (value != null && typeof value === 'object') {\n\t\tif ('$oid' in value) return new ObjectId((value as { $oid: string }).$oid)\n\t\tif ('$date' in value) return new Date((value as { $date: number }).$date)\n\t}\n\treturn value\n}\n\n// ── Cursor encoding/decoding ────────────────────────────────────────────\n\n/**\n * Encode a cursor from a document's sort field values.\n *\n * Format: base64(JSON([ direction, ...serializedValues ]))\n * Direction: 'f' (forward / endCursor) or 'b' (backward / startCursor).\n *\n * @example\n * ```ts\n * encodeCursor({ age: 25, _id: oid }, [['age', 1], ['_id', 1]], 'f')\n * // base64-encoded string\n * ```\n */\nexport function encodeCursor(\n\tdoc: Record<string, unknown>,\n\tsortKeys: SortEntry[],\n\tdirection: 'f' | 'b',\n): string {\n\tconst values = sortKeys.map(([field]) => serializeValue(doc[field]))\n\treturn btoa(JSON.stringify([direction, ...values]))\n}\n\n/**\n * Decode a cursor string into direction and sort field values.\n *\n * @throws {Error} When the cursor is malformed or has invalid format.\n *\n * @example\n * ```ts\n * const { direction, values } = decodeCursor(cursorString)\n * // direction: 'f' | 'b', values: unknown[]\n * ```\n */\nexport function decodeCursor(cursor: string): { direction: 'f' | 'b'; values: unknown[] } {\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(atob(cursor))\n\t} catch {\n\t\tthrow new Error('Invalid cursor: malformed encoding')\n\t}\n\tif (!Array.isArray(parsed) || parsed.length < 1) {\n\t\tthrow new Error('Invalid cursor: expected non-empty array')\n\t}\n\tconst [direction, ...rawValues] = parsed as [unknown, ...unknown[]]\n\tif (direction !== 'f' && direction !== 'b') {\n\t\tthrow new Error('Invalid cursor: unknown direction flag')\n\t}\n\treturn { direction, values: rawValues.map(deserializeValue) }\n}\n\n// ── Cursor filter builder ───────────────────────────────────────────────\n\n/**\n * Build a MongoDB `$or` filter for cursor-based pagination.\n *\n * For sort `{ role: 1, createdAt: -1, _id: 1 }` with values `['admin', date, id]`:\n * ```\n * { $or: [\n * { role: { $gt: 'admin' } },\n * { role: 'admin', createdAt: { $lt: date } },\n * { role: 'admin', createdAt: date, _id: { $gt: id } },\n * ]}\n * ```\n *\n * @example\n * ```ts\n * buildCursorFilter([['age', 1], ['_id', 1]], [25, oid], false)\n * // { $or: [{ age: { $gt: 25 } }, { age: 25, _id: { $gt: oid } }] }\n * ```\n */\nexport function buildCursorFilter(\n\tsortKeys: SortEntry[],\n\tvalues: unknown[],\n\tisBackward: boolean,\n): Record<string, unknown> {\n\tconst clauses: Record<string, unknown>[] = []\n\n\tfor (let i = 0; i < sortKeys.length; i++) {\n\t\tconst clause: Record<string, unknown> = {}\n\n\t\t// Equality prefix for all preceding fields\n\t\tfor (let j = 0; j < i; j++) {\n\t\t\t// biome-ignore lint/style/noNonNullAssertion: index is bounded by outer loop\n\t\t\tclause[sortKeys[j]![0]] = values[j]\n\t\t}\n\n\t\t// Comparison operator for current field\n\t\t// Forward + asc = $gt, Forward + desc = $lt\n\t\t// Backward reverses: Backward + asc = $lt, Backward + desc = $gt\n\t\t// biome-ignore lint/style/noNonNullAssertion: index is bounded by loop\n\t\tconst [field, direction] = sortKeys[i]!\n\t\tconst isAsc = direction === 1\n\t\tconst op = isAsc !== isBackward ? '$gt' : '$lt'\n\n\t\t// MongoDB's $gt/$lt with null doesn't work for cursor pagination:\n\t\t// $gt: null only matches BSON types above null (regex, etc.), not\n\t\t// strings/numbers. $lt: null matches nothing. In both directions,\n\t\t// \"past null\" means \"not null\", so use $ne: null.\n\t\tif (values[i] === null) {\n\t\t\tclause[field] = { $ne: null }\n\t\t} else {\n\t\t\tclause[field] = { [op]: values[i] }\n\t\t}\n\n\t\tclauses.push(clause)\n\t}\n\n\treturn { $or: clauses }\n}\n\n// ── Sort resolution ─────────────────────────────────────────────────────\n\n/**\n * Resolve sort keys, always appending `_id` as tiebreaker if not present.\n * Defaults to `[['_id', 1]]` when no sort is specified.\n *\n * @example\n * ```ts\n * resolveSortKeys({ age: 1 }) // [['age', 1], ['_id', 1]]\n * resolveSortKeys(null) // [['_id', 1]]\n * resolveSortKeys({ _id: -1 }) // [['_id', -1]]\n * ```\n */\nexport function resolveSortKeys(sortSpec: Record<string, 1 | -1> | null): SortEntry[] {\n\tconst entries: SortEntry[] = sortSpec ? (Object.entries(sortSpec) as SortEntry[]) : []\n\n\tif (!entries.some(([field]) => field === '_id')) {\n\t\tentries.push(['_id', 1])\n\t}\n\n\treturn entries\n}\n","import { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, InferDocument, InferInsert } from '../collection/types'\nimport { ZodmonValidationError } from '../errors/validation'\n\n/**\n * Insert a single document into the collection.\n *\n * Validates the input against the collection's Zod schema before writing.\n * Schema defaults (including auto-generated `_id`) are applied during\n * validation. Returns the full document with all defaults filled in.\n *\n * @param handle - The collection handle to insert into.\n * @param doc - The document to insert. Fields with `.default()` are optional.\n * @returns The inserted document with `_id` and all defaults applied.\n * @throws {ZodmonValidationError} When the document fails schema validation.\n *\n * @example\n * ```ts\n * const user = await insertOne(users, { name: 'Ada' })\n * console.log(user._id) // ObjectId (auto-generated)\n * console.log(user.role) // 'user' (schema default)\n * ```\n */\nexport async function insertOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tdoc: InferInsert<TDef>,\n): Promise<InferDocument<TDef>> {\n\tlet parsed: InferDocument<TDef>\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\tparsed = handle.definition.schema.parse(doc) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n\t// Safe cast: parsed is a full document with _id, matching the collection's\n\t// document type. MongoDB driver's OptionalUnlessRequiredId makes _id optional\n\t// for generic types, but we always have _id after parse.\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB driver's insertOne parameter type uses OptionalUnlessRequiredId which is not directly assignable from our generic InferDocument\n\tawait handle.native.insertOne(parsed as any)\n\treturn parsed\n}\n\n/**\n * Insert multiple documents into the collection.\n *\n * Validates every document against the collection's Zod schema before\n * writing any to MongoDB. If any document fails validation, none are\n * inserted (fail-fast before the driver call).\n *\n * @param handle - The collection handle to insert into.\n * @param docs - The documents to insert.\n * @returns The inserted documents with `_id` and all defaults applied.\n * @throws {ZodmonValidationError} When any document fails schema validation.\n *\n * @example\n * ```ts\n * const users = await insertMany(handle, [\n * { name: 'Ada' },\n * { name: 'Bob', role: 'admin' },\n * ])\n * ```\n */\nexport async function insertMany<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tdocs: InferInsert<TDef>[],\n): Promise<InferDocument<TDef>[]> {\n\tif (docs.length === 0) return []\n\tconst parsed: InferDocument<TDef>[] = []\n\tfor (const doc of docs) {\n\t\ttry {\n\t\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\t\tparsed.push(handle.definition.schema.parse(doc) as InferDocument<TDef>)\n\t\t} catch (err) {\n\t\t\tif (err instanceof z.ZodError) {\n\t\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t\t}\n\t\t\tthrow err\n\t\t}\n\t}\n\t// biome-ignore lint/suspicious/noExplicitAny: MongoDB driver's insertMany parameter type is not directly assignable from our generic InferDocument\n\tawait handle.native.insertMany(parsed as any)\n\treturn parsed\n}\n","import type { UpdateResult } from 'mongodb'\nimport { z } from 'zod'\nimport type { CollectionHandle } from '../client/handle'\nimport type { AnyCollection, InferDocument, ValidationMode } from '../collection/types'\nimport { ZodmonValidationError } from '../errors/validation'\nimport type { TypedFilter } from '../query/filter'\nimport type { TypedUpdateFilter } from '../query/update'\n\n/**\n * Options for {@link updateOne} and {@link updateMany}.\n */\nexport type UpdateOptions = {\n\t/** When `true`, inserts a new document if no document matches the filter. */\n\tupsert?: boolean\n}\n\n/**\n * Options for {@link findOneAndUpdate}.\n */\nexport type FindOneAndUpdateOptions = {\n\t/** Whether to return the document before or after the update. Defaults to `'after'`. */\n\treturnDocument?: 'before' | 'after'\n\t/** When `true`, inserts a new document if no document matches the filter. */\n\tupsert?: boolean\n\t/** Override the collection-level validation mode, or `false` to skip validation entirely. */\n\tvalidate?: ValidationMode | false\n}\n\n/**\n * Update a single document matching the filter.\n *\n * Applies the update operators to the first document that matches the filter.\n * Does not validate the update against the Zod schema — validation happens\n * at the field-operator level through {@link TypedUpdateFilter}.\n *\n * @param handle - The collection handle to update in.\n * @param filter - Type-safe filter to match documents.\n * @param update - Type-safe update operators to apply.\n * @param options - Optional settings such as `upsert`.\n * @returns The MongoDB `UpdateResult` with match/modify counts.\n *\n * @example\n * ```ts\n * const result = await updateOne(users, { name: 'Ada' }, { $set: { role: 'admin' } })\n * console.log(result.modifiedCount) // 1\n * ```\n */\nexport async function updateOne<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\toptions?: UpdateOptions,\n): Promise<UpdateResult> {\n\t// Safe cast: TypedFilter and TypedUpdateFilter are strict subsets of\n\t// MongoDB's Filter<T> and UpdateFilter<T>, but the intersection-based\n\t// mapped types cannot be structurally matched by the driver's looser types.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter/TypedUpdateFilter intersection types are not directly assignable to MongoDB's Filter/UpdateFilter\n\treturn await handle.native.updateOne(filter as any, update as any, options)\n}\n\n/**\n * Update all documents matching the filter.\n *\n * Applies the update operators to every document that matches the filter.\n * Does not validate the update against the Zod schema — validation happens\n * at the field-operator level through {@link TypedUpdateFilter}.\n *\n * @param handle - The collection handle to update in.\n * @param filter - Type-safe filter to match documents.\n * @param update - Type-safe update operators to apply.\n * @param options - Optional settings such as `upsert`.\n * @returns The MongoDB `UpdateResult` with match/modify counts.\n *\n * @example\n * ```ts\n * const result = await updateMany(users, { role: 'guest' }, { $set: { role: 'user' } })\n * console.log(result.modifiedCount) // number of guests promoted\n * ```\n */\nexport async function updateMany<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\toptions?: UpdateOptions,\n): Promise<UpdateResult> {\n\t// Safe cast: TypedFilter and TypedUpdateFilter are strict subsets of\n\t// MongoDB's Filter<T> and UpdateFilter<T>, but the intersection-based\n\t// mapped types cannot be structurally matched by the driver's looser types.\n\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter/TypedUpdateFilter intersection types are not directly assignable to MongoDB's Filter/UpdateFilter\n\treturn await handle.native.updateMany(filter as any, update as any, options)\n}\n\n/**\n * Find a single document matching the filter, apply an update, and return the document.\n *\n * By default, returns the document **after** the update is applied. Set\n * `returnDocument: 'before'` to get the pre-update snapshot. The returned\n * document is validated against the collection's Zod schema using the same\n * resolution logic as {@link findOne}.\n *\n * @param handle - The collection handle to update in.\n * @param filter - Type-safe filter to match documents.\n * @param update - Type-safe update operators to apply.\n * @param options - Optional settings: `returnDocument`, `upsert`, `validate`.\n * @returns The matched document (before or after update), or `null` if no document matches.\n * @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.\n *\n * @example\n * ```ts\n * const user = await findOneAndUpdate(\n * users,\n * { name: 'Ada' },\n * { $set: { role: 'admin' } },\n * )\n * if (user) console.log(user.role) // 'admin' (returned after update)\n * ```\n *\n * @example\n * ```ts\n * const before = await findOneAndUpdate(\n * users,\n * { name: 'Ada' },\n * { $inc: { loginCount: 1 } },\n * { returnDocument: 'before' },\n * )\n * ```\n */\nexport async function findOneAndUpdate<TDef extends AnyCollection>(\n\thandle: CollectionHandle<TDef>,\n\tfilter: TypedFilter<InferDocument<TDef>>,\n\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\toptions?: FindOneAndUpdateOptions,\n): Promise<InferDocument<TDef> | null> {\n\tconst driverOptions: Record<string, unknown> = {\n\t\treturnDocument: options?.returnDocument ?? 'after',\n\t\tincludeResultMetadata: false,\n\t}\n\tif (options?.upsert !== undefined) {\n\t\t// biome-ignore lint/complexity/useLiteralKeys: bracket notation required — noUncheckedIndexedAccess forbids dot access on Record<string, unknown>\n\t\tdriverOptions['upsert'] = options.upsert\n\t}\n\t// Safe cast: TypedFilter and TypedUpdateFilter are strict subsets of\n\t// MongoDB's Filter<T> and UpdateFilter<T>, but the intersection-based\n\t// mapped types cannot be structurally matched by the driver's looser types.\n\t// The options object is built dynamically to satisfy exactOptionalPropertyTypes.\n\tconst result = await handle.native.findOneAndUpdate(\n\t\t// biome-ignore lint/suspicious/noExplicitAny: TypedFilter intersection type is not directly assignable to MongoDB's Filter\n\t\tfilter as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: TypedUpdateFilter intersection type is not directly assignable to MongoDB's UpdateFilter\n\t\tupdate as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: dynamic options object is not assignable to driver's FindOneAndUpdateOptions under exactOptionalPropertyTypes\n\t\tdriverOptions as any,\n\t)\n\tif (!result) return null\n\n\tconst mode =\n\t\toptions?.validate !== undefined ? options.validate : handle.definition.options.validation\n\n\tif (mode === false || mode === 'passthrough') {\n\t\t// Safe cast: raw document from MongoDB matches the collection's document\n\t\t// shape at runtime. Skipping validation per user request.\n\t\treturn result as InferDocument<TDef>\n\t}\n\n\ttry {\n\t\t// Safe cast: schema.parse() returns z.infer<schema> which equals\n\t\t// InferDocument<TDef>, but TypeScript cannot prove this for\n\t\t// generic TDef. The runtime type is guaranteed correct by Zod.\n\t\treturn handle.definition.schema.parse(result) as InferDocument<TDef>\n\t} catch (err) {\n\t\tif (err instanceof z.ZodError) {\n\t\t\tthrow new ZodmonValidationError(handle.definition.name, err)\n\t\t}\n\t\tthrow err\n\t}\n}\n","import type { Collection, DeleteResult, UpdateResult } from 'mongodb'\nimport type { AnyCollection, IndexNames, InferDocument, InferInsert } from '../collection/types'\nimport type { FindOneAndDeleteOptions } from '../crud/delete'\nimport {\n\tdeleteMany as _deleteMany,\n\tdeleteOne as _deleteOne,\n\tfindOneAndDelete as _findOneAndDelete,\n} from '../crud/delete'\nimport type { FindOneOptions, FindOptions } from '../crud/find'\nimport { find as _find, findOne as _findOne, findOneOrThrow as _findOneOrThrow } from '../crud/find'\nimport { insertMany as _insertMany, insertOne as _insertOne } from '../crud/insert'\nimport type { FindOneAndUpdateOptions, UpdateOptions } from '../crud/update'\nimport {\n\tfindOneAndUpdate as _findOneAndUpdate,\n\tupdateMany as _updateMany,\n\tupdateOne as _updateOne,\n} from '../crud/update'\nimport { syncIndexes as _syncIndexes } from '../indexes/sync'\nimport type { SyncIndexesOptions, SyncIndexesResult } from '../indexes/types'\nimport type { TypedFindCursor } from '../query/cursor'\nimport type { TypedFilter } from '../query/filter'\nimport type { TypedUpdateFilter } from '../query/update'\n\n/**\n * Typed wrapper around a MongoDB driver `Collection`.\n *\n * Created by {@link Database.use}. Holds the original `CollectionDefinition`\n * (for runtime schema validation and index metadata) alongside the native\n * driver collection parameterized with the inferred document type.\n *\n * @typeParam TDef - The collection definition type. Used to derive both\n * the document type (`InferDocument`) and the insert type (`InferInsert`).\n */\nexport class CollectionHandle<TDef extends AnyCollection = AnyCollection> {\n\t/** The collection definition containing schema, name, and index metadata. */\n\treadonly definition: TDef\n\n\t/** The underlying MongoDB driver collection, typed to the inferred document type. */\n\treadonly native: Collection<InferDocument<TDef>>\n\n\tconstructor(definition: TDef, native: Collection<InferDocument<TDef>>) {\n\t\tthis.definition = definition\n\t\tthis.native = native\n\t}\n\n\t/**\n\t * Insert a single document into the collection.\n\t *\n\t * Validates the input against the collection's Zod schema before writing.\n\t * Schema defaults (including auto-generated `_id`) are applied during\n\t * validation. Returns the full document with all defaults filled in.\n\t *\n\t * @param doc - The document to insert. Fields with `.default()` are optional.\n\t * @returns The inserted document with `_id` and all defaults applied.\n\t * @throws {ZodmonValidationError} When the document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.insertOne({ name: 'Ada' })\n\t * console.log(user._id) // ObjectId (auto-generated)\n\t * console.log(user.role) // 'user' (schema default)\n\t * ```\n\t */\n\tasync insertOne(doc: InferInsert<TDef>): Promise<InferDocument<TDef>> {\n\t\treturn await _insertOne(this, doc)\n\t}\n\n\t/**\n\t * Insert multiple documents into the collection.\n\t *\n\t * Validates every document against the collection's Zod schema before\n\t * writing any to MongoDB. If any document fails validation, none are\n\t * inserted (fail-fast before the driver call).\n\t *\n\t * @param docs - The documents to insert.\n\t * @returns The inserted documents with `_id` and all defaults applied.\n\t * @throws {ZodmonValidationError} When any document fails schema validation.\n\t *\n\t * @example\n\t * ```ts\n\t * const created = await users.insertMany([\n\t * { name: 'Ada' },\n\t * { name: 'Bob', role: 'admin' },\n\t * ])\n\t * ```\n\t */\n\tasync insertMany(docs: InferInsert<TDef>[]): Promise<InferDocument<TDef>[]> {\n\t\treturn await _insertMany(this, docs)\n\t}\n\n\t/**\n\t * Find a single document matching the filter.\n\t *\n\t * Queries MongoDB, then validates the fetched document against the collection's\n\t * Zod schema. Validation mode is resolved from the per-query option, falling\n\t * back to the collection-level default (which defaults to `'strict'`).\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional projection and validation overrides.\n\t * @returns The matched document, or `null` if no document matches.\n\t * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOne({ name: 'Ada' })\n\t * if (user) console.log(user.role)\n\t * ```\n\t */\n\tasync findOne(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneOptions,\n\t): Promise<InferDocument<TDef> | null> {\n\t\treturn await _findOne(this, filter, options)\n\t}\n\n\t/**\n\t * Find a single document matching the filter, or throw if none exists.\n\t *\n\t * Behaves identically to {@link findOne} but throws {@link ZodmonNotFoundError}\n\t * instead of returning `null` when no document matches the filter.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional projection and validation overrides.\n\t * @returns The matched document (never null).\n\t * @throws {ZodmonNotFoundError} When no document matches the filter.\n\t * @throws {ZodmonValidationError} When the fetched document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOneOrThrow({ name: 'Ada' })\n\t * console.log(user.role) // guaranteed non-null\n\t * ```\n\t */\n\tasync findOneOrThrow(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneOptions,\n\t): Promise<InferDocument<TDef>> {\n\t\treturn await _findOneOrThrow(this, filter, options)\n\t}\n\n\t/**\n\t * Find all documents matching the filter, returning a chainable typed cursor.\n\t *\n\t * The cursor is lazy — no query is executed until a terminal method\n\t * (`toArray`, `for await`) is called. Use `sort`, `skip`, and `limit`\n\t * to shape the query before executing.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional validation overrides.\n\t * @returns A typed cursor for chaining query modifiers.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const admins = await users.find({ role: 'admin' })\n\t * .sort({ name: 1 })\n\t * .limit(10)\n\t * .toArray()\n\t * ```\n\t */\n\tfind(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOptions,\n\t): TypedFindCursor<TDef, IndexNames<TDef>> {\n\t\treturn _find(this, filter, options)\n\t}\n\n\t/**\n\t * Update a single document matching the filter.\n\t *\n\t * Applies the update operators to the first document that matches the filter.\n\t * Does not validate the update against the Zod schema — validation happens\n\t * at the field-operator level through {@link TypedUpdateFilter}.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param update - Type-safe update operators to apply.\n\t * @param options - Optional settings such as `upsert`.\n\t * @returns The MongoDB `UpdateResult` with match/modify counts.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const result = await users.updateOne({ name: 'Ada' }, { $set: { role: 'admin' } })\n\t * console.log(result.modifiedCount) // 1\n\t * ```\n\t */\n\tasync updateOne(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\t\toptions?: UpdateOptions,\n\t): Promise<UpdateResult> {\n\t\treturn await _updateOne(this, filter, update, options)\n\t}\n\n\t/**\n\t * Update all documents matching the filter.\n\t *\n\t * Applies the update operators to every document that matches the filter.\n\t * Does not validate the update against the Zod schema — validation happens\n\t * at the field-operator level through {@link TypedUpdateFilter}.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param update - Type-safe update operators to apply.\n\t * @param options - Optional settings such as `upsert`.\n\t * @returns The MongoDB `UpdateResult` with match/modify counts.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const result = await users.updateMany({ role: 'guest' }, { $set: { role: 'user' } })\n\t * console.log(result.modifiedCount) // number of guests promoted\n\t * ```\n\t */\n\tasync updateMany(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\t\toptions?: UpdateOptions,\n\t): Promise<UpdateResult> {\n\t\treturn await _updateMany(this, filter, update, options)\n\t}\n\n\t/**\n\t * Find a single document matching the filter, apply an update, and return the document.\n\t *\n\t * By default, returns the document **after** the update is applied. Set\n\t * `returnDocument: 'before'` to get the pre-update snapshot. The returned\n\t * document is validated against the collection's Zod schema using the same\n\t * resolution logic as {@link findOne}.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param update - Type-safe update operators to apply.\n\t * @param options - Optional settings: `returnDocument`, `upsert`, `validate`.\n\t * @returns The matched document (before or after update), or `null` if no document matches.\n\t * @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOneAndUpdate(\n\t * { name: 'Ada' },\n\t * { $set: { role: 'admin' } },\n\t * )\n\t * if (user) console.log(user.role) // 'admin' (returned after update)\n\t * ```\n\t */\n\tasync findOneAndUpdate(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\tupdate: TypedUpdateFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneAndUpdateOptions,\n\t): Promise<InferDocument<TDef> | null> {\n\t\treturn await _findOneAndUpdate(this, filter, update, options)\n\t}\n\n\t/**\n\t * Delete a single document matching the filter.\n\t *\n\t * Removes the first document that matches the filter from the collection.\n\t * No validation is performed — the document is deleted directly through\n\t * the MongoDB driver.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @returns The MongoDB `DeleteResult` with the deleted count.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const result = await users.deleteOne({ name: 'Ada' })\n\t * console.log(result.deletedCount) // 1\n\t * ```\n\t */\n\tasync deleteOne(filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult> {\n\t\treturn await _deleteOne(this, filter)\n\t}\n\n\t/**\n\t * Delete all documents matching the filter.\n\t *\n\t * Removes every document that matches the filter from the collection.\n\t * No validation is performed — documents are deleted directly through\n\t * the MongoDB driver.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @returns The MongoDB `DeleteResult` with the deleted count.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const result = await users.deleteMany({ role: 'guest' })\n\t * console.log(result.deletedCount) // number of guests removed\n\t * ```\n\t */\n\tasync deleteMany(filter: TypedFilter<InferDocument<TDef>>): Promise<DeleteResult> {\n\t\treturn await _deleteMany(this, filter)\n\t}\n\n\t/**\n\t * Find a single document matching the filter, delete it, and return the document.\n\t *\n\t * Returns the deleted document, or `null` if no document matches the filter.\n\t * The returned document is validated against the collection's Zod schema\n\t * using the same resolution logic as {@link findOne}.\n\t *\n\t * @param filter - Type-safe filter to match documents.\n\t * @param options - Optional settings: `validate`.\n\t * @returns The deleted document, or `null` if no document matches.\n\t * @throws {ZodmonValidationError} When the returned document fails schema validation in strict mode.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const user = await users.findOneAndDelete({ name: 'Ada' })\n\t * if (user) console.log(user.name) // 'Ada' (the deleted document)\n\t * ```\n\t */\n\tasync findOneAndDelete(\n\t\tfilter: TypedFilter<InferDocument<TDef>>,\n\t\toptions?: FindOneAndDeleteOptions,\n\t): Promise<InferDocument<TDef> | null> {\n\t\treturn await _findOneAndDelete(this, filter, options)\n\t}\n\n\t/**\n\t * Synchronize the indexes declared in this collection's schema with MongoDB.\n\t *\n\t * Compares the desired indexes (from field-level `.index()` / `.unique()` /\n\t * `.text()` / `.expireAfter()` and compound `indexes` in collection options)\n\t * with the indexes that currently exist in MongoDB, then creates, drops, or\n\t * reports differences depending on the options.\n\t *\n\t * @param options - Optional sync behavior (dryRun, dropOrphaned).\n\t * @returns A summary of created, dropped, skipped, and stale indexes.\n\t *\n\t * @example\n\t * ```ts\n\t * const users = db.use(Users)\n\t * const result = await users.syncIndexes()\n\t * console.log('Created:', result.created)\n\t * console.log('Stale:', result.stale.map(s => s.name))\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // Dry run to preview changes without modifying the database\n\t * const diff = await users.syncIndexes({ dryRun: true })\n\t * console.log('Would create:', diff.created)\n\t * console.log('Would drop:', diff.dropped)\n\t * ```\n\t */\n\tasync syncIndexes(options?: SyncIndexesOptions): Promise<SyncIndexesResult> {\n\t\treturn await _syncIndexes(this, options)\n\t}\n}\n","import { ObjectId } from 'mongodb'\nimport { z } from 'zod'\nimport { getIndexMetadata } from '../schema/extensions'\nimport { objectId } from '../schema/object-id'\nimport type {\n\tCollectionDefinition,\n\tCollectionOptions,\n\tCompoundIndexDefinition,\n\tFieldIndexDefinition,\n\tResolvedShape,\n} from './types'\n\n/**\n * Walk a Zod shape and extract field-level index metadata from each field.\n *\n * Returns an array of {@link FieldIndexDefinition} for every field that has\n * been marked with `.index()`, `.unique()`, `.text()`, or `.expireAfter()`.\n * Fields without index metadata are silently skipped.\n *\n * @param shape - A Zod shape object (the value passed to `z.object()`).\n * @returns An array of field index definitions with the field name attached.\n */\nexport function extractFieldIndexes(shape: z.core.$ZodShape): FieldIndexDefinition[] {\n\tconst result: FieldIndexDefinition[] = []\n\tfor (const [field, schema] of Object.entries(shape)) {\n\t\tconst meta = getIndexMetadata(schema)\n\t\tif (meta) {\n\t\t\tresult.push({ field, ...meta })\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Define a MongoDB collection with a Zod schema.\n *\n * Creates a {@link CollectionDefinition} that:\n * - Adds `_id: objectId()` if the shape doesn't already include `_id`\n * - Uses the user-provided `_id` schema if one is present (e.g. nanoid, UUID)\n * - Extracts field-level index metadata from the shape\n * - Separates compound indexes from the rest of the options\n * - Returns an immutable definition object\n *\n * @param name - The MongoDB collection name.\n * @param shape - A Zod shape object defining the document fields. May include a custom `_id`.\n * @param options - Optional collection-level configuration including compound indexes.\n * @returns A {@link CollectionDefinition} ready for use with `createClient()`.\n *\n * @example\n * ```ts\n * const Users = collection('users', {\n * email: z.string().unique(),\n * name: z.string().index(),\n * age: z.number().optional(),\n * })\n * ```\n */\nexport function collection<\n\tTShape extends z.core.$ZodShape,\n\tconst TIndexes extends readonly CompoundIndexDefinition<\n\t\tExtract<keyof TShape, string>\n\t>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[],\n>(\n\tname: string,\n\tshape: TShape,\n\toptions?: Omit<CollectionOptions<Extract<keyof TShape, string>>, 'indexes'> & {\n\t\tindexes?: TIndexes\n\t},\n): CollectionDefinition<TShape, [...TIndexes]> {\n\t// TypeScript cannot narrow generic conditional types through control flow,\n\t// so the assertion is needed here. Both branches are provably correct:\n\t// - when _id is in shape: ResolvedShape<TShape> = TShape\n\t// - when _id is absent: ResolvedShape<TShape> = { _id: ZodObjectId } & TShape\n\tconst resolvedShape = (\n\t\t'_id' in shape ? shape : { _id: objectId().default(() => new ObjectId()), ...shape }\n\t) as ResolvedShape<TShape>\n\tconst schema = z.object(resolvedShape)\n\n\tconst fieldIndexes = extractFieldIndexes(shape)\n\n\tconst { indexes: compoundIndexes, validation, ...rest } = options ?? {}\n\n\treturn {\n\t\tname,\n\t\t// Zod v4's z.object() returns ZodObject<{ -readonly [P in keyof T]: T[P] }> which\n\t\t// strips readonly modifiers. With exactOptionalPropertyTypes this mapped type is\n\t\t// not assignable to ZodObject<ResolvedShape<TShape>>. The cast is safe because\n\t\t// the runtime shape is correct — only the readonly modifier differs.\n\t\tschema: schema as CollectionDefinition<TShape, [...TIndexes]>['schema'],\n\t\tshape,\n\t\tfieldIndexes,\n\t\t// Safe cast: compoundIndexes is TIndexes at runtime (or an empty array when\n\t\t// no options provided). The spread into [...TIndexes] preserves the tuple type.\n\t\tcompoundIndexes: (compoundIndexes ?? []) as [...TIndexes],\n\t\toptions: {\n\t\t\tvalidation: validation ?? 'strict',\n\t\t\t...rest,\n\t\t},\n\t}\n}\n","import { z } from 'zod'\nimport { installRefExtension } from './ref'\n\n/**\n * Options controlling how a field-level MongoDB index is created.\n *\n * Passed to the `.index()` Zod extension method. Every property is optional;\n * omitting all of them creates a standard ascending, non-unique index.\n */\nexport type IndexOptions = {\n\t/** When `true`, MongoDB enforces a unique constraint on this field. */\n\tunique?: boolean\n\t/**\n\t * When `true`, the index skips documents where the field is `null` or missing.\n\t * Useful for optional fields that should be indexed only when present.\n\t */\n\tsparse?: boolean\n\t/** When `true`, creates a MongoDB text index for full-text search on this field. */\n\ttext?: boolean\n\t/**\n\t * When `true`, the index is created in descending order (`-1`).\n\t * Defaults to ascending (`1`) when omitted.\n\t */\n\tdescending?: boolean\n\t/**\n\t * TTL in seconds. MongoDB will automatically delete documents once the\n\t * indexed `Date` field is older than this many seconds. Only valid on\n\t * fields whose runtime type is `Date`.\n\t */\n\texpireAfter?: number\n\t/**\n\t * A partial filter expression. Only documents matching this filter are\n\t * included in the index. Maps directly to MongoDB's `partialFilterExpression`.\n\t */\n\tpartial?: Record<string, unknown>\n}\n\n/**\n * Metadata stored in the WeakMap sidecar for every schema that has been\n * marked with `.index()`. Always contains `indexed: true` plus any\n * {@link IndexOptions} the caller provided.\n */\nexport type IndexMetadata = {\n\t/** Always `true` — acts as a discriminator for \"this field is indexed\". */\n\tindexed: true\n} & IndexOptions\n\ndeclare module 'zod' {\n\tinterface ZodType {\n\t\t/**\n\t\t * Mark this field for indexing when `syncIndexes()` is called.\n\t\t *\n\t\t * Stores {@link IndexOptions} in a WeakMap sidecar so the collection\n\t\t * factory can later introspect each field and build the appropriate\n\t\t * MongoDB index specification.\n\t\t *\n\t\t * @param options - Optional index configuration (unique, sparse, etc.).\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const UserSchema = z.object({\n\t\t * email: z.string().index({ unique: true }),\n\t\t * age: z.number().index({ sparse: true }),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tindex(options?: IndexOptions): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ unique: true })`.\n\t\t *\n\t\t * Creates a unique index on this field, causing MongoDB to reject\n\t\t * duplicate values.\n\t\t *\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const UserSchema = z.object({\n\t\t * email: z.string().unique(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tunique(): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ text: true })`.\n\t\t *\n\t\t * Creates a MongoDB text index on this field, enabling `$text` queries\n\t\t * for full-text search.\n\t\t *\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const PostSchema = z.object({\n\t\t * body: z.string().text(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\ttext(): this\n\n\t\t/**\n\t\t * Shorthand for `.index({ expireAfter: seconds })`.\n\t\t *\n\t\t * Creates a TTL (Time-To-Live) index. MongoDB will automatically\n\t\t * remove documents once the indexed `Date` field is older than\n\t\t * the specified number of seconds.\n\t\t *\n\t\t * @param seconds - Number of seconds after which documents expire.\n\t\t * @returns The same schema instance for chaining.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const SessionSchema = z.object({\n\t\t * createdAt: z.date().expireAfter(3600), // 1 hour TTL\n\t\t * })\n\t\t * ```\n\t\t */\n\t\texpireAfter(seconds: number): this\n\t}\n}\n\n/**\n * WeakMap sidecar that associates a Zod schema instance with its\n * {@link IndexMetadata}.\n *\n * A WeakMap is used so that index metadata does not prevent garbage collection\n * of schema instances. The keys are the Zod schema objects themselves, and\n * the values are the corresponding `IndexMetadata` descriptors.\n */\nconst indexMetadata = new WeakMap<object, IndexMetadata>()\n\n/**\n * Retrieve the index metadata attached to a Zod schema, if any.\n *\n * Returns `undefined` when the schema was never marked with an index\n * extension (`.index()`, `.unique()`, `.text()`, or `.expireAfter()`).\n *\n * @param schema - The Zod schema to inspect. Accepts `unknown` for\n * convenience; non-object values safely return `undefined`.\n * @returns The {@link IndexMetadata} for the schema, or `undefined`.\n *\n * @example\n * ```ts\n * const email = z.string().index({ unique: true })\n * const meta = getIndexMetadata(email)\n * // => { indexed: true, unique: true }\n * ```\n */\nexport function getIndexMetadata(schema: unknown): IndexMetadata | undefined {\n\tif (typeof schema !== 'object' || schema === null) return undefined\n\treturn indexMetadata.get(schema)\n}\n\n/**\n * Symbol used as a guard property on `ZodType.prototype` to prevent\n * double-registration of Zodmon extension methods. The symbol is created\n * with `Symbol.for` so it is shared across realms / duplicate module loads.\n */\nconst GUARD = Symbol.for('zodmon_extensions')\n\n/**\n * Monkey-patch Zod's `ZodType.prototype` with Zodmon extension methods\n * (`.index()`, `.unique()`, `.text()`, `.expireAfter()`).\n *\n * In Zod v4, methods are copied from `ZodType.prototype` to each instance\n * during construction via the internal `init` loop (`Object.keys(proto)` ->\n * copy to instance). Extension methods use `enumerable: true` so they are\n * picked up by this loop for every schema created after installation.\n *\n * The function is idempotent: calling it more than once is a safe no-op,\n * guarded by a non-enumerable `Symbol.for('zodmon_extensions')` property\n * on the prototype.\n *\n * This function is called at module level when `extensions.ts` is first\n * imported, so consumers never need to call it manually. It is exported\n * primarily for use in tests.\n *\n * @example\n * ```ts\n * import { installExtensions } from '@zodmon/core/schema/extensions'\n * installExtensions() // safe to call multiple times\n *\n * const indexed = z.string().index({ unique: true })\n * ```\n */\nexport function installExtensions(): void {\n\tconst proto = z.ZodType.prototype\n\tif (GUARD in proto) return\n\n\tObject.defineProperty(proto, 'index', {\n\t\t/**\n\t\t * Declares a MongoDB index on this field. Accepts optional\n\t\t * {@link IndexOptions} to configure uniqueness, sparseness, text search,\n\t\t * sort direction, TTL, or partial filters.\n\t\t *\n\t\t * @param options - Index configuration. Omit for a standard ascending index.\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const name = z.string().index()\n\t\t * const email = z.string().index({ unique: true, sparse: true })\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto, options?: IndexOptions): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, ...options })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'unique', {\n\t\t/**\n\t\t * Shorthand for `.index({ unique: true })`. Marks this field as requiring\n\t\t * a unique index in MongoDB, preventing duplicate values.\n\t\t *\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const email = z.string().unique()\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, unique: true })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'text', {\n\t\t/**\n\t\t * Shorthand for `.index({ text: true })`. Creates a MongoDB text index on\n\t\t * this field, enabling full-text search queries with `$text`.\n\t\t *\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const bio = z.string().text()\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, text: true })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, 'expireAfter', {\n\t\t/**\n\t\t * Shorthand for `.index({ expireAfter: seconds })`. Creates a TTL index on\n\t\t * a `Date` field. MongoDB will automatically remove documents once the field\n\t\t * value is older than the specified number of seconds.\n\t\t *\n\t\t * @param seconds - TTL in seconds after which documents expire.\n\t\t * @returns The same schema instance for chainability.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const expiresAt = z.date().expireAfter(86400) // 24 hours\n\t\t * ```\n\t\t */\n\t\tvalue(this: typeof proto, seconds: number): typeof proto {\n\t\t\tindexMetadata.set(this, { indexed: true, expireAfter: seconds })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tinstallRefExtension()\n\n\tObject.defineProperty(proto, GUARD, {\n\t\tvalue: true,\n\t\tenumerable: false,\n\t\tconfigurable: false,\n\t\twritable: false,\n\t})\n}\n\ninstallExtensions()\n","import { z } from 'zod'\nimport type { AnyCollection, InferDocument } from '../collection/types'\n\n/**\n * Type-level marker that carries the target collection type through the\n * type system. Intersected with the schema return type by `.ref()` so\n * that `RefFields<T>` (future) can extract ref relationships.\n *\n * This is a phantom brand — no runtime value has this property.\n */\nexport type RefMarker<TCollection extends AnyCollection = AnyCollection> = {\n\treadonly _ref: TCollection\n}\n\n/**\n * Metadata stored in the WeakMap sidecar for schemas marked with `.ref()`.\n * Holds a reference to the target collection definition object.\n */\nexport type RefMetadata = {\n\treadonly collection: AnyCollection\n}\n\n/**\n * Module augmentation: adds `.ref()` to all `ZodType` schemas.\n *\n * The intersection constraint `this['_zod']['output'] extends InferDocument<TCollection>['_id']`\n * ensures compile-time type safety: the field's output type must match the\n * target collection's `_id` type. Mismatches produce a type error.\n *\n * Supports both default ObjectId `_id` and custom `_id` types (string, nanoid, etc.):\n * - `objectId().ref(Users)` compiles when Users has ObjectId `_id`\n * - `z.string().ref(Orgs)` compiles when Orgs has string `_id`\n * - `z.string().ref(Users)` is a type error (string ≠ ObjectId)\n * - `objectId().ref(Orgs)` is a type error (ObjectId ≠ string)\n */\ndeclare module 'zod' {\n\tinterface ZodType {\n\t\t/**\n\t\t * Declare a typed foreign key reference to another collection.\n\t\t *\n\t\t * Stores the target collection definition in metadata for runtime\n\t\t * populate resolution, and brands the return type with\n\t\t * `RefMarker<TCollection>` so `RefFields<T>` can extract refs\n\t\t * at the type level.\n\t\t *\n\t\t * The field's output type must match the target collection's `_id` type.\n\t\t * Mismatched types produce a compile error.\n\t\t *\n\t\t * Apply `.ref()` before wrapper methods like `.optional()` or `.nullable()`:\n\t\t * `objectId().ref(Users).optional()` — not `objectId().optional().ref(Users)`.\n\t\t *\n\t\t * @param collection - The target collection definition object.\n\t\t * @returns The same schema instance, branded with the ref marker.\n\t\t *\n\t\t * @example\n\t\t * ```ts\n\t\t * const Posts = collection('posts', {\n\t\t * authorId: objectId().ref(Users),\n\t\t * title: z.string(),\n\t\t * })\n\t\t * ```\n\t\t */\n\t\tref<TCollection extends AnyCollection>(\n\t\t\tcollection: TCollection &\n\t\t\t\t(this['_zod']['output'] extends InferDocument<TCollection>['_id'] ? unknown : never),\n\t\t): this & RefMarker<TCollection>\n\t}\n}\n\n/**\n * WeakMap sidecar that associates a Zod schema instance with its\n * {@link RefMetadata}. Uses WeakMap so ref metadata does not prevent\n * garbage collection of schema instances.\n */\nconst refMetadata = new WeakMap<object, RefMetadata>()\n\n/**\n * Retrieve the ref metadata attached to a Zod schema, if any.\n *\n * Returns `undefined` when the schema was never marked with `.ref()`.\n *\n * @param schema - The Zod schema to inspect. Accepts `unknown` for\n * convenience; non-object values safely return `undefined`.\n * @returns The {@link RefMetadata} for the schema, or `undefined`.\n *\n * @example\n * ```ts\n * const authorId = objectId().ref(Users)\n * const meta = getRefMetadata(authorId)\n * // => { collection: Users }\n * ```\n */\nexport function getRefMetadata(schema: unknown): RefMetadata | undefined {\n\tif (typeof schema !== 'object' || schema === null) return undefined\n\treturn refMetadata.get(schema)\n}\n\n/**\n * Symbol guard to prevent double-registration of the `.ref()` extension.\n * Uses `Symbol.for` so it is shared across realms / duplicate module loads.\n */\nconst REF_GUARD = Symbol.for('zodmon_ref')\n\n/**\n * Install the `.ref()` extension method on `ZodType.prototype`.\n *\n * Idempotent — safe to call multiple times.\n */\nexport function installRefExtension(): void {\n\tconst proto = z.ZodType.prototype\n\tif (REF_GUARD in proto) return\n\n\tObject.defineProperty(proto, 'ref', {\n\t\tvalue(this: typeof proto, collection: AnyCollection): typeof proto {\n\t\t\trefMetadata.set(this, { collection })\n\t\t\treturn this\n\t\t},\n\t\tenumerable: true,\n\t\tconfigurable: true,\n\t\twritable: true,\n\t})\n\n\tObject.defineProperty(proto, REF_GUARD, {\n\t\tvalue: true,\n\t\tenumerable: false,\n\t\tconfigurable: false,\n\t\twritable: false,\n\t})\n}\n","import { ObjectId } from 'mongodb'\nimport { type ZodCustom, type ZodPipe, type ZodTransform, z } from 'zod'\n\n/** Matches a 24-character hexadecimal string (case-insensitive). */\nconst OBJECT_ID_HEX = /^[a-f\\d]{24}$/i\n\n/**\n * The Zod type produced by {@link objectId}. A pipeline that validates an\n * input as either a `string` (24-char hex) or an `ObjectId` instance, then\n * transforms it into a concrete `ObjectId`.\n *\n * Use `z.infer<ZodObjectId>` to extract the output type (`ObjectId`) and\n * `z.input<ZodObjectId>` for the input type (`string | ObjectId`).\n */\nexport type ZodObjectId = ZodPipe<\n\tZodCustom<string | ObjectId, string | ObjectId>,\n\tZodTransform<ObjectId, string | ObjectId>\n>\n\n/**\n * Create a Zod schema that validates and coerces values into MongoDB\n * `ObjectId` instances.\n *\n * Accepts either:\n * - An existing `ObjectId` instance (passed through unchanged).\n * - A 24-character hexadecimal string (coerced to `ObjectId`).\n *\n * All other inputs are rejected with the message `\"Invalid ObjectId\"`.\n *\n * @returns A {@link ZodObjectId} schema.\n *\n * @example\n * ```ts\n * const schema = objectId()\n *\n * schema.parse(new ObjectId()) // OK — pass-through\n * schema.parse('64f1a2b3c4d5e6f7a8b9c0d1') // OK — coerced to ObjectId\n * schema.parse('not-valid') // throws ZodError\n * ```\n *\n * @example Inside a z.object() shape:\n * ```ts\n * const UserSchema = z.object({\n * _id: objectId(),\n * name: z.string(),\n * })\n * ```\n */\nexport function objectId(): ZodObjectId {\n\treturn z\n\t\t.custom<string | ObjectId>((val): val is string | ObjectId => {\n\t\t\tif (val instanceof ObjectId) return true\n\t\t\treturn typeof val === 'string' && OBJECT_ID_HEX.test(val)\n\t\t}, 'Invalid ObjectId')\n\t\t.transform((val) => (val instanceof ObjectId ? val : ObjectId.createFromHexString(val)))\n}\n","import type { CompoundIndexDefinition } from './types'\n\ntype IndexDirection = 1 | -1\n\ntype CompoundIndexOptions = NonNullable<CompoundIndexDefinition['options']>\n\n/**\n * A builder for compound index definitions.\n *\n * Provides a fluent API for declaring compound indexes with options like\n * `unique`, `sparse`, and custom `name`. Each method returns a new\n * IndexBuilder instance (immutable pattern — the original is never mutated).\n *\n * IndexBuilder is structurally compatible with {@link CompoundIndexDefinition}\n * so instances can be used directly in `CollectionOptions.indexes`.\n *\n * Dot-notation paths like `'address.city'` are accepted at the value level\n * (any string satisfies `TKeys`), but type-level validation against nested\n * schema paths is deferred to a future release.\n *\n * @example\n * ```ts\n * index({ email: 1, role: -1 }).unique().name('email_role_idx')\n * ```\n */\nexport class IndexBuilder<TKeys extends string> {\n\t// Typed as Partial for structural compatibility with CompoundIndexDefinition.\n\t// The constructor guarantees all keys are present at runtime.\n\treadonly fields: Partial<Record<TKeys, IndexDirection>>\n\treadonly options: CompoundIndexOptions\n\n\tconstructor(fields: Record<TKeys, IndexDirection>) {\n\t\tthis.fields = fields\n\t\tthis.options = {}\n\t}\n\n\tprivate _clone(options: CompoundIndexOptions): IndexBuilder<TKeys> {\n\t\t// Object.create returns `any`; cast is safe because we assign the correct shape\n\t\treturn Object.assign(Object.create(IndexBuilder.prototype) as IndexBuilder<TKeys>, {\n\t\t\tfields: this.fields,\n\t\t\toptions,\n\t\t})\n\t}\n\n\t// Safe cast: _clone returns IndexBuilder<TKeys> but `this` may carry an\n\t// intersection from .name(). The cast is safe because _clone preserves all fields.\n\tunique(): this {\n\t\treturn this._clone({ ...this.options, unique: true }) as this\n\t}\n\n\t// Safe cast: same reasoning as unique().\n\tsparse(): this {\n\t\treturn this._clone({ ...this.options, sparse: true }) as this\n\t}\n\n\t/**\n\t * Set a custom name for this index, preserving the literal type.\n\t *\n\t * The returned builder carries the literal name type via an intersection,\n\t * enabling type-safe `.hint()` on cursors that only accepts declared names.\n\t *\n\t * @param name - The index name.\n\t * @returns A new IndexBuilder with the name recorded at the type level.\n\t *\n\t * @example\n\t * ```ts\n\t * index({ email: 1, role: -1 }).name('email_role_idx')\n\t * ```\n\t */\n\tname<TName extends string>(\n\t\tname: TName,\n\t): IndexBuilder<TKeys> & { readonly options: { readonly name: TName } } {\n\t\t// Safe cast: _clone returns IndexBuilder<TKeys> which is structurally correct;\n\t\t// the intersection adds the literal name type for downstream type extraction.\n\t\treturn this._clone({ ...this.options, name }) as IndexBuilder<TKeys> & {\n\t\t\treadonly options: { readonly name: TName }\n\t\t}\n\t}\n}\n\n/**\n * Create a compound index definition with a fluent builder API.\n *\n * Returns an {@link IndexBuilder} that is structurally compatible with\n * `CompoundIndexDefinition`, so it can be used directly in\n * `CollectionOptions.indexes` alongside plain objects.\n *\n * @param fields - An object mapping field names to sort direction (1 or -1).\n * @returns An {@link IndexBuilder} instance.\n *\n * @example\n * ```ts\n * collection('users', { email: z.string(), role: z.string() }, {\n * indexes: [\n * index({ email: 1, role: -1 }).unique(),\n * { fields: { role: 1 } },\n * ],\n * })\n * ```\n */\nexport function index<TKeys extends string>(\n\tfields: Record<TKeys, IndexDirection>,\n): IndexBuilder<TKeys> {\n\treturn new IndexBuilder(fields)\n}\n","import { ObjectId } from 'mongodb'\n\n/**\n * Create or coerce a MongoDB `ObjectId`.\n *\n * - Called with **no arguments**: generates a brand-new `ObjectId`.\n * - Called with a **hex string**: coerces it to an `ObjectId` via\n * `ObjectId.createFromHexString`.\n * - Called with an **existing `ObjectId`**: returns it unchanged.\n *\n * This is a convenience wrapper that removes the need for `new ObjectId()`\n * boilerplate throughout application code.\n *\n * @param value - Optional hex string or `ObjectId` to coerce. Omit to\n * generate a new `ObjectId`.\n * @returns An `ObjectId` instance.\n *\n * @example\n * ```ts\n * oid() // new random ObjectId\n * oid('64f1a2b3c4d5e6f7a8b9c0d1') // coerce hex string\n * oid(existingId) // pass-through\n * ```\n */\nexport function oid(): ObjectId\nexport function oid(value: string): ObjectId\nexport function oid(value: ObjectId): ObjectId\nexport function oid(value?: string | ObjectId): ObjectId {\n\tif (value === undefined) return new ObjectId()\n\tif (value instanceof ObjectId) return value\n\treturn ObjectId.createFromHexString(value)\n}\n\n/**\n * Type guard that narrows an `unknown` value to `ObjectId`.\n *\n * Uses `instanceof` internally, so it works with any value without risk\n * of throwing.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an `ObjectId` instance.\n *\n * @example\n * ```ts\n * const raw: unknown = getFromDb()\n * if (isOid(raw)) {\n * console.log(raw.toHexString()) // raw is narrowed to ObjectId\n * }\n * ```\n */\nexport function isOid(value: unknown): value is ObjectId {\n\treturn value instanceof ObjectId\n}\n","import type { TypedFilter } from './filter'\n\n// ── Value operators ──────────────────────────────────────────────────────────\n\n/**\n * Matches values equal to the specified value.\n *\n * @example\n * ```ts\n * // Explicit equality (equivalent to { name: 'Alice' })\n * users.find({ name: $eq('Alice') })\n * ```\n */\nexport const $eq = <V>(value: V): { $eq: V } => ({ $eq: value })\n\n/**\n * Matches values not equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ role: $ne('banned') })\n * ```\n */\nexport const $ne = <V>(value: V): { $ne: V } => ({ $ne: value })\n\n/**\n * Matches values greater than the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $gt(18) })\n * ```\n */\nexport const $gt = <V>(value: V): { $gt: V } => ({ $gt: value })\n\n/**\n * Matches values greater than or equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $gte(18) })\n * ```\n */\nexport const $gte = <V>(value: V): { $gte: V } => ({ $gte: value })\n\n/**\n * Matches values less than the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $lt(65) })\n * ```\n */\nexport const $lt = <V>(value: V): { $lt: V } => ({ $lt: value })\n\n/**\n * Matches values less than or equal to the specified value.\n *\n * @example\n * ```ts\n * users.find({ age: $lte(65) })\n * ```\n */\nexport const $lte = <V>(value: V): { $lte: V } => ({ $lte: value })\n\n/**\n * Matches any value in the specified array.\n *\n * @example\n * ```ts\n * users.find({ role: $in(['admin', 'moderator']) })\n * ```\n */\nexport const $in = <V>(values: V[]): { $in: V[] } => ({ $in: values })\n\n/**\n * Matches none of the values in the specified array.\n *\n * @example\n * ```ts\n * users.find({ role: $nin(['banned', 'suspended']) })\n * ```\n */\nexport const $nin = <V>(values: V[]): { $nin: V[] } => ({ $nin: values })\n\n/**\n * Matches documents where the field exists (or does not exist).\n * Defaults to `true` when called with no arguments.\n *\n * @example\n * ```ts\n * // Field must exist\n * users.find({ email: $exists() })\n *\n * // Field must not exist\n * users.find({ deletedAt: $exists(false) })\n * ```\n */\nexport const $exists = (flag = true): { $exists: boolean } => ({ $exists: flag })\n\n/**\n * Matches string values against a regular expression pattern.\n * Only valid on string fields.\n *\n * @example\n * ```ts\n * users.find({ name: $regex(/^A/i) })\n * users.find({ email: $regex('^admin@') })\n * ```\n */\nexport const $regex = (pattern: RegExp | string): { $regex: RegExp | string } => ({\n\t$regex: pattern,\n})\n\n/**\n * Negates a comparison operator. Wraps the given operator object\n * in a `$not` condition.\n *\n * @example\n * ```ts\n * // Age is NOT greater than 65\n * users.find({ age: $not($gt(65)) })\n *\n * // Name does NOT match pattern\n * users.find({ name: $not($regex(/^test/)) })\n * ```\n */\nexport const $not = <O extends Record<string, unknown>>(op: O): { $not: O } => ({\n\t$not: op,\n})\n\n// ── Logical operators ────────────────────────────────────────────────────────\n\n/**\n * Joins filter clauses with a logical OR. Matches documents that satisfy\n * at least one of the provided filters.\n *\n * @example\n * ```ts\n * // T inferred from collection's find() context\n * users.find($or({ role: 'admin' }, { age: $gte(18) }))\n *\n * // Explicit generic for standalone usage\n * const filter = $or<User>({ role: 'admin' }, { age: $gte(18) })\n * ```\n */\nexport const $or = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $or: filters }) as TypedFilter<T>\n\n/**\n * Joins filter clauses with a logical AND. Matches documents that satisfy\n * all of the provided filters. Useful for dynamic filter building where\n * multiple conditions on the same field would conflict in an object literal.\n *\n * @example\n * ```ts\n * // T inferred from collection's find() context\n * users.find($and(\n * $or({ role: 'admin' }, { role: 'moderator' }),\n * { age: $gte(18) },\n * { email: $exists() },\n * ))\n * ```\n */\nexport const $and = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $and: filters }) as TypedFilter<T>\n\n/**\n * Joins filter clauses with a logical NOR. Matches documents that fail\n * all of the provided filters.\n *\n * @example\n * ```ts\n * // Exclude banned and suspended users\n * users.find($nor({ role: 'banned' }, { role: 'suspended' }))\n * ```\n */\nexport const $nor = <T>(...filters: NoInfer<TypedFilter<T>>[]): TypedFilter<T> =>\n\t({ $nor: filters }) as TypedFilter<T>\n\n// ── Escape hatch ─────────────────────────────────────────────────────────────\n\n/**\n * Escape hatch for unsupported or raw MongoDB filter operators.\n * Wraps an untyped filter object so it can be passed where `TypedFilter<T>` is expected.\n * Use when you need operators not covered by the type system (e.g., `$text`, `$geoNear`).\n *\n * @example\n * ```ts\n * users.find(raw({ $text: { $search: 'mongodb tutorial' } }))\n * ```\n */\n// biome-ignore lint/suspicious/noExplicitAny: intentional escape hatch for raw MongoDB filters\nexport const raw = <T = any>(filter: Record<string, unknown>): TypedFilter<T> =>\n\tfilter as TypedFilter<T>\n","import {\n\t$and,\n\t$eq,\n\t$exists,\n\t$gt,\n\t$gte,\n\t$in,\n\t$lt,\n\t$lte,\n\t$ne,\n\t$nin,\n\t$nor,\n\t$not,\n\t$or,\n\t$regex,\n\traw,\n} from './operators'\n\n/**\n * Convenience namespace that groups all query operators under a single import.\n *\n * Property names strip the `$` prefix since the namespace itself is `$`.\n * Individual operator exports (`$or`, `$gt`, etc.) remain available for\n * tree-shaking — this namespace is the ergonomic alternative.\n *\n * @example\n * ```ts\n * import { $ } from '@zodmon/core'\n *\n * users.find($.or({ role: 'admin' }, { age: $.gte(25) }))\n * users.find({ name: $.regex(/^A/i), age: $.gt(18) })\n * users.find($.and({ published: true }, { views: $.gte(100) }))\n * ```\n */\nexport const $ = {\n\teq: $eq,\n\tne: $ne,\n\tgt: $gt,\n\tgte: $gte,\n\tlt: $lt,\n\tlte: $lte,\n\tin: $in,\n\tnin: $nin,\n\texists: $exists,\n\tregex: $regex,\n\tnot: $not,\n\tor: $or,\n\tand: $and,\n\tnor: $nor,\n\traw,\n} as const\n"],"mappings":"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","names":["import_mongodb","collection","import_zod","collection","import_zod","sortKeys","sortKeys","raw","raw","import_zod","import_zod","import_mongodb","import_zod","import_zod","import_zod","collection","import_mongodb","import_zod","import_mongodb"]}
|