@atscript/db-mongo 0.1.39 → 0.1.41

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.
@@ -0,0 +1,344 @@
1
+ import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
+ import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
3
+ import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
4
+
5
+ //#region src/lib/collection-patcher.d.ts
6
+ /**
7
+ * Context interface for CollectionPatcher.
8
+ * Decouples the patcher from AsCollection, allowing MongoAdapter to provide this.
9
+ */
10
+ interface TCollectionPatcherContext {
11
+ flatMap: Map<string, TAtscriptAnnotatedType>;
12
+ prepareId(id: any): any;
13
+ createValidator(opts?: Partial<TValidatorOptions>): Validator<any>;
14
+ }
15
+ /**
16
+ * CollectionPatcher is a small helper that converts a *patch payload* produced
17
+ * by Atscript into a shape that the official MongoDB driver understands – a
18
+ * triple of `(filter, update, options)` to be fed to `collection.updateOne()`.
19
+ *
20
+ * Supported high‑level operations for *top‑level arrays* (see the attached
21
+ * spreadsheet in the chat):
22
+ *
23
+ * | Payload field | MongoDB operator | Purpose |
24
+ * |-------------- |-------------------------|----------------------------------------|
25
+ * | `$replace` | full `$set` | Replace the whole array. |
26
+ * | `$insert` | `$push` | Append new items (duplicates allowed). |
27
+ * | `$upsert` | custom | Insert or update by *key* (see TODO). |
28
+ * | `$update` | `$set` + `arrayFilters` | Update array elements matched by *key* |
29
+ * | `$remove` | `$pullAll` / `$pull` | Remove by value or by *key*. |
30
+ *
31
+ * The class walks through the incoming payload, detects which of the above
32
+ * operations applies to each top‑level array and builds the corresponding
33
+ * MongoDB update document. Primitive fields are flattened into a regular
34
+ * `$set` map.
35
+ */
36
+ declare class CollectionPatcher {
37
+ private collection;
38
+ private payload;
39
+ private ops?;
40
+ constructor(collection: TCollectionPatcherContext, payload: any, ops?: TFieldOps | undefined);
41
+ static getKeyProps: typeof getKeyProps;
42
+ /**
43
+ * Internal accumulator: filter passed to `updateOne()`.
44
+ * Filled only with the `_id` field right now.
45
+ */
46
+ private filterObj;
47
+ /** MongoDB *update* document being built. */
48
+ private updatePipeline;
49
+ /** Current `$set` stage being populated. */
50
+ private currentSetStage;
51
+ /** Additional *options* (mainly `arrayFilters`). */
52
+ private optionsObj;
53
+ /**
54
+ * Entry point – walk the payload, build `filter`, `update` and `options`.
55
+ *
56
+ * @returns Helper object exposing both individual parts and
57
+ * a `.toArgs()` convenience callback.
58
+ */
59
+ preparePatch(): {
60
+ toArgs: () => [Filter<any>, UpdateFilter<any> | Document[], UpdateOptions];
61
+ filter: Filter<any>;
62
+ updateFilter: Document[];
63
+ updateOptions: UpdateOptions;
64
+ };
65
+ /** Builds a MongoDB aggregation expression for an $inc or $mul field op. */
66
+ private _fieldOpExpr;
67
+ /**
68
+ * Helper – lazily create `$set` section and assign *key* → *value*.
69
+ *
70
+ * @param key Fully‑qualified dotted path
71
+ * @param val Value to be written
72
+ * @private
73
+ */
74
+ private _set;
75
+ /**
76
+ * Recursively walk through the patch *payload* and convert it into `$set`/…
77
+ * statements. Top‑level arrays are delegated to {@link parseArrayPatch}.
78
+ *
79
+ * @param payload Current payload chunk
80
+ * @param prefix Dotted path accumulated so far
81
+ * @private
82
+ */
83
+ private flattenPayload;
84
+ /**
85
+ * Dispatch a *single* array patch. Exactly one of `$replace`, `$insert`,
86
+ * `$upsert`, `$update`, `$remove` must be present – otherwise we throw.
87
+ *
88
+ * @param key Dotted path to the array field
89
+ * @param value Payload slice for that field
90
+ * @private
91
+ */
92
+ private parseArrayPatch;
93
+ /**
94
+ * Build an *aggregation‐expression* that checks equality by **all** keys in
95
+ * `keys`. Example output for keys `["id", "lang"]` and bases `a`, `b`:
96
+ * ```json
97
+ * { "$and": [ { "$eq": ["$$a.id", "$$b.id"] }, { "$eq": ["$$a.lang", "$$b.lang"] } ] }
98
+ * ```
99
+ *
100
+ * @param keys Ordered list of key property names
101
+ * @param left Base token for *left* expression (e.g. `"$$el"`)
102
+ * @param right Base token for *right* expression (e.g. `"$$this"`)
103
+ */
104
+ private _keysEqual;
105
+ /**
106
+ * `$replace` – overwrite the entire array with `input`.
107
+ *
108
+ * @param key Dotted path to the array
109
+ * @param input New array value (may be `undefined`)
110
+ * @private
111
+ */
112
+ private _replace;
113
+ /**
114
+ * `$insert`
115
+ * - plain append → $concatArrays
116
+ * - unique / keyed → delegate to _upsert (insert-or-update)
117
+ */
118
+ private _insert;
119
+ /**
120
+ * `$upsert`
121
+ * - keyed → remove existing matching by key(s) then append candidate
122
+ * - unique → $setUnion (deep equality)
123
+ */
124
+ private _upsert;
125
+ /**
126
+ * `$update`
127
+ * - keyed → map array and merge / replace matching element(s)
128
+ * - non-keyed → behave like `$addToSet` (insert only when not present)
129
+ */
130
+ private _update;
131
+ /**
132
+ * `$remove`
133
+ * - keyed → filter out any element whose key set matches a payload item
134
+ * - non-keyed → deep equality remove (`$setDifference`)
135
+ */
136
+ private _remove;
137
+ }
138
+ //#endregion
139
+ //#region src/lib/mongo-types.d.ts
140
+ interface TPlainIndex {
141
+ key: string;
142
+ name: string;
143
+ type: "plain" | "unique" | "text";
144
+ fields: Record<string, 1 | "text">;
145
+ weights: Record<string, number>;
146
+ }
147
+ interface TSearchIndex {
148
+ key: string;
149
+ name: string;
150
+ type: "dynamic_text" | "search_text" | "vector";
151
+ definition: TMongoSearchIndexDefinition;
152
+ }
153
+ type TMongoIndex = TPlainIndex | TSearchIndex;
154
+ type TVectorSimilarity = "cosine" | "euclidean" | "dotProduct";
155
+ interface TMongoSearchIndexDefinition {
156
+ mappings?: {
157
+ dynamic?: boolean;
158
+ fields?: Record<string, {
159
+ type: string;
160
+ analyzer?: string;
161
+ }>;
162
+ };
163
+ fields?: Array<{
164
+ path: string;
165
+ type: "filter" | "vector";
166
+ similarity?: TVectorSimilarity;
167
+ numDimensions?: number;
168
+ }>;
169
+ analyzer?: string;
170
+ text?: {
171
+ fuzzy?: {
172
+ maxEdits: number;
173
+ };
174
+ };
175
+ }
176
+ //#endregion
177
+ //#region src/lib/mongo-adapter.d.ts
178
+ declare class MongoAdapter extends BaseDbAdapter {
179
+ protected readonly db: Db;
180
+ protected readonly client?: MongoClient | undefined;
181
+ private _collection?;
182
+ /** MongoDB-specific indexes (search, vector) — separate from table.indexes. */
183
+ protected _mongoIndexes: Map<string, TMongoIndex>;
184
+ /** Vector search filter associations built during flattening. */
185
+ protected _vectorFilters: Map<string, string>;
186
+ /** Default similarity thresholds per vector index (from @db.search.vector.threshold). */
187
+ protected _vectorThresholds: Map<string, number>;
188
+ /** Cached search index lookup. */
189
+ protected _searchIndexesMap?: Map<string, TMongoIndex>;
190
+ /** Physical field names with @db.default.increment → optional start value. */
191
+ protected _incrementFields: Map<string, number | undefined>;
192
+ /** Physical field names that have a non-binary collation (nocase/unicode). */
193
+ private _collateFields?;
194
+ /** Capped collection options from @db.mongo.capped. */
195
+ protected _cappedOptions?: {
196
+ size: number;
197
+ max?: number;
198
+ };
199
+ /** Whether the schema explicitly defines _id (via @db.mongo.collection or manual _id field). */
200
+ protected _hasExplicitId: boolean;
201
+ /** Unique fields accumulated during onFieldScanned, returned via getMetadataOverrides. */
202
+ private _pendingUniqueFields;
203
+ constructor(db: Db, client?: MongoClient | undefined);
204
+ private get _client();
205
+ /**
206
+ * Per-client cache: whether transactions are unavailable (standalone MongoDB).
207
+ * Shared across all adapter instances for the same client so topology is probed once.
208
+ */
209
+ private static readonly _txDisabledClients;
210
+ private get _txDisabled();
211
+ private set _txDisabled(value);
212
+ /**
213
+ * Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
214
+ * This handles txnNumber management and automatic retry for
215
+ * `TransientTransactionError` / `UnknownTransactionCommitResult`.
216
+ */
217
+ withTransaction<T>(fn: () => Promise<T>): Promise<T>;
218
+ private static readonly _noSession;
219
+ /** Returns `{ session }` opts if inside a transaction, empty object otherwise. */
220
+ protected _getSessionOpts(): {
221
+ session: ClientSession;
222
+ } | Record<string, never>;
223
+ get collection(): Collection<any>;
224
+ aggregatePipeline(pipeline: Document[]): AggregationCursor;
225
+ aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
226
+ get idType(): "string" | "number" | "objectId";
227
+ prepareId(id: unknown, _fieldType: unknown): unknown;
228
+ /**
229
+ * Convenience method that uses `idType` to transform an ID value.
230
+ * For use in controllers that don't have access to the field type.
231
+ */
232
+ prepareIdFromIdType<D = string | number | ObjectId>(id: string | number | ObjectId): D;
233
+ supportsNestedObjects(): boolean;
234
+ supportsNativePatch(): boolean;
235
+ getValidatorPlugins(): ReturnType<BaseDbAdapter["getValidatorPlugins"]>;
236
+ getAdapterTableName(_type: unknown): string | undefined;
237
+ supportsNativeRelations(): boolean;
238
+ loadRelations(rows: Array<Record<string, unknown>>, withRelations: WithRelation[], relations: ReadonlyMap<string, TDbRelation>, foreignKeys: ReadonlyMap<string, TDbForeignKey>, tableResolver?: TTableResolver): Promise<void>;
239
+ /** Returns the context object used by CollectionPatcher. */
240
+ getPatcherContext(): TCollectionPatcherContext;
241
+ nativePatch(filter: FilterExpr, patch: unknown, ops?: TFieldOps): Promise<TDbUpdateResult>;
242
+ onBeforeFlatten(_type: unknown): void;
243
+ onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
244
+ getMetadataOverrides(meta: TableMetadata): TMetadataOverrides;
245
+ onAfterFlatten(): void;
246
+ /** Returns MongoDB-specific search index map (internal). */
247
+ getMongoSearchIndexes(): Map<string, TMongoIndex>;
248
+ /** Returns a specific MongoDB search index by name. */
249
+ getMongoSearchIndex(name?: string): TMongoIndex | undefined;
250
+ /** Returns the default similarity threshold for a vector index (from @db.search.vector.threshold). */
251
+ getVectorThreshold(indexName?: string): number | undefined;
252
+ getSearchIndexes(): TSearchIndexInfo[];
253
+ isVectorSearchable(): boolean;
254
+ search(text: string, query: DbQuery, indexName?: string): Promise<Record<string, unknown>[]>;
255
+ searchWithCount(text: string, query: DbQuery, indexName?: string): Promise<{
256
+ data: Array<Record<string, unknown>>;
257
+ count: number;
258
+ }>;
259
+ vectorSearch(vector: number[], query: DbQuery, indexName?: string): Promise<Record<string, unknown>[]>;
260
+ vectorSearchWithCount(vector: number[], query: DbQuery, indexName?: string): Promise<{
261
+ data: Array<Record<string, unknown>>;
262
+ count: number;
263
+ }>;
264
+ findManyWithCount(query: DbQuery): Promise<{
265
+ data: Array<Record<string, unknown>>;
266
+ count: number;
267
+ }>;
268
+ collectionExists(): Promise<boolean>;
269
+ ensureCollectionExists(): Promise<void>;
270
+ /**
271
+ * Wraps an async operation to catch MongoDB duplicate key errors
272
+ * (code 11000) and rethrow as structured `DbError`.
273
+ */
274
+ private _wrapDuplicateKeyError;
275
+ insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
276
+ insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
277
+ findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
278
+ findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
279
+ count(query: DbQuery): Promise<number>;
280
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
281
+ replaceOne(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
282
+ deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
283
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
284
+ replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
285
+ deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
286
+ clearCollectionCache(): void;
287
+ tableExists(): Promise<boolean>;
288
+ ensureTable(): Promise<void>;
289
+ syncIndexes(): Promise<void>;
290
+ syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
291
+ dropColumns(columns: string[]): Promise<void>;
292
+ renameTable(oldName: string): Promise<void>;
293
+ recreateTable(): Promise<void>;
294
+ dropTable(): Promise<void>;
295
+ dropViewByName(viewName: string): Promise<void>;
296
+ dropTableByName(tableName: string): Promise<void>;
297
+ getDesiredTableOptions(): TExistingTableOption[];
298
+ getExistingTableOptions(): Promise<TExistingTableOption[]>;
299
+ destructiveOptionKeys(): ReadonlySet<string>;
300
+ /** Returns the counters collection used for atomic auto-increment. */
301
+ protected get _countersCollection(): Collection<{
302
+ _id: string;
303
+ seq: number;
304
+ }>;
305
+ /** Returns physical field names of increment fields that are undefined in the data. */
306
+ private _fieldsNeedingIncrement;
307
+ /**
308
+ * Atomically allocates `count` sequential values for each increment field
309
+ * using a counter collection. Returns a map of field → first allocated value.
310
+ */
311
+ private _allocateIncrementValues;
312
+ /** Reads current max value for a single field via $group aggregation. */
313
+ private _getCurrentFieldMax;
314
+ /** Allocates increment values for a batch of items, assigning in order. */
315
+ private _assignBatchIncrements;
316
+ private _buildFindOptions;
317
+ /**
318
+ * Returns MongoDB collation options if any filter field has a non-binary collation.
319
+ * Uses pre-computed insights when available, falls back to computing them on demand.
320
+ * Maps: nocase → strength 2 (case-insensitive), unicode → strength 1 (case+accent-insensitive).
321
+ */
322
+ private _getCollationOpts;
323
+ protected _addMongoIndexField(type: TPlainIndex["type"], name: string, field: string, weight?: number): void;
324
+ protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition): void;
325
+ protected _addFieldToSearchIndex(type: TSearchIndex["type"], _name: string | undefined, fieldName: string, analyzer?: string): void;
326
+ }
327
+ //#endregion
328
+ //#region src/lib/mongo-filter.d.ts
329
+ /**
330
+ * Translates a generic {@link FilterExpr} into a MongoDB-compatible
331
+ * {@link Filter} document.
332
+ *
333
+ * MongoDB's query language is nearly identical to the `FilterExpr` structure,
334
+ * so this is largely a structural pass-through via the `walkFilter` visitor.
335
+ */
336
+ declare function buildMongoFilter(filter: FilterExpr): Filter<any>;
337
+ //#endregion
338
+ //#region src/lib/validate-plugins.d.ts
339
+ declare const validateMongoIdPlugin: TValidatorPlugin;
340
+ //#endregion
341
+ //#region src/lib/index.d.ts
342
+ declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
343
+ //#endregion
344
+ export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
@@ -0,0 +1,344 @@
1
+ import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
+ import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
3
+ import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
4
+
5
+ //#region src/lib/collection-patcher.d.ts
6
+ /**
7
+ * Context interface for CollectionPatcher.
8
+ * Decouples the patcher from AsCollection, allowing MongoAdapter to provide this.
9
+ */
10
+ interface TCollectionPatcherContext {
11
+ flatMap: Map<string, TAtscriptAnnotatedType>;
12
+ prepareId(id: any): any;
13
+ createValidator(opts?: Partial<TValidatorOptions>): Validator<any>;
14
+ }
15
+ /**
16
+ * CollectionPatcher is a small helper that converts a *patch payload* produced
17
+ * by Atscript into a shape that the official MongoDB driver understands – a
18
+ * triple of `(filter, update, options)` to be fed to `collection.updateOne()`.
19
+ *
20
+ * Supported high‑level operations for *top‑level arrays* (see the attached
21
+ * spreadsheet in the chat):
22
+ *
23
+ * | Payload field | MongoDB operator | Purpose |
24
+ * |-------------- |-------------------------|----------------------------------------|
25
+ * | `$replace` | full `$set` | Replace the whole array. |
26
+ * | `$insert` | `$push` | Append new items (duplicates allowed). |
27
+ * | `$upsert` | custom | Insert or update by *key* (see TODO). |
28
+ * | `$update` | `$set` + `arrayFilters` | Update array elements matched by *key* |
29
+ * | `$remove` | `$pullAll` / `$pull` | Remove by value or by *key*. |
30
+ *
31
+ * The class walks through the incoming payload, detects which of the above
32
+ * operations applies to each top‑level array and builds the corresponding
33
+ * MongoDB update document. Primitive fields are flattened into a regular
34
+ * `$set` map.
35
+ */
36
+ declare class CollectionPatcher {
37
+ private collection;
38
+ private payload;
39
+ private ops?;
40
+ constructor(collection: TCollectionPatcherContext, payload: any, ops?: TFieldOps | undefined);
41
+ static getKeyProps: typeof getKeyProps;
42
+ /**
43
+ * Internal accumulator: filter passed to `updateOne()`.
44
+ * Filled only with the `_id` field right now.
45
+ */
46
+ private filterObj;
47
+ /** MongoDB *update* document being built. */
48
+ private updatePipeline;
49
+ /** Current `$set` stage being populated. */
50
+ private currentSetStage;
51
+ /** Additional *options* (mainly `arrayFilters`). */
52
+ private optionsObj;
53
+ /**
54
+ * Entry point – walk the payload, build `filter`, `update` and `options`.
55
+ *
56
+ * @returns Helper object exposing both individual parts and
57
+ * a `.toArgs()` convenience callback.
58
+ */
59
+ preparePatch(): {
60
+ toArgs: () => [Filter<any>, UpdateFilter<any> | Document[], UpdateOptions];
61
+ filter: Filter<any>;
62
+ updateFilter: Document[];
63
+ updateOptions: UpdateOptions;
64
+ };
65
+ /** Builds a MongoDB aggregation expression for an $inc or $mul field op. */
66
+ private _fieldOpExpr;
67
+ /**
68
+ * Helper – lazily create `$set` section and assign *key* → *value*.
69
+ *
70
+ * @param key Fully‑qualified dotted path
71
+ * @param val Value to be written
72
+ * @private
73
+ */
74
+ private _set;
75
+ /**
76
+ * Recursively walk through the patch *payload* and convert it into `$set`/…
77
+ * statements. Top‑level arrays are delegated to {@link parseArrayPatch}.
78
+ *
79
+ * @param payload Current payload chunk
80
+ * @param prefix Dotted path accumulated so far
81
+ * @private
82
+ */
83
+ private flattenPayload;
84
+ /**
85
+ * Dispatch a *single* array patch. Exactly one of `$replace`, `$insert`,
86
+ * `$upsert`, `$update`, `$remove` must be present – otherwise we throw.
87
+ *
88
+ * @param key Dotted path to the array field
89
+ * @param value Payload slice for that field
90
+ * @private
91
+ */
92
+ private parseArrayPatch;
93
+ /**
94
+ * Build an *aggregation‐expression* that checks equality by **all** keys in
95
+ * `keys`. Example output for keys `["id", "lang"]` and bases `a`, `b`:
96
+ * ```json
97
+ * { "$and": [ { "$eq": ["$$a.id", "$$b.id"] }, { "$eq": ["$$a.lang", "$$b.lang"] } ] }
98
+ * ```
99
+ *
100
+ * @param keys Ordered list of key property names
101
+ * @param left Base token for *left* expression (e.g. `"$$el"`)
102
+ * @param right Base token for *right* expression (e.g. `"$$this"`)
103
+ */
104
+ private _keysEqual;
105
+ /**
106
+ * `$replace` – overwrite the entire array with `input`.
107
+ *
108
+ * @param key Dotted path to the array
109
+ * @param input New array value (may be `undefined`)
110
+ * @private
111
+ */
112
+ private _replace;
113
+ /**
114
+ * `$insert`
115
+ * - plain append → $concatArrays
116
+ * - unique / keyed → delegate to _upsert (insert-or-update)
117
+ */
118
+ private _insert;
119
+ /**
120
+ * `$upsert`
121
+ * - keyed → remove existing matching by key(s) then append candidate
122
+ * - unique → $setUnion (deep equality)
123
+ */
124
+ private _upsert;
125
+ /**
126
+ * `$update`
127
+ * - keyed → map array and merge / replace matching element(s)
128
+ * - non-keyed → behave like `$addToSet` (insert only when not present)
129
+ */
130
+ private _update;
131
+ /**
132
+ * `$remove`
133
+ * - keyed → filter out any element whose key set matches a payload item
134
+ * - non-keyed → deep equality remove (`$setDifference`)
135
+ */
136
+ private _remove;
137
+ }
138
+ //#endregion
139
+ //#region src/lib/mongo-types.d.ts
140
+ interface TPlainIndex {
141
+ key: string;
142
+ name: string;
143
+ type: "plain" | "unique" | "text";
144
+ fields: Record<string, 1 | "text">;
145
+ weights: Record<string, number>;
146
+ }
147
+ interface TSearchIndex {
148
+ key: string;
149
+ name: string;
150
+ type: "dynamic_text" | "search_text" | "vector";
151
+ definition: TMongoSearchIndexDefinition;
152
+ }
153
+ type TMongoIndex = TPlainIndex | TSearchIndex;
154
+ type TVectorSimilarity = "cosine" | "euclidean" | "dotProduct";
155
+ interface TMongoSearchIndexDefinition {
156
+ mappings?: {
157
+ dynamic?: boolean;
158
+ fields?: Record<string, {
159
+ type: string;
160
+ analyzer?: string;
161
+ }>;
162
+ };
163
+ fields?: Array<{
164
+ path: string;
165
+ type: "filter" | "vector";
166
+ similarity?: TVectorSimilarity;
167
+ numDimensions?: number;
168
+ }>;
169
+ analyzer?: string;
170
+ text?: {
171
+ fuzzy?: {
172
+ maxEdits: number;
173
+ };
174
+ };
175
+ }
176
+ //#endregion
177
+ //#region src/lib/mongo-adapter.d.ts
178
+ declare class MongoAdapter extends BaseDbAdapter {
179
+ protected readonly db: Db;
180
+ protected readonly client?: MongoClient | undefined;
181
+ private _collection?;
182
+ /** MongoDB-specific indexes (search, vector) — separate from table.indexes. */
183
+ protected _mongoIndexes: Map<string, TMongoIndex>;
184
+ /** Vector search filter associations built during flattening. */
185
+ protected _vectorFilters: Map<string, string>;
186
+ /** Default similarity thresholds per vector index (from @db.search.vector.threshold). */
187
+ protected _vectorThresholds: Map<string, number>;
188
+ /** Cached search index lookup. */
189
+ protected _searchIndexesMap?: Map<string, TMongoIndex>;
190
+ /** Physical field names with @db.default.increment → optional start value. */
191
+ protected _incrementFields: Map<string, number | undefined>;
192
+ /** Physical field names that have a non-binary collation (nocase/unicode). */
193
+ private _collateFields?;
194
+ /** Capped collection options from @db.mongo.capped. */
195
+ protected _cappedOptions?: {
196
+ size: number;
197
+ max?: number;
198
+ };
199
+ /** Whether the schema explicitly defines _id (via @db.mongo.collection or manual _id field). */
200
+ protected _hasExplicitId: boolean;
201
+ /** Unique fields accumulated during onFieldScanned, returned via getMetadataOverrides. */
202
+ private _pendingUniqueFields;
203
+ constructor(db: Db, client?: MongoClient | undefined);
204
+ private get _client();
205
+ /**
206
+ * Per-client cache: whether transactions are unavailable (standalone MongoDB).
207
+ * Shared across all adapter instances for the same client so topology is probed once.
208
+ */
209
+ private static readonly _txDisabledClients;
210
+ private get _txDisabled();
211
+ private set _txDisabled(value);
212
+ /**
213
+ * Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
214
+ * This handles txnNumber management and automatic retry for
215
+ * `TransientTransactionError` / `UnknownTransactionCommitResult`.
216
+ */
217
+ withTransaction<T>(fn: () => Promise<T>): Promise<T>;
218
+ private static readonly _noSession;
219
+ /** Returns `{ session }` opts if inside a transaction, empty object otherwise. */
220
+ protected _getSessionOpts(): {
221
+ session: ClientSession;
222
+ } | Record<string, never>;
223
+ get collection(): Collection<any>;
224
+ aggregatePipeline(pipeline: Document[]): AggregationCursor;
225
+ aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
226
+ get idType(): "string" | "number" | "objectId";
227
+ prepareId(id: unknown, _fieldType: unknown): unknown;
228
+ /**
229
+ * Convenience method that uses `idType` to transform an ID value.
230
+ * For use in controllers that don't have access to the field type.
231
+ */
232
+ prepareIdFromIdType<D = string | number | ObjectId>(id: string | number | ObjectId): D;
233
+ supportsNestedObjects(): boolean;
234
+ supportsNativePatch(): boolean;
235
+ getValidatorPlugins(): ReturnType<BaseDbAdapter["getValidatorPlugins"]>;
236
+ getAdapterTableName(_type: unknown): string | undefined;
237
+ supportsNativeRelations(): boolean;
238
+ loadRelations(rows: Array<Record<string, unknown>>, withRelations: WithRelation[], relations: ReadonlyMap<string, TDbRelation>, foreignKeys: ReadonlyMap<string, TDbForeignKey>, tableResolver?: TTableResolver): Promise<void>;
239
+ /** Returns the context object used by CollectionPatcher. */
240
+ getPatcherContext(): TCollectionPatcherContext;
241
+ nativePatch(filter: FilterExpr, patch: unknown, ops?: TFieldOps): Promise<TDbUpdateResult>;
242
+ onBeforeFlatten(_type: unknown): void;
243
+ onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap<AtscriptMetadata>): void;
244
+ getMetadataOverrides(meta: TableMetadata): TMetadataOverrides;
245
+ onAfterFlatten(): void;
246
+ /** Returns MongoDB-specific search index map (internal). */
247
+ getMongoSearchIndexes(): Map<string, TMongoIndex>;
248
+ /** Returns a specific MongoDB search index by name. */
249
+ getMongoSearchIndex(name?: string): TMongoIndex | undefined;
250
+ /** Returns the default similarity threshold for a vector index (from @db.search.vector.threshold). */
251
+ getVectorThreshold(indexName?: string): number | undefined;
252
+ getSearchIndexes(): TSearchIndexInfo[];
253
+ isVectorSearchable(): boolean;
254
+ search(text: string, query: DbQuery, indexName?: string): Promise<Record<string, unknown>[]>;
255
+ searchWithCount(text: string, query: DbQuery, indexName?: string): Promise<{
256
+ data: Array<Record<string, unknown>>;
257
+ count: number;
258
+ }>;
259
+ vectorSearch(vector: number[], query: DbQuery, indexName?: string): Promise<Record<string, unknown>[]>;
260
+ vectorSearchWithCount(vector: number[], query: DbQuery, indexName?: string): Promise<{
261
+ data: Array<Record<string, unknown>>;
262
+ count: number;
263
+ }>;
264
+ findManyWithCount(query: DbQuery): Promise<{
265
+ data: Array<Record<string, unknown>>;
266
+ count: number;
267
+ }>;
268
+ collectionExists(): Promise<boolean>;
269
+ ensureCollectionExists(): Promise<void>;
270
+ /**
271
+ * Wraps an async operation to catch MongoDB duplicate key errors
272
+ * (code 11000) and rethrow as structured `DbError`.
273
+ */
274
+ private _wrapDuplicateKeyError;
275
+ insertOne(data: Record<string, unknown>): Promise<TDbInsertResult>;
276
+ insertMany(data: Array<Record<string, unknown>>): Promise<TDbInsertManyResult>;
277
+ findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
278
+ findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
279
+ count(query: DbQuery): Promise<number>;
280
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
281
+ replaceOne(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
282
+ deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
283
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
284
+ replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
285
+ deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
286
+ clearCollectionCache(): void;
287
+ tableExists(): Promise<boolean>;
288
+ ensureTable(): Promise<void>;
289
+ syncIndexes(): Promise<void>;
290
+ syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
291
+ dropColumns(columns: string[]): Promise<void>;
292
+ renameTable(oldName: string): Promise<void>;
293
+ recreateTable(): Promise<void>;
294
+ dropTable(): Promise<void>;
295
+ dropViewByName(viewName: string): Promise<void>;
296
+ dropTableByName(tableName: string): Promise<void>;
297
+ getDesiredTableOptions(): TExistingTableOption[];
298
+ getExistingTableOptions(): Promise<TExistingTableOption[]>;
299
+ destructiveOptionKeys(): ReadonlySet<string>;
300
+ /** Returns the counters collection used for atomic auto-increment. */
301
+ protected get _countersCollection(): Collection<{
302
+ _id: string;
303
+ seq: number;
304
+ }>;
305
+ /** Returns physical field names of increment fields that are undefined in the data. */
306
+ private _fieldsNeedingIncrement;
307
+ /**
308
+ * Atomically allocates `count` sequential values for each increment field
309
+ * using a counter collection. Returns a map of field → first allocated value.
310
+ */
311
+ private _allocateIncrementValues;
312
+ /** Reads current max value for a single field via $group aggregation. */
313
+ private _getCurrentFieldMax;
314
+ /** Allocates increment values for a batch of items, assigning in order. */
315
+ private _assignBatchIncrements;
316
+ private _buildFindOptions;
317
+ /**
318
+ * Returns MongoDB collation options if any filter field has a non-binary collation.
319
+ * Uses pre-computed insights when available, falls back to computing them on demand.
320
+ * Maps: nocase → strength 2 (case-insensitive), unicode → strength 1 (case+accent-insensitive).
321
+ */
322
+ private _getCollationOpts;
323
+ protected _addMongoIndexField(type: TPlainIndex["type"], name: string, field: string, weight?: number): void;
324
+ protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition): void;
325
+ protected _addFieldToSearchIndex(type: TSearchIndex["type"], _name: string | undefined, fieldName: string, analyzer?: string): void;
326
+ }
327
+ //#endregion
328
+ //#region src/lib/mongo-filter.d.ts
329
+ /**
330
+ * Translates a generic {@link FilterExpr} into a MongoDB-compatible
331
+ * {@link Filter} document.
332
+ *
333
+ * MongoDB's query language is nearly identical to the `FilterExpr` structure,
334
+ * so this is largely a structural pass-through via the `walkFilter` visitor.
335
+ */
336
+ declare function buildMongoFilter(filter: FilterExpr): Filter<any>;
337
+ //#endregion
338
+ //#region src/lib/validate-plugins.d.ts
339
+ declare const validateMongoIdPlugin: TValidatorPlugin;
340
+ //#endregion
341
+ //#region src/lib/index.d.ts
342
+ declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
343
+ //#endregion
344
+ export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };