@zodmon/core 0.7.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 +316 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +439 -15
- package/dist/index.d.ts +439 -15
- package/dist/index.js +309 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -252,17 +252,132 @@ type InferInsert<TDef extends {
|
|
|
252
252
|
/**
|
|
253
253
|
* The immutable definition object returned by collection().
|
|
254
254
|
* Holds everything needed to later create a live collection handle.
|
|
255
|
+
*
|
|
256
|
+
* @typeParam TShape - The Zod shape defining document fields.
|
|
257
|
+
* @typeParam TIndexes - The compound indexes array type, preserving literal name types.
|
|
255
258
|
*/
|
|
256
|
-
type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape> = {
|
|
259
|
+
type CollectionDefinition<TShape extends z.core.$ZodShape = z.core.$ZodShape, TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]> = {
|
|
257
260
|
readonly name: string;
|
|
258
261
|
readonly schema: z.ZodObject<ResolvedShape<TShape>>;
|
|
259
262
|
readonly shape: TShape;
|
|
260
263
|
readonly fieldIndexes: FieldIndexDefinition[];
|
|
261
|
-
readonly compoundIndexes:
|
|
264
|
+
readonly compoundIndexes: TIndexes;
|
|
262
265
|
readonly options: Required<Pick<CollectionOptions, 'validation'>> & Omit<CollectionOptions, 'indexes' | 'validation'>;
|
|
263
266
|
};
|
|
264
267
|
/** Erased collection type for use in generic contexts. */
|
|
265
268
|
type AnyCollection = CollectionDefinition<z.core.$ZodShape>;
|
|
269
|
+
/**
|
|
270
|
+
* Extract declared index names from a collection definition.
|
|
271
|
+
*
|
|
272
|
+
* Walks the `compoundIndexes` tuple and extracts the literal `name` string
|
|
273
|
+
* from each index that declared one via `.name()`. Falls back to `string`
|
|
274
|
+
* when no compound indexes have names, allowing any string as a hint.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const Users = collection('users', { email: z.string() }, {
|
|
279
|
+
* indexes: [
|
|
280
|
+
* index({ email: 1 }).name('email_idx'),
|
|
281
|
+
* ],
|
|
282
|
+
* })
|
|
283
|
+
* type Names = IndexNames<typeof Users> // 'email_idx'
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
type IndexNames<TDef extends {
|
|
287
|
+
readonly compoundIndexes: readonly {
|
|
288
|
+
options?: {
|
|
289
|
+
name?: string;
|
|
290
|
+
};
|
|
291
|
+
}[];
|
|
292
|
+
}> = ExtractNames<TDef['compoundIndexes']> extends never ? string : ExtractNames<TDef['compoundIndexes']>;
|
|
293
|
+
/** @internal Helper that extracts the `name` literal from each index in a tuple. */
|
|
294
|
+
type ExtractNames<T extends readonly {
|
|
295
|
+
options?: {
|
|
296
|
+
name?: string;
|
|
297
|
+
};
|
|
298
|
+
}[]> = {
|
|
299
|
+
[K in keyof T]: T[K] extends {
|
|
300
|
+
readonly options: {
|
|
301
|
+
readonly name: infer N;
|
|
302
|
+
};
|
|
303
|
+
} ? N extends string ? N : never : never;
|
|
304
|
+
}[number];
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Options controlling how {@link syncIndexes} behaves.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* await users.syncIndexes({ dryRun: true })
|
|
312
|
+
* await users.syncIndexes({ dropOrphaned: true })
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
type SyncIndexesOptions = {
|
|
316
|
+
/**
|
|
317
|
+
* When `true`, compute the diff without actually creating, dropping, or
|
|
318
|
+
* modifying any indexes. The returned {@link SyncIndexesResult} shows what
|
|
319
|
+
* *would* happen.
|
|
320
|
+
*/
|
|
321
|
+
dryRun?: boolean;
|
|
322
|
+
/**
|
|
323
|
+
* When `true`, drop indexes that exist in MongoDB but are not declared in
|
|
324
|
+
* the schema. Also drops and recreates stale indexes (same key, different
|
|
325
|
+
* options). When `false` (the default), orphaned and stale indexes are
|
|
326
|
+
* reported but left untouched.
|
|
327
|
+
*/
|
|
328
|
+
dropOrphaned?: boolean;
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Describes an index whose key matches a desired index but whose options differ.
|
|
332
|
+
*
|
|
333
|
+
* Returned in {@link SyncIndexesResult.stale} so the caller can decide whether
|
|
334
|
+
* to manually reconcile or re-run with `dropOrphaned: true`.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* const result = await users.syncIndexes()
|
|
339
|
+
* for (const s of result.stale) {
|
|
340
|
+
* console.log(`${s.name}: key=${JSON.stringify(s.key)}`)
|
|
341
|
+
* console.log(` existing=${JSON.stringify(s.existing)}`)
|
|
342
|
+
* console.log(` desired=${JSON.stringify(s.desired)}`)
|
|
343
|
+
* }
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
type StaleIndex = {
|
|
347
|
+
/** The MongoDB index name (e.g. `'email_1'`). */
|
|
348
|
+
name: string;
|
|
349
|
+
/** The index key spec (e.g. `{ email: 1 }`). */
|
|
350
|
+
key: Record<string, 1 | -1 | 'text'>;
|
|
351
|
+
/** The relevant options currently set on the existing index. */
|
|
352
|
+
existing: Record<string, unknown>;
|
|
353
|
+
/** The options the schema declares for this index. */
|
|
354
|
+
desired: Record<string, unknown>;
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* The result of a {@link syncIndexes} call.
|
|
358
|
+
*
|
|
359
|
+
* Every array contains index names. `stale` contains full details so the
|
|
360
|
+
* caller can inspect the mismatch.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* const result = await users.syncIndexes()
|
|
365
|
+
* console.log('created:', result.created)
|
|
366
|
+
* console.log('dropped:', result.dropped)
|
|
367
|
+
* console.log('skipped:', result.skipped)
|
|
368
|
+
* console.log('stale:', result.stale.map(s => s.name))
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
type SyncIndexesResult = {
|
|
372
|
+
/** Names of indexes that were created (or would be created in dryRun mode). */
|
|
373
|
+
created: string[];
|
|
374
|
+
/** Names of indexes that were dropped (or would be dropped in dryRun mode). */
|
|
375
|
+
dropped: string[];
|
|
376
|
+
/** Names of indexes that already existed with matching options — no action taken. */
|
|
377
|
+
skipped: string[];
|
|
378
|
+
/** Indexes whose key matches a desired spec but whose options differ. */
|
|
379
|
+
stale: StaleIndex[];
|
|
380
|
+
};
|
|
266
381
|
|
|
267
382
|
/**
|
|
268
383
|
* Comparison operators for a field value of type `V`.
|
|
@@ -554,7 +669,7 @@ type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
|
|
|
554
669
|
/**
|
|
555
670
|
* Type-safe cursor wrapping MongoDB's `FindCursor`.
|
|
556
671
|
*
|
|
557
|
-
* Provides chainable query modifiers (`sort`, `skip`, `limit`) that return
|
|
672
|
+
* Provides chainable query modifiers (`sort`, `skip`, `limit`, `hint`) that return
|
|
558
673
|
* `this` for fluent chaining, and terminal methods (`toArray`,
|
|
559
674
|
* `[Symbol.asyncIterator]`) that validate each document against the
|
|
560
675
|
* collection's Zod schema before returning.
|
|
@@ -562,6 +677,7 @@ type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
|
|
|
562
677
|
* Created by {@link find} — do not construct directly.
|
|
563
678
|
*
|
|
564
679
|
* @typeParam TDef - The collection definition type, used to infer the document type.
|
|
680
|
+
* @typeParam TIndexNames - Union of declared index names accepted by `.hint()`.
|
|
565
681
|
*
|
|
566
682
|
* @example
|
|
567
683
|
* ```ts
|
|
@@ -571,7 +687,7 @@ type TypedSort<T> = Partial<Record<keyof T & string, 1 | -1>>;
|
|
|
571
687
|
* .toArray()
|
|
572
688
|
* ```
|
|
573
689
|
*/
|
|
574
|
-
declare class TypedFindCursor<TDef extends AnyCollection> {
|
|
690
|
+
declare class TypedFindCursor<TDef extends AnyCollection, TIndexNames extends string = string> {
|
|
575
691
|
/** @internal */
|
|
576
692
|
private cursor;
|
|
577
693
|
/** @internal */
|
|
@@ -627,6 +743,26 @@ declare class TypedFindCursor<TDef extends AnyCollection> {
|
|
|
627
743
|
* ```
|
|
628
744
|
*/
|
|
629
745
|
limit(n: number): this;
|
|
746
|
+
/**
|
|
747
|
+
* Force the query optimizer to use the specified index.
|
|
748
|
+
*
|
|
749
|
+
* Only accepts index names that were declared via `.name()` in the
|
|
750
|
+
* collection definition. If no named indexes exist, any string is accepted.
|
|
751
|
+
*
|
|
752
|
+
* @param indexName - The name of a declared compound index.
|
|
753
|
+
* @returns `this` for chaining.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* ```ts
|
|
757
|
+
* const Users = collection('users', { email: z.string(), role: z.string() }, {
|
|
758
|
+
* indexes: [index({ email: 1, role: -1 }).name('email_role_idx')],
|
|
759
|
+
* })
|
|
760
|
+
* const admins = await users.find({ role: 'admin' })
|
|
761
|
+
* .hint('email_role_idx')
|
|
762
|
+
* .toArray()
|
|
763
|
+
* ```
|
|
764
|
+
*/
|
|
765
|
+
hint(indexName: TIndexNames): this;
|
|
630
766
|
/**
|
|
631
767
|
* Execute the query with offset-based pagination, returning a page of documents
|
|
632
768
|
* with total count and navigation metadata.
|
|
@@ -794,7 +930,7 @@ type FindOptions = {
|
|
|
794
930
|
* }
|
|
795
931
|
* ```
|
|
796
932
|
*/
|
|
797
|
-
declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef
|
|
933
|
+
declare function find<TDef extends AnyCollection>(handle: CollectionHandle<TDef>, filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
798
934
|
|
|
799
935
|
/**
|
|
800
936
|
* Extracts the element type from an array type.
|
|
@@ -1220,7 +1356,7 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1220
1356
|
* .toArray()
|
|
1221
1357
|
* ```
|
|
1222
1358
|
*/
|
|
1223
|
-
find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef
|
|
1359
|
+
find(filter: TypedFilter<InferDocument<TDef>>, options?: FindOptions): TypedFindCursor<TDef, IndexNames<TDef>>;
|
|
1224
1360
|
/**
|
|
1225
1361
|
* Update a single document matching the filter.
|
|
1226
1362
|
*
|
|
@@ -1342,6 +1478,34 @@ declare class CollectionHandle<TDef extends AnyCollection = AnyCollection> {
|
|
|
1342
1478
|
* ```
|
|
1343
1479
|
*/
|
|
1344
1480
|
findOneAndDelete(filter: TypedFilter<InferDocument<TDef>>, options?: FindOneAndDeleteOptions): Promise<InferDocument<TDef> | null>;
|
|
1481
|
+
/**
|
|
1482
|
+
* Synchronize the indexes declared in this collection's schema with MongoDB.
|
|
1483
|
+
*
|
|
1484
|
+
* Compares the desired indexes (from field-level `.index()` / `.unique()` /
|
|
1485
|
+
* `.text()` / `.expireAfter()` and compound `indexes` in collection options)
|
|
1486
|
+
* with the indexes that currently exist in MongoDB, then creates, drops, or
|
|
1487
|
+
* reports differences depending on the options.
|
|
1488
|
+
*
|
|
1489
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
1490
|
+
* @returns A summary of created, dropped, skipped, and stale indexes.
|
|
1491
|
+
*
|
|
1492
|
+
* @example
|
|
1493
|
+
* ```ts
|
|
1494
|
+
* const users = db.use(Users)
|
|
1495
|
+
* const result = await users.syncIndexes()
|
|
1496
|
+
* console.log('Created:', result.created)
|
|
1497
|
+
* console.log('Stale:', result.stale.map(s => s.name))
|
|
1498
|
+
* ```
|
|
1499
|
+
*
|
|
1500
|
+
* @example
|
|
1501
|
+
* ```ts
|
|
1502
|
+
* // Dry run to preview changes without modifying the database
|
|
1503
|
+
* const diff = await users.syncIndexes({ dryRun: true })
|
|
1504
|
+
* console.log('Would create:', diff.created)
|
|
1505
|
+
* console.log('Would drop:', diff.dropped)
|
|
1506
|
+
* ```
|
|
1507
|
+
*/
|
|
1508
|
+
syncIndexes(options?: SyncIndexesOptions): Promise<SyncIndexesResult>;
|
|
1345
1509
|
}
|
|
1346
1510
|
|
|
1347
1511
|
/**
|
|
@@ -1376,13 +1540,28 @@ declare class Database {
|
|
|
1376
1540
|
* @param def - A collection definition created by `collection()`.
|
|
1377
1541
|
* @returns A typed collection handle for CRUD operations.
|
|
1378
1542
|
*/
|
|
1379
|
-
use<TShape extends z.core.$ZodShape>(def: CollectionDefinition<TShape>): CollectionHandle<CollectionDefinition<TShape>>;
|
|
1543
|
+
use<TShape extends z.core.$ZodShape, TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]>(def: CollectionDefinition<TShape, TIndexes>): CollectionHandle<CollectionDefinition<TShape, TIndexes>>;
|
|
1380
1544
|
/**
|
|
1381
|
-
* Synchronize indexes
|
|
1545
|
+
* Synchronize indexes for all registered collections with MongoDB.
|
|
1546
|
+
*
|
|
1547
|
+
* Iterates every collection registered via {@link use} and calls
|
|
1548
|
+
* {@link syncIndexes} on each one. Returns a record keyed by collection
|
|
1549
|
+
* name with the sync result for each.
|
|
1550
|
+
*
|
|
1551
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
1552
|
+
* @returns A record mapping collection names to their sync results.
|
|
1382
1553
|
*
|
|
1383
|
-
*
|
|
1554
|
+
* @example
|
|
1555
|
+
* ```ts
|
|
1556
|
+
* const db = createClient('mongodb://localhost:27017', 'myapp')
|
|
1557
|
+
* db.use(Users)
|
|
1558
|
+
* db.use(Posts)
|
|
1559
|
+
* const results = await db.syncIndexes()
|
|
1560
|
+
* console.log(results['users'].created) // ['email_1']
|
|
1561
|
+
* console.log(results['posts'].created) // ['title_1']
|
|
1562
|
+
* ```
|
|
1384
1563
|
*/
|
|
1385
|
-
syncIndexes(): Promise<
|
|
1564
|
+
syncIndexes(options?: SyncIndexesOptions): Promise<Record<string, SyncIndexesResult>>;
|
|
1386
1565
|
/**
|
|
1387
1566
|
* Execute a function within a MongoDB transaction with auto-commit/rollback.
|
|
1388
1567
|
*
|
|
@@ -1457,7 +1636,9 @@ declare function extractFieldIndexes(shape: z.core.$ZodShape): FieldIndexDefinit
|
|
|
1457
1636
|
* })
|
|
1458
1637
|
* ```
|
|
1459
1638
|
*/
|
|
1460
|
-
declare function collection<TShape extends z.core.$ZodShape>(name: string, shape: TShape, options?: CollectionOptions<Extract<keyof TShape, string
|
|
1639
|
+
declare function collection<TShape extends z.core.$ZodShape, const TIndexes extends readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[] = readonly CompoundIndexDefinition<Extract<keyof TShape, string>>[]>(name: string, shape: TShape, options?: Omit<CollectionOptions<Extract<keyof TShape, string>>, 'indexes'> & {
|
|
1640
|
+
indexes?: TIndexes;
|
|
1641
|
+
}): CollectionDefinition<TShape, [...TIndexes]>;
|
|
1461
1642
|
|
|
1462
1643
|
type IndexDirection = 1 | -1;
|
|
1463
1644
|
type CompoundIndexOptions = NonNullable<CompoundIndexDefinition['options']>;
|
|
@@ -1485,9 +1666,27 @@ declare class IndexBuilder<TKeys extends string> {
|
|
|
1485
1666
|
readonly options: CompoundIndexOptions;
|
|
1486
1667
|
constructor(fields: Record<TKeys, IndexDirection>);
|
|
1487
1668
|
private _clone;
|
|
1488
|
-
unique():
|
|
1489
|
-
sparse():
|
|
1490
|
-
|
|
1669
|
+
unique(): this;
|
|
1670
|
+
sparse(): this;
|
|
1671
|
+
/**
|
|
1672
|
+
* Set a custom name for this index, preserving the literal type.
|
|
1673
|
+
*
|
|
1674
|
+
* The returned builder carries the literal name type via an intersection,
|
|
1675
|
+
* enabling type-safe `.hint()` on cursors that only accepts declared names.
|
|
1676
|
+
*
|
|
1677
|
+
* @param name - The index name.
|
|
1678
|
+
* @returns A new IndexBuilder with the name recorded at the type level.
|
|
1679
|
+
*
|
|
1680
|
+
* @example
|
|
1681
|
+
* ```ts
|
|
1682
|
+
* index({ email: 1, role: -1 }).name('email_role_idx')
|
|
1683
|
+
* ```
|
|
1684
|
+
*/
|
|
1685
|
+
name<TName extends string>(name: TName): IndexBuilder<TKeys> & {
|
|
1686
|
+
readonly options: {
|
|
1687
|
+
readonly name: TName;
|
|
1688
|
+
};
|
|
1689
|
+
};
|
|
1491
1690
|
}
|
|
1492
1691
|
/**
|
|
1493
1692
|
* Create a compound index definition with a fluent builder API.
|
|
@@ -1652,6 +1851,231 @@ declare function oid(value: ObjectId): ObjectId;
|
|
|
1652
1851
|
*/
|
|
1653
1852
|
declare function isOid(value: unknown): value is ObjectId;
|
|
1654
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* A normalized index specification ready for comparison and creation.
|
|
1856
|
+
*
|
|
1857
|
+
* `key` maps field paths to direction (`1`, `-1`, or `'text'`).
|
|
1858
|
+
* `options` holds MongoDB index options (`unique`, `sparse`, etc.).
|
|
1859
|
+
*
|
|
1860
|
+
* @example
|
|
1861
|
+
* ```ts
|
|
1862
|
+
* const spec: IndexSpec = {
|
|
1863
|
+
* key: { email: 1 },
|
|
1864
|
+
* options: { unique: true },
|
|
1865
|
+
* }
|
|
1866
|
+
* ```
|
|
1867
|
+
*/
|
|
1868
|
+
type IndexSpec = {
|
|
1869
|
+
key: Record<string, 1 | -1 | 'text'>;
|
|
1870
|
+
options: Record<string, unknown>;
|
|
1871
|
+
};
|
|
1872
|
+
/**
|
|
1873
|
+
* Convert a field-level index definition to a normalized {@link IndexSpec}.
|
|
1874
|
+
*
|
|
1875
|
+
* Maps schema metadata (`text`, `descending`, `unique`, `sparse`, `expireAfter`,
|
|
1876
|
+
* `partial`) to their MongoDB driver equivalents.
|
|
1877
|
+
*
|
|
1878
|
+
* @param def - A field index definition extracted from schema metadata.
|
|
1879
|
+
* @returns A normalized index spec with key and options.
|
|
1880
|
+
*
|
|
1881
|
+
* @example
|
|
1882
|
+
* ```ts
|
|
1883
|
+
* const spec = toFieldIndexSpec({ field: 'email', indexed: true, unique: true })
|
|
1884
|
+
* // => { key: { email: 1 }, options: { unique: true } }
|
|
1885
|
+
*
|
|
1886
|
+
* const ttl = toFieldIndexSpec({ field: 'expiresAt', indexed: true, expireAfter: 3600 })
|
|
1887
|
+
* // => { key: { expiresAt: 1 }, options: { expireAfterSeconds: 3600 } }
|
|
1888
|
+
* ```
|
|
1889
|
+
*/
|
|
1890
|
+
declare function toFieldIndexSpec(def: FieldIndexDefinition): IndexSpec;
|
|
1891
|
+
/**
|
|
1892
|
+
* Convert a compound index definition to a normalized {@link IndexSpec}.
|
|
1893
|
+
*
|
|
1894
|
+
* Copies the `fields` map directly to `key` and maps builder options
|
|
1895
|
+
* (`unique`, `sparse`, `name`, `partial`) to MongoDB driver equivalents.
|
|
1896
|
+
*
|
|
1897
|
+
* @param def - A compound index definition from the collection options.
|
|
1898
|
+
* @returns A normalized index spec with key and options.
|
|
1899
|
+
*
|
|
1900
|
+
* @example
|
|
1901
|
+
* ```ts
|
|
1902
|
+
* const spec = toCompoundIndexSpec({
|
|
1903
|
+
* fields: { email: 1, role: -1 },
|
|
1904
|
+
* options: { unique: true, name: 'email_role_idx' },
|
|
1905
|
+
* })
|
|
1906
|
+
* // => { key: { email: 1, role: -1 }, options: { unique: true, name: 'email_role_idx' } }
|
|
1907
|
+
* ```
|
|
1908
|
+
*/
|
|
1909
|
+
declare function toCompoundIndexSpec(def: CompoundIndexDefinition): IndexSpec;
|
|
1910
|
+
/**
|
|
1911
|
+
* Produce a stable, deterministic string from an index key for comparison.
|
|
1912
|
+
*
|
|
1913
|
+
* Entries are sorted alphabetically by field name and formatted as
|
|
1914
|
+
* `field:direction` pairs joined by commas. Two index keys that should be
|
|
1915
|
+
* considered the same will always produce the same string.
|
|
1916
|
+
*
|
|
1917
|
+
* @param key - An index key mapping field names to direction.
|
|
1918
|
+
* @returns A string like `'email:1,role:-1'`.
|
|
1919
|
+
*
|
|
1920
|
+
* @example
|
|
1921
|
+
* ```ts
|
|
1922
|
+
* serializeIndexKey({ email: 1, role: -1 })
|
|
1923
|
+
* // => 'email:1,role:-1'
|
|
1924
|
+
*
|
|
1925
|
+
* serializeIndexKey({ name: 'text' })
|
|
1926
|
+
* // => 'name:text'
|
|
1927
|
+
* ```
|
|
1928
|
+
*/
|
|
1929
|
+
declare function serializeIndexKey(key: Record<string, 1 | -1 | 'text'>): string;
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Structural constraint for the handle argument of {@link syncIndexes}.
|
|
1933
|
+
*
|
|
1934
|
+
* Uses a structural type rather than `CollectionHandle<AnyCollection>` to avoid
|
|
1935
|
+
* TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection
|
|
1936
|
+
* handle returned by `db.use()` satisfies this constraint.
|
|
1937
|
+
*/
|
|
1938
|
+
type SyncableHandle = {
|
|
1939
|
+
readonly definition: {
|
|
1940
|
+
readonly fieldIndexes: FieldIndexDefinition[];
|
|
1941
|
+
readonly compoundIndexes: readonly CompoundIndexDefinition[];
|
|
1942
|
+
};
|
|
1943
|
+
readonly native: Collection<any>;
|
|
1944
|
+
};
|
|
1945
|
+
/**
|
|
1946
|
+
* Extract the comparable options from a MongoDB index info object.
|
|
1947
|
+
*
|
|
1948
|
+
* Pulls only the keys listed in {@link COMPARABLE_OPTION_KEYS} from the raw
|
|
1949
|
+
* index info returned by `listIndexes()`. Keys whose value is `undefined`
|
|
1950
|
+
* are omitted so that JSON comparison works correctly.
|
|
1951
|
+
*
|
|
1952
|
+
* @param info - A raw MongoDB index info object.
|
|
1953
|
+
* @returns A plain object with only the relevant option keys.
|
|
1954
|
+
*
|
|
1955
|
+
* @example
|
|
1956
|
+
* ```ts
|
|
1957
|
+
* const opts = extractComparableOptions({ v: 2, unique: true, key: { email: 1 } })
|
|
1958
|
+
* // => { unique: true }
|
|
1959
|
+
* ```
|
|
1960
|
+
*/
|
|
1961
|
+
declare function extractComparableOptions(info: Record<string, unknown>): Record<string, unknown>;
|
|
1962
|
+
/**
|
|
1963
|
+
* Generate the default MongoDB index name from a key spec.
|
|
1964
|
+
*
|
|
1965
|
+
* MongoDB names indexes by joining `field_direction` pairs with underscores.
|
|
1966
|
+
* For example, `{ email: 1, role: -1 }` becomes `'email_1_role_-1'`.
|
|
1967
|
+
*
|
|
1968
|
+
* @param key - An index key mapping field names to direction.
|
|
1969
|
+
* @returns The generated index name string.
|
|
1970
|
+
*
|
|
1971
|
+
* @example
|
|
1972
|
+
* ```ts
|
|
1973
|
+
* generateIndexName({ email: 1 })
|
|
1974
|
+
* // => 'email_1'
|
|
1975
|
+
*
|
|
1976
|
+
* generateIndexName({ email: 1, role: -1 })
|
|
1977
|
+
* // => 'email_1_role_-1'
|
|
1978
|
+
* ```
|
|
1979
|
+
*/
|
|
1980
|
+
declare function generateIndexName(key: Record<string, 1 | -1 | 'text'>): string;
|
|
1981
|
+
/**
|
|
1982
|
+
* Synchronize the indexes declared in a collection's schema with MongoDB.
|
|
1983
|
+
*
|
|
1984
|
+
* Compares the desired indexes (from field-level and compound index definitions)
|
|
1985
|
+
* with the indexes that currently exist in MongoDB, then creates, drops, or
|
|
1986
|
+
* reports differences depending on the options.
|
|
1987
|
+
*
|
|
1988
|
+
* **Algorithm:**
|
|
1989
|
+
* 1. Build desired specs from field indexes and compound indexes.
|
|
1990
|
+
* 2. Fetch existing indexes from MongoDB via `listIndexes()`.
|
|
1991
|
+
* 3. For each desired spec, compare against existing:
|
|
1992
|
+
* - Missing → create (unless `dryRun`).
|
|
1993
|
+
* - Present with same options → skip.
|
|
1994
|
+
* - Present with different options → stale (or drop+recreate if `dropOrphaned`).
|
|
1995
|
+
* 4. For each existing index not in desired set (excluding `_id_`):
|
|
1996
|
+
* - If `dropOrphaned` → drop (unless `dryRun`).
|
|
1997
|
+
* 5. Return a summary of all actions taken.
|
|
1998
|
+
*
|
|
1999
|
+
* @param handle - A collection handle created by `db.use()`.
|
|
2000
|
+
* @param options - Optional sync behavior (dryRun, dropOrphaned).
|
|
2001
|
+
* @returns A summary of created, dropped, skipped, and stale indexes.
|
|
2002
|
+
*
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```ts
|
|
2005
|
+
* import { syncIndexes } from '@zodmon/core'
|
|
2006
|
+
*
|
|
2007
|
+
* const users = db.use(Users)
|
|
2008
|
+
* const result = await syncIndexes(users)
|
|
2009
|
+
* console.log('Created:', result.created)
|
|
2010
|
+
* console.log('Stale:', result.stale.map(s => s.name))
|
|
2011
|
+
* ```
|
|
2012
|
+
*
|
|
2013
|
+
* @example
|
|
2014
|
+
* ```ts
|
|
2015
|
+
* // Dry run — no changes made
|
|
2016
|
+
* const diff = await syncIndexes(users, { dryRun: true })
|
|
2017
|
+
* console.log('Would create:', diff.created)
|
|
2018
|
+
* console.log('Would drop:', diff.dropped)
|
|
2019
|
+
* ```
|
|
2020
|
+
*/
|
|
2021
|
+
declare function syncIndexes(handle: SyncableHandle, options?: SyncIndexesOptions): Promise<SyncIndexesResult>;
|
|
2022
|
+
|
|
2023
|
+
/**
|
|
2024
|
+
* Structural constraint for the definition argument of {@link checkUnindexedFields}.
|
|
2025
|
+
*
|
|
2026
|
+
* Uses a structural type rather than `CollectionDefinition<z.core.$ZodShape>` to avoid
|
|
2027
|
+
* TypeScript variance issues with `exactOptionalPropertyTypes`. Any collection
|
|
2028
|
+
* definition returned by `collection()` satisfies this constraint.
|
|
2029
|
+
*/
|
|
2030
|
+
type WarnableDefinition = {
|
|
2031
|
+
readonly name: string;
|
|
2032
|
+
readonly fieldIndexes: FieldIndexDefinition[];
|
|
2033
|
+
readonly compoundIndexes: readonly CompoundIndexDefinition[];
|
|
2034
|
+
readonly options: {
|
|
2035
|
+
warnUnindexedQueries?: boolean;
|
|
2036
|
+
};
|
|
2037
|
+
};
|
|
2038
|
+
/**
|
|
2039
|
+
* Warn about unindexed fields used in a query filter.
|
|
2040
|
+
*
|
|
2041
|
+
* When `warnUnindexedQueries` is enabled on a collection definition, this
|
|
2042
|
+
* function checks each top-level field in the filter against the collection's
|
|
2043
|
+
* declared indexes. Fields that are not covered by any index produce a
|
|
2044
|
+
* `console.warn` message to help identify queries that may cause full
|
|
2045
|
+
* collection scans in development.
|
|
2046
|
+
*
|
|
2047
|
+
* **Covered fields:**
|
|
2048
|
+
* - `_id` (always indexed by MongoDB)
|
|
2049
|
+
* - Fields with `.index()`, `.unique()`, `.text()`, or `.expireAfter()` (field-level indexes)
|
|
2050
|
+
* - The **first** field of each compound index (prefix matching)
|
|
2051
|
+
*
|
|
2052
|
+
* **Skipped keys:**
|
|
2053
|
+
* - MongoDB operators: `$or`, `$and`, `$nor`, `$text`, `$where`, `$expr`, `$comment`
|
|
2054
|
+
*
|
|
2055
|
+
* This function is a no-op (zero overhead) when `warnUnindexedQueries` is not
|
|
2056
|
+
* explicitly set to `true`.
|
|
2057
|
+
*
|
|
2058
|
+
* @param definition - The collection definition containing index metadata.
|
|
2059
|
+
* @param filter - The query filter to check for unindexed fields.
|
|
2060
|
+
*
|
|
2061
|
+
* @example
|
|
2062
|
+
* ```ts
|
|
2063
|
+
* import { collection, checkUnindexedFields } from '@zodmon/core'
|
|
2064
|
+
*
|
|
2065
|
+
* const Users = collection('users', {
|
|
2066
|
+
* email: z.string().unique(),
|
|
2067
|
+
* name: z.string(),
|
|
2068
|
+
* }, { warnUnindexedQueries: true })
|
|
2069
|
+
*
|
|
2070
|
+
* // No warning — email is indexed
|
|
2071
|
+
* checkUnindexedFields(Users, { email: 'ada@example.com' })
|
|
2072
|
+
*
|
|
2073
|
+
* // Warns: "[zodmon] warn: query on 'users' uses unindexed field 'name'"
|
|
2074
|
+
* checkUnindexedFields(Users, { name: 'Ada' })
|
|
2075
|
+
* ```
|
|
2076
|
+
*/
|
|
2077
|
+
declare function checkUnindexedFields(definition: WarnableDefinition, filter: Record<string, unknown>): void;
|
|
2078
|
+
|
|
1655
2079
|
/**
|
|
1656
2080
|
* Convenience namespace that groups all query operators under a single import.
|
|
1657
2081
|
*
|
|
@@ -2038,4 +2462,4 @@ type UpdateFilterOf<TDef extends AnyCollection> = TypedUpdateFilter<InferDocumen
|
|
|
2038
2462
|
*/
|
|
2039
2463
|
type SortOf<TDef extends AnyCollection> = TypedSort<InferDocument<TDef>>;
|
|
2040
2464
|
|
|
2041
|
-
export { $, $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AddToSetEach, type AddToSetFields, type AnyCollection, type ArrayElement, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, type CurrentDateFields, type CursorPage, type CursorPaginateOptions, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FilterOf, type FindOneAndDeleteOptions, type FindOneAndUpdateOptions, type FindOneOptions, type FindOptions, type HandleOf, type IncFields, IndexBuilder, type IndexMetadata, type IndexOptions, type InferDocument, type InferInsert, type OffsetPage, type OffsetPaginateOptions, type PopFields, type PullFields, type PushFields, type PushModifiers, type RefMarker, type RefMetadata, type RenameFields, type ResolvedShape, type SetFields, type SortOf, type TypedFilter, TypedFindCursor, type TypedSort, type TypedUpdateFilter, type UnsetFields, type UpdateFilterOf, type UpdateOptions, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, collection, createClient, deleteMany, deleteOne, extractDbName, extractFieldIndexes, find, findOne, findOneAndDelete, findOneAndUpdate, findOneOrThrow, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, isOid, objectId, oid, raw, updateMany, updateOne };
|
|
2465
|
+
export { $, $and, $eq, $exists, $gt, $gte, $in, $lt, $lte, $ne, $nin, $nor, $not, $or, $regex, type AddToSetEach, type AddToSetFields, type AnyCollection, type ArrayElement, type CollectionDefinition, CollectionHandle, type CollectionOptions, type ComparisonOperators, type CompoundIndexDefinition, type CurrentDateFields, type CursorPage, type CursorPaginateOptions, Database, type DotPathType, type DotPaths, type FieldIndexDefinition, type FilterOf, type FindOneAndDeleteOptions, type FindOneAndUpdateOptions, type FindOneOptions, type FindOptions, type HandleOf, type IncFields, IndexBuilder, type IndexMetadata, type IndexNames, type IndexOptions, type IndexSpec, type InferDocument, type InferInsert, type OffsetPage, type OffsetPaginateOptions, type PopFields, type PullFields, type PushFields, type PushModifiers, type RefMarker, type RefMetadata, type RenameFields, type ResolvedShape, type SetFields, type SortOf, type StaleIndex, type SyncIndexesOptions, type SyncIndexesResult, type TypedFilter, TypedFindCursor, type TypedSort, type TypedUpdateFilter, type UnsetFields, type UpdateFilterOf, type UpdateOptions, type ValidationMode, type ZodObjectId, ZodmonNotFoundError, ZodmonValidationError, checkUnindexedFields, collection, createClient, deleteMany, deleteOne, extractComparableOptions, extractDbName, extractFieldIndexes, find, findOne, findOneAndDelete, findOneAndUpdate, findOneOrThrow, generateIndexName, getIndexMetadata, getRefMetadata, index, insertMany, insertOne, isOid, objectId, oid, raw, serializeIndexKey, syncIndexes, toCompoundIndexSpec, toFieldIndexSpec, updateMany, updateOne };
|