@zodmon/core 0.1.0 → 0.3.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 +374 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +897 -5
- package/dist/index.d.ts +897 -5
- package/dist/index.js +350 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,904 @@
|
|
|
1
|
-
import { ObjectId } from 'mongodb';
|
|
2
|
-
import { ZodPipe, ZodCustom, ZodTransform } from 'zod';
|
|
1
|
+
import { ObjectId, Document, Collection, MongoClientOptions } from 'mongodb';
|
|
2
|
+
import { ZodPipe, ZodCustom, ZodTransform, z } from 'zod';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Options controlling how a field-level MongoDB index is created.
|
|
6
|
+
*
|
|
7
|
+
* Passed to the `.index()` Zod extension method. Every property is optional;
|
|
8
|
+
* omitting all of them creates a standard ascending, non-unique index.
|
|
9
|
+
*/
|
|
10
|
+
type IndexOptions = {
|
|
11
|
+
/** When `true`, MongoDB enforces a unique constraint on this field. */
|
|
12
|
+
unique?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* When `true`, the index skips documents where the field is `null` or missing.
|
|
15
|
+
* Useful for optional fields that should be indexed only when present.
|
|
16
|
+
*/
|
|
17
|
+
sparse?: boolean;
|
|
18
|
+
/** When `true`, creates a MongoDB text index for full-text search on this field. */
|
|
19
|
+
text?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* When `true`, the index is created in descending order (`-1`).
|
|
22
|
+
* Defaults to ascending (`1`) when omitted.
|
|
23
|
+
*/
|
|
24
|
+
descending?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* TTL in seconds. MongoDB will automatically delete documents once the
|
|
27
|
+
* indexed `Date` field is older than this many seconds. Only valid on
|
|
28
|
+
* fields whose runtime type is `Date`.
|
|
29
|
+
*/
|
|
30
|
+
expireAfter?: number;
|
|
31
|
+
/**
|
|
32
|
+
* A partial filter expression. Only documents matching this filter are
|
|
33
|
+
* included in the index. Maps directly to MongoDB's `partialFilterExpression`.
|
|
34
|
+
*/
|
|
35
|
+
partial?: Record<string, unknown>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Metadata stored in the WeakMap sidecar for every schema that has been
|
|
39
|
+
* marked with `.index()`. Always contains `indexed: true` plus any
|
|
40
|
+
* {@link IndexOptions} the caller provided.
|
|
41
|
+
*/
|
|
42
|
+
type IndexMetadata = {
|
|
43
|
+
/** Always `true` — acts as a discriminator for "this field is indexed". */
|
|
44
|
+
indexed: true;
|
|
45
|
+
} & IndexOptions;
|
|
46
|
+
declare module 'zod' {
|
|
47
|
+
interface ZodType {
|
|
48
|
+
/**
|
|
49
|
+
* Mark this field for indexing when `syncIndexes()` is called.
|
|
50
|
+
*
|
|
51
|
+
* Stores {@link IndexOptions} in a WeakMap sidecar so the collection
|
|
52
|
+
* factory can later introspect each field and build the appropriate
|
|
53
|
+
* MongoDB index specification.
|
|
54
|
+
*
|
|
55
|
+
* @param options - Optional index configuration (unique, sparse, etc.).
|
|
56
|
+
* @returns The same schema instance for chaining.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const UserSchema = z.object({
|
|
61
|
+
* email: z.string().index({ unique: true }),
|
|
62
|
+
* age: z.number().index({ sparse: true }),
|
|
63
|
+
* })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
index(options?: IndexOptions): this;
|
|
67
|
+
/**
|
|
68
|
+
* Shorthand for `.index({ unique: true })`.
|
|
69
|
+
*
|
|
70
|
+
* Creates a unique index on this field, causing MongoDB to reject
|
|
71
|
+
* duplicate values.
|
|
72
|
+
*
|
|
73
|
+
* @returns The same schema instance for chaining.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```ts
|
|
77
|
+
* const UserSchema = z.object({
|
|
78
|
+
* email: z.string().unique(),
|
|
79
|
+
* })
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
unique(): this;
|
|
83
|
+
/**
|
|
84
|
+
* Shorthand for `.index({ text: true })`.
|
|
85
|
+
*
|
|
86
|
+
* Creates a MongoDB text index on this field, enabling `$text` queries
|
|
87
|
+
* for full-text search.
|
|
88
|
+
*
|
|
89
|
+
* @returns The same schema instance for chaining.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* const PostSchema = z.object({
|
|
94
|
+
* body: z.string().text(),
|
|
95
|
+
* })
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
text(): this;
|
|
99
|
+
/**
|
|
100
|
+
* Shorthand for `.index({ expireAfter: seconds })`.
|
|
101
|
+
*
|
|
102
|
+
* Creates a TTL (Time-To-Live) index. MongoDB will automatically
|
|
103
|
+
* remove documents once the indexed `Date` field is older than
|
|
104
|
+
* the specified number of seconds.
|
|
105
|
+
*
|
|
106
|
+
* @param seconds - Number of seconds after which documents expire.
|
|
107
|
+
* @returns The same schema instance for chaining.
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const SessionSchema = z.object({
|
|
112
|
+
* createdAt: z.date().expireAfter(3600), // 1 hour TTL
|
|
113
|
+
* })
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
expireAfter(seconds: number): this;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Retrieve the index metadata attached to a Zod schema, if any.
|
|
121
|
+
*
|
|
122
|
+
* Returns `undefined` when the schema was never marked with an index
|
|
123
|
+
* extension (`.index()`, `.unique()`, `.text()`, or `.expireAfter()`).
|
|
124
|
+
*
|
|
125
|
+
* @param schema - The Zod schema to inspect. Accepts `unknown` for
|
|
126
|
+
* convenience; non-object values safely return `undefined`.
|
|
127
|
+
* @returns The {@link IndexMetadata} for the schema, or `undefined`.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* const email = z.string().index({ unique: true })
|
|
132
|
+
* const meta = getIndexMetadata(email)
|
|
133
|
+
* // => { indexed: true, unique: true }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
declare function getIndexMetadata(schema: unknown): IndexMetadata | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* Monkey-patch Zod's `ZodType.prototype` with Zodmon extension methods
|
|
139
|
+
* (`.index()`, `.unique()`, `.text()`, `.expireAfter()`).
|
|
140
|
+
*
|
|
141
|
+
* In Zod v4, methods are copied from `ZodType.prototype` to each instance
|
|
142
|
+
* during construction via the internal `init` loop (`Object.keys(proto)` ->
|
|
143
|
+
* copy to instance). Extension methods use `enumerable: true` so they are
|
|
144
|
+
* picked up by this loop for every schema created after installation.
|
|
145
|
+
*
|
|
146
|
+
* The function is idempotent: calling it more than once is a safe no-op,
|
|
147
|
+
* guarded by a non-enumerable `Symbol.for('zodmon_extensions')` property
|
|
148
|
+
* on the prototype.
|
|
149
|
+
*
|
|
150
|
+
* This function is called at module level when `extensions.ts` is first
|
|
151
|
+
* imported, so consumers never need to call it manually. It is exported
|
|
152
|
+
* primarily for use in tests.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* import { installExtensions } from '@zodmon/core/schema/extensions'
|
|
157
|
+
* installExtensions() // safe to call multiple times
|
|
158
|
+
*
|
|
159
|
+
* const indexed = z.string().index({ unique: true })
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
declare function installExtensions(): void;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The Zod type produced by {@link objectId}. A pipeline that validates an
|
|
166
|
+
* input as either a `string` (24-char hex) or an `ObjectId` instance, then
|
|
167
|
+
* transforms it into a concrete `ObjectId`.
|
|
168
|
+
*
|
|
169
|
+
* Use `z.infer<ZodObjectId>` to extract the output type (`ObjectId`) and
|
|
170
|
+
* `z.input<ZodObjectId>` for the input type (`string | ObjectId`).
|
|
171
|
+
*/
|
|
172
|
+
type ZodObjectId = ZodPipe<ZodCustom<string | ObjectId, string | ObjectId>, ZodTransform<ObjectId, string | ObjectId>>;
|
|
173
|
+
/**
|
|
174
|
+
* Create a Zod schema that validates and coerces values into MongoDB
|
|
175
|
+
* `ObjectId` instances.
|
|
176
|
+
*
|
|
177
|
+
* Accepts either:
|
|
178
|
+
* - An existing `ObjectId` instance (passed through unchanged).
|
|
179
|
+
* - A 24-character hexadecimal string (coerced to `ObjectId`).
|
|
180
|
+
*
|
|
181
|
+
* All other inputs are rejected with the message `"Invalid ObjectId"`.
|
|
182
|
+
*
|
|
183
|
+
* @returns A {@link ZodObjectId} schema.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* const schema = objectId()
|
|
188
|
+
*
|
|
189
|
+
* schema.parse(new ObjectId()) // OK — pass-through
|
|
190
|
+
* schema.parse('64f1a2b3c4d5e6f7a8b9c0d1') // OK — coerced to ObjectId
|
|
191
|
+
* schema.parse('not-valid') // throws ZodError
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* @example Inside a z.object() shape:
|
|
195
|
+
* ```ts
|
|
196
|
+
* const UserSchema = z.object({
|
|
197
|
+
* _id: objectId(),
|
|
198
|
+
* name: z.string(),
|
|
199
|
+
* })
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
declare function objectId(): ZodObjectId;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* A compound index definition declared at the collection level.
|
|
206
|
+
* Maps field paths to sort direction (1 ascending, -1 descending)
|
|
207
|
+
* with optional index-level configuration.
|
|
208
|
+
*
|
|
209
|
+
* Generic over `TKeys` so that only fields present in the collection
|
|
210
|
+
* schema are accepted — referencing a nonexistent field is a type error.
|
|
211
|
+
*/
|
|
212
|
+
type CompoundIndexDefinition<TKeys extends string = string> = {
|
|
213
|
+
fields: Partial<Record<TKeys, 1 | -1>>;
|
|
214
|
+
options?: {
|
|
215
|
+
unique?: boolean;
|
|
216
|
+
sparse?: boolean;
|
|
217
|
+
partial?: Record<string, unknown>;
|
|
218
|
+
name?: string;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* A resolved field-level index extracted from schema metadata.
|
|
223
|
+
* Produced by walking the shape and calling getIndexMetadata() on each field.
|
|
224
|
+
*/
|
|
225
|
+
type FieldIndexDefinition = {
|
|
226
|
+
field: string;
|
|
227
|
+
} & IndexOptions;
|
|
228
|
+
/** Options passed to collection() as the third argument. */
|
|
229
|
+
type CollectionOptions<TKeys extends string = string> = {
|
|
230
|
+
indexes?: CompoundIndexDefinition<TKeys>[];
|
|
231
|
+
validation?: 'strict' | 'strip' | 'passthrough';
|
|
232
|
+
warnUnindexedQueries?: boolean;
|
|
233
|
+
schemaVersion?: number;
|
|
234
|
+
migrate?: (doc: Record<string, unknown>, version: number) => Record<string, unknown>;
|
|
235
|
+
writeback?: boolean;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Resolves the final shape for a collection schema.
|
|
239
|
+
*
|
|
240
|
+
* If the user-provided shape already contains `_id`, it is used as-is.
|
|
241
|
+
* Otherwise, `_id: ZodObjectId` is prepended automatically.
|
|
242
|
+
* This allows custom id types (nanoid, UUID, etc.) when needed.
|
|
243
|
+
*/
|
|
244
|
+
type ResolvedShape<TShape extends z.core.$ZodShape> = '_id' extends keyof TShape ? TShape : {
|
|
245
|
+
_id: ZodObjectId;
|
|
246
|
+
} & TShape;
|
|
247
|
+
/**
|
|
248
|
+
* The document type inferred from a collection definition.
|
|
249
|
+
* Uses the compiled schema directly, so it reflects whatever `_id`
|
|
250
|
+
* type was provided (custom or default ObjectId).
|
|
251
|
+
*
|
|
252
|
+
* Uses a structural constraint (`{ schema: z.ZodType }`) instead of
|
|
253
|
+
* `CollectionDefinition<z.core.$ZodShape>` because the conditional
|
|
254
|
+
* `ResolvedShape` type creates variance issues with index signatures.
|
|
255
|
+
*/
|
|
256
|
+
type InferDocument<TDef extends {
|
|
257
|
+
readonly schema: z.ZodType;
|
|
258
|
+
}> = z.infer<TDef['schema']>;
|
|
259
|
+
/**
|
|
260
|
+
* The immutable definition object returned by collection().
|
|
261
|
+
* Holds everything needed to later create a live collection handle.
|
|
262
|
+
*/
|
|
263
|
+
type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape> = {
|
|
264
|
+
readonly name: string;
|
|
265
|
+
readonly schema: z.ZodObject<ResolvedShape<TShape>>;
|
|
266
|
+
readonly shape: TShape;
|
|
267
|
+
readonly fieldIndexes: FieldIndexDefinition[];
|
|
268
|
+
readonly compoundIndexes: CompoundIndexDefinition<Extract<keyof TShape, string>>[];
|
|
269
|
+
readonly options: Required<Pick<CollectionOptions, 'validation'>> & Omit<CollectionOptions, 'indexes' | 'validation'>;
|
|
270
|
+
};
|
|
271
|
+
/** Erased collection type for use in generic contexts. */
|
|
272
|
+
type AnyCollection = CollectionDefinition<z.core.$ZodShape>;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Typed wrapper around a MongoDB driver `Collection`.
|
|
276
|
+
*
|
|
277
|
+
* Created by {@link Database.use}. Holds the original `CollectionDefinition`
|
|
278
|
+
* (for runtime schema validation and index metadata) alongside the native
|
|
279
|
+
* driver collection parameterized with the inferred document type.
|
|
280
|
+
*
|
|
281
|
+
* CRUD methods (insertOne, find, etc.) are added to this class by
|
|
282
|
+
* subsequent modules — the handle itself is intentionally behavior-free.
|
|
283
|
+
*
|
|
284
|
+
* @typeParam TDoc - The document type inferred from the Zod schema
|
|
285
|
+
* (e.g. `{ _id: ObjectId; name: string }`). Defaults to `Document`.
|
|
286
|
+
*/
|
|
287
|
+
declare class CollectionHandle<TDoc extends Document = Document> {
|
|
288
|
+
/** The collection definition containing schema, name, and index metadata. */
|
|
289
|
+
readonly definition: AnyCollection;
|
|
290
|
+
/** The underlying MongoDB driver collection, typed to `TDoc`. */
|
|
291
|
+
readonly native: Collection<TDoc>;
|
|
292
|
+
constructor(definition: AnyCollection, native: Collection<TDoc>);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Wraps a MongoDB `MongoClient` and `Db`, providing typed collection access
|
|
297
|
+
* through {@link CollectionHandle}s.
|
|
298
|
+
*
|
|
299
|
+
* Connection is lazy — the driver connects on the first operation, not at
|
|
300
|
+
* construction time. Call {@link close} for graceful shutdown.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```ts
|
|
304
|
+
* const db = createClient('mongodb://localhost:27017', 'myapp')
|
|
305
|
+
* const users = db.use(UsersCollection)
|
|
306
|
+
* await users.native.insertOne({ _id: oid(), name: 'Ada' })
|
|
307
|
+
* await db.close()
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
declare class Database {
|
|
311
|
+
private readonly _client;
|
|
312
|
+
private readonly _db;
|
|
313
|
+
/** Registered collection definitions, keyed by name. Used by syncIndexes(). */
|
|
314
|
+
private readonly _collections;
|
|
315
|
+
constructor(uri: string, dbName: string, options?: MongoClientOptions);
|
|
316
|
+
/**
|
|
317
|
+
* Register a collection definition and return a typed {@link CollectionHandle}.
|
|
318
|
+
*
|
|
319
|
+
* The handle's `native` property is a MongoDB `Collection<TDoc>` where `TDoc`
|
|
320
|
+
* is the document type inferred from the definition's Zod schema. Calling
|
|
321
|
+
* `use()` multiple times with the same definition is safe — each call returns
|
|
322
|
+
* a new lightweight handle backed by the same underlying driver collection.
|
|
323
|
+
*
|
|
324
|
+
* @param def - A collection definition created by `collection()`.
|
|
325
|
+
* @returns A typed collection handle for CRUD operations.
|
|
326
|
+
*/
|
|
327
|
+
use<TShape extends z.core.$ZodShape>(def: CollectionDefinition<TShape>): CollectionHandle<InferDocument<CollectionDefinition<TShape>>>;
|
|
328
|
+
/**
|
|
329
|
+
* Synchronize indexes defined in registered collections with MongoDB.
|
|
330
|
+
*
|
|
331
|
+
* Stub — full implementation in TASK-92.
|
|
332
|
+
*/
|
|
333
|
+
syncIndexes(): Promise<void>;
|
|
334
|
+
/**
|
|
335
|
+
* Execute a function within a MongoDB transaction with auto-commit/rollback.
|
|
336
|
+
*
|
|
337
|
+
* Stub — full implementation in TASK-106.
|
|
338
|
+
*/
|
|
339
|
+
transaction<T>(_fn: () => Promise<T>): Promise<T>;
|
|
340
|
+
/**
|
|
341
|
+
* Close the underlying `MongoClient` connection. Safe to call even if
|
|
342
|
+
* no connection was established (the driver handles this gracefully).
|
|
343
|
+
*/
|
|
344
|
+
close(): Promise<void>;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Extract the database name from a MongoDB connection URI.
|
|
348
|
+
*
|
|
349
|
+
* Handles standard URIs, multi-host/replica set, SRV (`mongodb+srv://`),
|
|
350
|
+
* auth credentials, query parameters, and percent-encoded database names.
|
|
351
|
+
* Returns `undefined` when no database name is present.
|
|
352
|
+
*/
|
|
353
|
+
declare function extractDbName(uri: string): string | undefined;
|
|
354
|
+
/**
|
|
355
|
+
* Create a new {@link Database} instance wrapping a MongoDB connection.
|
|
356
|
+
*
|
|
357
|
+
* The connection is lazy — the driver connects on the first operation.
|
|
358
|
+
* Pass any `MongoClientOptions` to configure connection pooling, timeouts, etc.
|
|
359
|
+
*
|
|
360
|
+
* When `dbName` is omitted, the database name is extracted from the URI path
|
|
361
|
+
* (e.g. `mongodb://localhost:27017/myapp` → `'myapp'`). If no database name
|
|
362
|
+
* is found in either the arguments or the URI, a warning is logged and
|
|
363
|
+
* MongoDB's default `'test'` database is used.
|
|
364
|
+
*
|
|
365
|
+
* @param uri - MongoDB connection string (e.g. `mongodb://localhost:27017`).
|
|
366
|
+
* @param dbName - The database name to use.
|
|
367
|
+
* @param options - Optional MongoDB driver client options.
|
|
368
|
+
* @returns A new `Database` instance.
|
|
369
|
+
*/
|
|
370
|
+
declare function createClient(uri: string, dbName: string, options?: MongoClientOptions): Database;
|
|
371
|
+
declare function createClient(uri: string, options?: MongoClientOptions): Database;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Walk a Zod shape and extract field-level index metadata from each field.
|
|
375
|
+
*
|
|
376
|
+
* Returns an array of {@link FieldIndexDefinition} for every field that has
|
|
377
|
+
* been marked with `.index()`, `.unique()`, `.text()`, or `.expireAfter()`.
|
|
378
|
+
* Fields without index metadata are silently skipped.
|
|
379
|
+
*
|
|
380
|
+
* @param shape - A Zod shape object (the value passed to `z.object()`).
|
|
381
|
+
* @returns An array of field index definitions with the field name attached.
|
|
382
|
+
*/
|
|
383
|
+
declare function extractFieldIndexes(shape: z.core.$ZodShape): FieldIndexDefinition[];
|
|
384
|
+
/**
|
|
385
|
+
* Define a MongoDB collection with a Zod schema.
|
|
386
|
+
*
|
|
387
|
+
* Creates a {@link CollectionDefinition} that:
|
|
388
|
+
* - Adds `_id: objectId()` if the shape doesn't already include `_id`
|
|
389
|
+
* - Uses the user-provided `_id` schema if one is present (e.g. nanoid, UUID)
|
|
390
|
+
* - Extracts field-level index metadata from the shape
|
|
391
|
+
* - Separates compound indexes from the rest of the options
|
|
392
|
+
* - Returns an immutable definition object
|
|
393
|
+
*
|
|
394
|
+
* @param name - The MongoDB collection name.
|
|
395
|
+
* @param shape - A Zod shape object defining the document fields. May include a custom `_id`.
|
|
396
|
+
* @param options - Optional collection-level configuration including compound indexes.
|
|
397
|
+
* @returns A {@link CollectionDefinition} ready for use with `createClient()`.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```ts
|
|
401
|
+
* const Users = collection('users', {
|
|
402
|
+
* email: z.string().unique(),
|
|
403
|
+
* name: z.string().index(),
|
|
404
|
+
* age: z.number().optional(),
|
|
405
|
+
* })
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
408
|
+
declare function collection<TShape extends z.core.$ZodShape>(name: string, shape: TShape, options?: CollectionOptions<Extract<keyof TShape, string>>): CollectionDefinition<TShape>;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* A builder for compound index definitions.
|
|
412
|
+
*
|
|
413
|
+
* Provides a fluent API for declaring compound indexes with options like
|
|
414
|
+
* `unique`, `sparse`, and custom `name`. Each method returns a new
|
|
415
|
+
* IndexBuilder instance (immutable pattern — the original is never mutated).
|
|
416
|
+
*
|
|
417
|
+
* IndexBuilder is structurally compatible with {@link CompoundIndexDefinition}
|
|
418
|
+
* so instances can be used directly in `CollectionOptions.indexes`.
|
|
419
|
+
*
|
|
420
|
+
* Dot-notation paths like `'address.city'` are accepted at the value level
|
|
421
|
+
* (any string satisfies `TKeys`), but type-level validation against nested
|
|
422
|
+
* schema paths is deferred to a future release.
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* ```ts
|
|
426
|
+
* index({ email: 1, role: -1 }).unique().name('email_role_idx')
|
|
427
|
+
* ```
|
|
428
|
+
*/
|
|
429
|
+
|
|
430
|
+
type IndexDirection = 1 | -1;
|
|
431
|
+
type CompoundIndexOptions = NonNullable<CompoundIndexDefinition['options']>;
|
|
432
|
+
declare class IndexBuilder<TKeys extends string> {
|
|
433
|
+
readonly fields: Partial<Record<TKeys, IndexDirection>>;
|
|
434
|
+
readonly options: CompoundIndexOptions;
|
|
435
|
+
constructor(fields: Record<TKeys, IndexDirection>);
|
|
436
|
+
private _clone;
|
|
437
|
+
unique(): IndexBuilder<TKeys>;
|
|
438
|
+
sparse(): IndexBuilder<TKeys>;
|
|
439
|
+
name(name: string): IndexBuilder<TKeys>;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Create a compound index definition with a fluent builder API.
|
|
443
|
+
*
|
|
444
|
+
* Returns an {@link IndexBuilder} that is structurally compatible with
|
|
445
|
+
* `CompoundIndexDefinition`, so it can be used directly in
|
|
446
|
+
* `CollectionOptions.indexes` alongside plain objects.
|
|
447
|
+
*
|
|
448
|
+
* @param fields - An object mapping field names to sort direction (1 or -1).
|
|
449
|
+
* @returns An {@link IndexBuilder} instance.
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```ts
|
|
453
|
+
* collection('users', { email: z.string(), role: z.string() }, {
|
|
454
|
+
* indexes: [
|
|
455
|
+
* index({ email: 1, role: -1 }).unique(),
|
|
456
|
+
* { fields: { role: 1 } },
|
|
457
|
+
* ],
|
|
458
|
+
* })
|
|
459
|
+
* ```
|
|
460
|
+
*/
|
|
461
|
+
declare function index<TKeys extends string>(fields: Record<TKeys, IndexDirection>): IndexBuilder<TKeys>;
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Create or coerce a MongoDB `ObjectId`.
|
|
465
|
+
*
|
|
466
|
+
* - Called with **no arguments**: generates a brand-new `ObjectId`.
|
|
467
|
+
* - Called with a **hex string**: coerces it to an `ObjectId` via
|
|
468
|
+
* `ObjectId.createFromHexString`.
|
|
469
|
+
* - Called with an **existing `ObjectId`**: returns it unchanged.
|
|
470
|
+
*
|
|
471
|
+
* This is a convenience wrapper that removes the need for `new ObjectId()`
|
|
472
|
+
* boilerplate throughout application code.
|
|
473
|
+
*
|
|
474
|
+
* @param value - Optional hex string or `ObjectId` to coerce. Omit to
|
|
475
|
+
* generate a new `ObjectId`.
|
|
476
|
+
* @returns An `ObjectId` instance.
|
|
477
|
+
*
|
|
478
|
+
* @example
|
|
479
|
+
* ```ts
|
|
480
|
+
* oid() // new random ObjectId
|
|
481
|
+
* oid('64f1a2b3c4d5e6f7a8b9c0d1') // coerce hex string
|
|
482
|
+
* oid(existingId) // pass-through
|
|
483
|
+
* ```
|
|
484
|
+
*/
|
|
4
485
|
declare function oid(): ObjectId;
|
|
5
486
|
declare function oid(value: string): ObjectId;
|
|
6
487
|
declare function oid(value: ObjectId): ObjectId;
|
|
488
|
+
/**
|
|
489
|
+
* Type guard that narrows an `unknown` value to `ObjectId`.
|
|
490
|
+
*
|
|
491
|
+
* Uses `instanceof` internally, so it works with any value without risk
|
|
492
|
+
* of throwing.
|
|
493
|
+
*
|
|
494
|
+
* @param value - The value to check.
|
|
495
|
+
* @returns `true` if `value` is an `ObjectId` instance.
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* ```ts
|
|
499
|
+
* const raw: unknown = getFromDb()
|
|
500
|
+
* if (isOid(raw)) {
|
|
501
|
+
* console.log(raw.toHexString()) // raw is narrowed to ObjectId
|
|
502
|
+
* }
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
7
505
|
declare function isOid(value: unknown): value is ObjectId;
|
|
8
506
|
|
|
9
|
-
|
|
10
|
-
|
|
507
|
+
/**
|
|
508
|
+
* Comparison operators for a field value of type `V`.
|
|
509
|
+
*
|
|
510
|
+
* Maps each MongoDB comparison operator to its expected value type.
|
|
511
|
+
* `$regex` is only available when `V` extends `string`.
|
|
512
|
+
*
|
|
513
|
+
* Used as the operator object that can be assigned to a field in {@link TypedFilter}.
|
|
514
|
+
*
|
|
515
|
+
* @example
|
|
516
|
+
* ```ts
|
|
517
|
+
* // As a raw object (without builder functions)
|
|
518
|
+
* const filter: TypedFilter<User> = { age: { $gt: 25, $lte: 65 } }
|
|
519
|
+
*
|
|
520
|
+
* // $regex only available on string fields
|
|
521
|
+
* const filter: TypedFilter<User> = { name: { $regex: /^A/i } }
|
|
522
|
+
* ```
|
|
523
|
+
*/
|
|
524
|
+
type ComparisonOperators<V> = {
|
|
525
|
+
/** Matches values equal to the specified value. */
|
|
526
|
+
$eq?: V;
|
|
527
|
+
/** Matches values not equal to the specified value. */
|
|
528
|
+
$ne?: V;
|
|
529
|
+
/** Matches values greater than the specified value. */
|
|
530
|
+
$gt?: V;
|
|
531
|
+
/** Matches values greater than or equal to the specified value. */
|
|
532
|
+
$gte?: V;
|
|
533
|
+
/** Matches values less than the specified value. */
|
|
534
|
+
$lt?: V;
|
|
535
|
+
/** Matches values less than or equal to the specified value. */
|
|
536
|
+
$lte?: V;
|
|
537
|
+
/** Matches any value in the specified array. */
|
|
538
|
+
$in?: V[];
|
|
539
|
+
/** Matches none of the values in the specified array. */
|
|
540
|
+
$nin?: V[];
|
|
541
|
+
/** Matches documents where the field exists (`true`) or does not exist (`false`). */
|
|
542
|
+
$exists?: boolean;
|
|
543
|
+
/** Negates a comparison operator. */
|
|
544
|
+
$not?: ComparisonOperators<V>;
|
|
545
|
+
} & (V extends string ? {
|
|
546
|
+
$regex?: RegExp | string;
|
|
547
|
+
} : unknown);
|
|
548
|
+
/** Depth counter for limiting dot-notation recursion. Index = current depth, value = next depth. */
|
|
549
|
+
type Prev = [never, 0, 1, 2];
|
|
550
|
+
/**
|
|
551
|
+
* Generates a union of all valid dot-separated paths for nested object fields in `T`.
|
|
552
|
+
*
|
|
553
|
+
* Recursion is limited to 3 levels deep to prevent TypeScript compilation performance issues.
|
|
554
|
+
* Only plain object fields are traversed — arrays, `Date`, `RegExp`, and `ObjectId` are
|
|
555
|
+
* treated as leaf nodes and do not produce sub-paths.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* ```ts
|
|
559
|
+
* type User = { address: { city: string; geo: { lat: number; lng: number } } }
|
|
560
|
+
*
|
|
561
|
+
* // DotPaths<User> = 'address.city' | 'address.geo' | 'address.geo.lat' | 'address.geo.lng'
|
|
562
|
+
* ```
|
|
563
|
+
*/
|
|
564
|
+
type DotPaths<T, Depth extends number = 3> = Depth extends 0 ? never : {
|
|
565
|
+
[K in keyof T & string]: NonNullable<T[K]> extends ReadonlyArray<unknown> | Date | RegExp | ObjectId ? never : NonNullable<T[K]> extends Record<string, unknown> ? `${K}.${keyof NonNullable<T[K]> & string}` | `${K}.${DotPaths<NonNullable<T[K]>, Prev[Depth]>}` : never;
|
|
566
|
+
}[keyof T & string];
|
|
567
|
+
/**
|
|
568
|
+
* Resolves the value type at a dot-separated path `P` within type `T`.
|
|
569
|
+
*
|
|
570
|
+
* Splits `P` on the first `.` and recursively descends into `T`'s nested types.
|
|
571
|
+
* Returns `never` if the path is invalid.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* type User = { address: { city: string; geo: { lat: number } } }
|
|
576
|
+
*
|
|
577
|
+
* // DotPathType<User, 'address.city'> = string
|
|
578
|
+
* // DotPathType<User, 'address.geo.lat'> = number
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
type DotPathType<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends keyof NonNullable<T[K]> ? NonNullable<T[K]>[Rest] : DotPathType<NonNullable<T[K]>, Rest> : never : P extends keyof T ? T[P] : never;
|
|
582
|
+
/**
|
|
583
|
+
* Strict type-safe MongoDB filter query type.
|
|
584
|
+
*
|
|
585
|
+
* Validates filter objects at compile time — rejects nonexistent fields, type mismatches,
|
|
586
|
+
* and invalid operator usage. Unlike the MongoDB driver's `Filter<T>`, does NOT allow
|
|
587
|
+
* arbitrary keys via `& Document`.
|
|
588
|
+
*
|
|
589
|
+
* Supports three forms of filter expressions:
|
|
590
|
+
* - **Direct field values** (implicit `$eq`): `{ name: 'Alice' }`
|
|
591
|
+
* - **Comparison operators**: `{ age: { $gt: 25 } }` or `{ age: $gt(25) }`
|
|
592
|
+
* - **Dot notation** for nested fields up to 3 levels: `{ 'address.city': 'NYC' }`
|
|
593
|
+
*
|
|
594
|
+
* Logical operators `$and`, `$or`, and `$nor` accept arrays of `TypedFilter<T>`
|
|
595
|
+
* for composing complex queries.
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```ts
|
|
599
|
+
* // Simple equality
|
|
600
|
+
* const filter: TypedFilter<User> = { name: 'Alice' }
|
|
601
|
+
*
|
|
602
|
+
* // Builder functions mixed with object literals
|
|
603
|
+
* const filter: TypedFilter<User> = { age: $gte(18), role: $in(['admin', 'mod']) }
|
|
604
|
+
*
|
|
605
|
+
* // Logical composition
|
|
606
|
+
* const filter = $and<User>(
|
|
607
|
+
* $or<User>({ role: 'admin' }, { role: 'moderator' }),
|
|
608
|
+
* { age: $gte(18) },
|
|
609
|
+
* { email: $exists() },
|
|
610
|
+
* )
|
|
611
|
+
*
|
|
612
|
+
* // Dynamic conditional building
|
|
613
|
+
* const conditions: TypedFilter<User>[] = []
|
|
614
|
+
* if (name) conditions.push({ name })
|
|
615
|
+
* if (minAge) conditions.push({ age: $gte(minAge) })
|
|
616
|
+
* const filter = conditions.length ? $and<User>(...conditions) : {}
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
type TypedFilter<T> = {
|
|
620
|
+
[K in keyof T]?: T[K] | ComparisonOperators<T[K]>;
|
|
621
|
+
} & {
|
|
622
|
+
[P in DotPaths<T>]?: DotPathType<T, P> | ComparisonOperators<DotPathType<T, P>>;
|
|
623
|
+
} & {
|
|
624
|
+
/** Joins clauses with a logical AND. Matches documents that satisfy all filters. */
|
|
625
|
+
$and?: TypedFilter<T>[];
|
|
626
|
+
/** Joins clauses with a logical OR. Matches documents that satisfy at least one filter. */
|
|
627
|
+
$or?: TypedFilter<T>[];
|
|
628
|
+
/** Joins clauses with a logical NOR. Matches documents that fail all filters. */
|
|
629
|
+
$nor?: TypedFilter<T>[];
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Matches values equal to the specified value.
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* // Explicit equality (equivalent to { name: 'Alice' })
|
|
638
|
+
* users.find({ name: $eq('Alice') })
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
declare const $eq: <V>(value: V) => {
|
|
642
|
+
$eq: V;
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Matches values not equal to the specified value.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* ```ts
|
|
649
|
+
* users.find({ role: $ne('banned') })
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
declare const $ne: <V>(value: V) => {
|
|
653
|
+
$ne: V;
|
|
654
|
+
};
|
|
655
|
+
/**
|
|
656
|
+
* Matches values greater than the specified value.
|
|
657
|
+
*
|
|
658
|
+
* @example
|
|
659
|
+
* ```ts
|
|
660
|
+
* users.find({ age: $gt(18) })
|
|
661
|
+
* ```
|
|
662
|
+
*/
|
|
663
|
+
declare const $gt: <V>(value: V) => {
|
|
664
|
+
$gt: V;
|
|
665
|
+
};
|
|
666
|
+
/**
|
|
667
|
+
* Matches values greater than or equal to the specified value.
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```ts
|
|
671
|
+
* users.find({ age: $gte(18) })
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
declare const $gte: <V>(value: V) => {
|
|
675
|
+
$gte: V;
|
|
676
|
+
};
|
|
677
|
+
/**
|
|
678
|
+
* Matches values less than the specified value.
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```ts
|
|
682
|
+
* users.find({ age: $lt(65) })
|
|
683
|
+
* ```
|
|
684
|
+
*/
|
|
685
|
+
declare const $lt: <V>(value: V) => {
|
|
686
|
+
$lt: V;
|
|
687
|
+
};
|
|
688
|
+
/**
|
|
689
|
+
* Matches values less than or equal to the specified value.
|
|
690
|
+
*
|
|
691
|
+
* @example
|
|
692
|
+
* ```ts
|
|
693
|
+
* users.find({ age: $lte(65) })
|
|
694
|
+
* ```
|
|
695
|
+
*/
|
|
696
|
+
declare const $lte: <V>(value: V) => {
|
|
697
|
+
$lte: V;
|
|
698
|
+
};
|
|
699
|
+
/**
|
|
700
|
+
* Matches any value in the specified array.
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* ```ts
|
|
704
|
+
* users.find({ role: $in(['admin', 'moderator']) })
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
declare const $in: <V>(values: V[]) => {
|
|
708
|
+
$in: V[];
|
|
709
|
+
};
|
|
710
|
+
/**
|
|
711
|
+
* Matches none of the values in the specified array.
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* users.find({ role: $nin(['banned', 'suspended']) })
|
|
716
|
+
* ```
|
|
717
|
+
*/
|
|
718
|
+
declare const $nin: <V>(values: V[]) => {
|
|
719
|
+
$nin: V[];
|
|
720
|
+
};
|
|
721
|
+
/**
|
|
722
|
+
* Matches documents where the field exists (or does not exist).
|
|
723
|
+
* Defaults to `true` when called with no arguments.
|
|
724
|
+
*
|
|
725
|
+
* @example
|
|
726
|
+
* ```ts
|
|
727
|
+
* // Field must exist
|
|
728
|
+
* users.find({ email: $exists() })
|
|
729
|
+
*
|
|
730
|
+
* // Field must not exist
|
|
731
|
+
* users.find({ deletedAt: $exists(false) })
|
|
732
|
+
* ```
|
|
733
|
+
*/
|
|
734
|
+
declare const $exists: (flag?: boolean) => {
|
|
735
|
+
$exists: boolean;
|
|
736
|
+
};
|
|
737
|
+
/**
|
|
738
|
+
* Matches string values against a regular expression pattern.
|
|
739
|
+
* Only valid on string fields.
|
|
740
|
+
*
|
|
741
|
+
* @example
|
|
742
|
+
* ```ts
|
|
743
|
+
* users.find({ name: $regex(/^A/i) })
|
|
744
|
+
* users.find({ email: $regex('^admin@') })
|
|
745
|
+
* ```
|
|
746
|
+
*/
|
|
747
|
+
declare const $regex: (pattern: RegExp | string) => {
|
|
748
|
+
$regex: RegExp | string;
|
|
749
|
+
};
|
|
750
|
+
/**
|
|
751
|
+
* Negates a comparison operator. Wraps the given operator object
|
|
752
|
+
* in a `$not` condition.
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* ```ts
|
|
756
|
+
* // Age is NOT greater than 65
|
|
757
|
+
* users.find({ age: $not($gt(65)) })
|
|
758
|
+
*
|
|
759
|
+
* // Name does NOT match pattern
|
|
760
|
+
* users.find({ name: $not($regex(/^test/)) })
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
declare const $not: <O extends Record<string, unknown>>(op: O) => {
|
|
764
|
+
$not: O;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* Joins filter clauses with a logical OR. Matches documents that satisfy
|
|
768
|
+
* at least one of the provided filters.
|
|
769
|
+
*
|
|
770
|
+
* @example
|
|
771
|
+
* ```ts
|
|
772
|
+
* users.find($or({ role: 'admin' }, { age: $gte(18) }))
|
|
773
|
+
*
|
|
774
|
+
* // Dynamic composition
|
|
775
|
+
* const conditions: TypedFilter<User>[] = []
|
|
776
|
+
* if (name) conditions.push({ name })
|
|
777
|
+
* if (role) conditions.push({ role })
|
|
778
|
+
* users.find($or(...conditions))
|
|
779
|
+
* ```
|
|
780
|
+
*/
|
|
781
|
+
declare const $or: <T>(...filters: TypedFilter<T>[]) => TypedFilter<T>;
|
|
782
|
+
/**
|
|
783
|
+
* Joins filter clauses with a logical AND. Matches documents that satisfy
|
|
784
|
+
* all of the provided filters. Useful for dynamic filter building where
|
|
785
|
+
* multiple conditions on the same field would conflict in an object literal.
|
|
786
|
+
*
|
|
787
|
+
* @example
|
|
788
|
+
* ```ts
|
|
789
|
+
* users.find($and(
|
|
790
|
+
* $or({ role: 'admin' }, { role: 'moderator' }),
|
|
791
|
+
* { age: $gte(18) },
|
|
792
|
+
* { email: $exists() },
|
|
793
|
+
* ))
|
|
794
|
+
* ```
|
|
795
|
+
*/
|
|
796
|
+
declare const $and: <T>(...filters: TypedFilter<T>[]) => TypedFilter<T>;
|
|
797
|
+
/**
|
|
798
|
+
* Joins filter clauses with a logical NOR. Matches documents that fail
|
|
799
|
+
* all of the provided filters.
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```ts
|
|
803
|
+
* // Exclude banned and suspended users
|
|
804
|
+
* users.find($nor({ role: 'banned' }, { role: 'suspended' }))
|
|
805
|
+
* ```
|
|
806
|
+
*/
|
|
807
|
+
declare const $nor: <T>(...filters: TypedFilter<T>[]) => TypedFilter<T>;
|
|
808
|
+
/**
|
|
809
|
+
* Escape hatch for unsupported or raw MongoDB filter operators.
|
|
810
|
+
* Wraps an untyped filter object so it can be passed where `TypedFilter<T>` is expected.
|
|
811
|
+
* Use when you need operators not covered by the type system (e.g., `$text`, `$geoNear`).
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* ```ts
|
|
815
|
+
* users.find(raw({ $text: { $search: 'mongodb tutorial' } }))
|
|
816
|
+
* ```
|
|
817
|
+
*/
|
|
818
|
+
declare const raw: <T = any>(filter: Record<string, unknown>) => TypedFilter<T>;
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Type-level marker that carries the target collection type through the
|
|
822
|
+
* type system. Intersected with the schema return type by `.ref()` so
|
|
823
|
+
* that `RefFields<T>` (future) can extract ref relationships.
|
|
824
|
+
*
|
|
825
|
+
* This is a phantom brand — no runtime value has this property.
|
|
826
|
+
*/
|
|
827
|
+
type RefMarker<TCollection extends AnyCollection = AnyCollection> = {
|
|
828
|
+
readonly _ref: TCollection;
|
|
829
|
+
};
|
|
830
|
+
/**
|
|
831
|
+
* Metadata stored in the WeakMap sidecar for schemas marked with `.ref()`.
|
|
832
|
+
* Holds a reference to the target collection definition object.
|
|
833
|
+
*/
|
|
834
|
+
type RefMetadata = {
|
|
835
|
+
readonly collection: AnyCollection;
|
|
836
|
+
};
|
|
837
|
+
/**
|
|
838
|
+
* Module augmentation: adds `.ref()` to all `ZodType` schemas.
|
|
839
|
+
*
|
|
840
|
+
* The intersection constraint `this['_zod']['output'] extends InferDocument<TCollection>['_id']`
|
|
841
|
+
* ensures compile-time type safety: the field's output type must match the
|
|
842
|
+
* target collection's `_id` type. Mismatches produce a type error.
|
|
843
|
+
*
|
|
844
|
+
* Supports both default ObjectId `_id` and custom `_id` types (string, nanoid, etc.):
|
|
845
|
+
* - `objectId().ref(Users)` compiles when Users has ObjectId `_id`
|
|
846
|
+
* - `z.string().ref(Orgs)` compiles when Orgs has string `_id`
|
|
847
|
+
* - `z.string().ref(Users)` is a type error (string ≠ ObjectId)
|
|
848
|
+
* - `objectId().ref(Orgs)` is a type error (ObjectId ≠ string)
|
|
849
|
+
*/
|
|
850
|
+
declare module 'zod' {
|
|
851
|
+
interface ZodType {
|
|
852
|
+
/**
|
|
853
|
+
* Declare a typed foreign key reference to another collection.
|
|
854
|
+
*
|
|
855
|
+
* Stores the target collection definition in metadata for runtime
|
|
856
|
+
* populate resolution, and brands the return type with
|
|
857
|
+
* `RefMarker<TCollection>` so `RefFields<T>` can extract refs
|
|
858
|
+
* at the type level.
|
|
859
|
+
*
|
|
860
|
+
* The field's output type must match the target collection's `_id` type.
|
|
861
|
+
* Mismatched types produce a compile error.
|
|
862
|
+
*
|
|
863
|
+
* Apply `.ref()` before wrapper methods like `.optional()` or `.nullable()`:
|
|
864
|
+
* `objectId().ref(Users).optional()` — not `objectId().optional().ref(Users)`.
|
|
865
|
+
*
|
|
866
|
+
* @param collection - The target collection definition object.
|
|
867
|
+
* @returns The same schema instance, branded with the ref marker.
|
|
868
|
+
*
|
|
869
|
+
* @example
|
|
870
|
+
* ```ts
|
|
871
|
+
* const Posts = collection('posts', {
|
|
872
|
+
* authorId: objectId().ref(Users),
|
|
873
|
+
* title: z.string(),
|
|
874
|
+
* })
|
|
875
|
+
* ```
|
|
876
|
+
*/
|
|
877
|
+
ref<TCollection extends AnyCollection>(collection: TCollection & (this['_zod']['output'] extends InferDocument<TCollection>['_id'] ? unknown : never)): this & RefMarker<TCollection>;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Retrieve the ref metadata attached to a Zod schema, if any.
|
|
882
|
+
*
|
|
883
|
+
* Returns `undefined` when the schema was never marked with `.ref()`.
|
|
884
|
+
*
|
|
885
|
+
* @param schema - The Zod schema to inspect. Accepts `unknown` for
|
|
886
|
+
* convenience; non-object values safely return `undefined`.
|
|
887
|
+
* @returns The {@link RefMetadata} for the schema, or `undefined`.
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```ts
|
|
891
|
+
* const authorId = objectId().ref(Users)
|
|
892
|
+
* const meta = getRefMetadata(authorId)
|
|
893
|
+
* // => { collection: Users }
|
|
894
|
+
* ```
|
|
895
|
+
*/
|
|
896
|
+
declare function getRefMetadata(schema: unknown): RefMetadata | undefined;
|
|
897
|
+
/**
|
|
898
|
+
* Install the `.ref()` extension method on `ZodType.prototype`.
|
|
899
|
+
*
|
|
900
|
+
* Idempotent — safe to call multiple times.
|
|
901
|
+
*/
|
|
902
|
+
declare function installRefExtension(): void;
|
|
11
903
|
|
|
12
|
-
export { type ZodObjectId, isOid, objectId, oid };
|
|
904
|
+
export { $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AnyCollection, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type RefMarker, type RefMetadata, type ResolvedShape, type TypedFilter, type ZodObjectId, collection, createClient, extractDbName, extractFieldIndexes, getIndexMetadata, getRefMetadata, index, installExtensions, installRefExtension, isOid, objectId, oid, raw };
|