@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.
package/dist/index.cjs CHANGED
@@ -1,28 +1,58 @@
1
- "use strict";
2
- const require_mongo_filter = require('./mongo-filter-C8w5by9H.cjs');
3
- const __atscript_db = require_mongo_filter.__toESM(require("@atscript/db"));
4
- const mongodb = require_mongo_filter.__toESM(require("mongodb"));
5
-
6
- //#region packages/db-mongo/src/lib/collection-patcher.ts
7
- function _define_property$1(obj, key, value) {
8
- if (key in obj) Object.defineProperty(obj, key, {
9
- value,
10
- enumerable: true,
11
- configurable: true,
12
- writable: true
13
- });
14
- else obj[key] = value;
15
- return obj;
16
- }
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_mongo_filter = require("./mongo-filter-B5ZINZPF.cjs");
3
+ let _atscript_db = require("@atscript/db");
4
+ let mongodb = require("mongodb");
5
+ //#region src/lib/collection-patcher.ts
6
+ /**
7
+ * CollectionPatcher is a small helper that converts a *patch payload* produced
8
+ * by Atscript into a shape that the official MongoDB driver understands – a
9
+ * triple of `(filter, update, options)` to be fed to `collection.updateOne()`.
10
+ *
11
+ * Supported high‑level operations for *top‑level arrays* (see the attached
12
+ * spreadsheet in the chat):
13
+ *
14
+ * | Payload field | MongoDB operator | Purpose |
15
+ * |-------------- |-------------------------|----------------------------------------|
16
+ * | `$replace` | full `$set` | Replace the whole array. |
17
+ * | `$insert` | `$push` | Append new items (duplicates allowed). |
18
+ * | `$upsert` | custom | Insert or update by *key* (see TODO). |
19
+ * | `$update` | `$set` + `arrayFilters` | Update array elements matched by *key* |
20
+ * | `$remove` | `$pullAll` / `$pull` | Remove by value or by *key*. |
21
+ *
22
+ * The class walks through the incoming payload, detects which of the above
23
+ * operations applies to each top‑level array and builds the corresponding
24
+ * MongoDB update document. Primitive fields are flattened into a regular
25
+ * `$set` map.
26
+ */
17
27
  var CollectionPatcher = class {
28
+ constructor(collection, payload, ops) {
29
+ this.collection = collection;
30
+ this.payload = payload;
31
+ this.ops = ops;
32
+ }
33
+ static getKeyProps = _atscript_db.getKeyProps;
34
+ /**
35
+ * Internal accumulator: filter passed to `updateOne()`.
36
+ * Filled only with the `_id` field right now.
37
+ */
38
+ filterObj = {};
39
+ /** MongoDB *update* document being built. */
40
+ updatePipeline = [];
41
+ /** Current `$set` stage being populated. */
42
+ currentSetStage = null;
43
+ /** Additional *options* (mainly `arrayFilters`). */
44
+ optionsObj = {};
18
45
  /**
19
46
  * Entry point – walk the payload, build `filter`, `update` and `options`.
20
47
  *
21
48
  * @returns Helper object exposing both individual parts and
22
49
  * a `.toArgs()` convenience callback.
23
- */ preparePatch() {
50
+ */
51
+ preparePatch() {
24
52
  this.filterObj = { _id: this.collection.prepareId(this.payload._id) };
25
53
  this.flattenPayload(this.payload);
54
+ if (this.ops?.inc) for (const key in this.ops.inc) this._set(key, this._fieldOpExpr(key, "inc", this.ops.inc[key]));
55
+ if (this.ops?.mul) for (const key in this.ops.mul) this._set(key, this._fieldOpExpr(key, "mul", this.ops.mul[key]));
26
56
  const updateFilter = this.updatePipeline;
27
57
  return {
28
58
  toArgs: () => [
@@ -35,13 +65,18 @@ var CollectionPatcher = class {
35
65
  updateOptions: this.optionsObj
36
66
  };
37
67
  }
68
+ /** Builds a MongoDB aggregation expression for an $inc or $mul field op. */
69
+ _fieldOpExpr(key, op, value) {
70
+ return op === "inc" ? { $add: [`$${key}`, value] } : { $multiply: [`$${key}`, value] };
71
+ }
38
72
  /**
39
73
  * Helper – lazily create `$set` section and assign *key* → *value*.
40
74
  *
41
75
  * @param key Fully‑qualified dotted path
42
76
  * @param val Value to be written
43
77
  * @private
44
- */ _set(key, val) {
78
+ */
79
+ _set(key, val) {
45
80
  if (this.currentSetStage && !(key in this.currentSetStage.$set)) {
46
81
  this.currentSetStage.$set[key] = val;
47
82
  return;
@@ -56,15 +91,20 @@ var CollectionPatcher = class {
56
91
  * @param payload Current payload chunk
57
92
  * @param prefix Dotted path accumulated so far
58
93
  * @private
59
- */ flattenPayload(payload, prefix = "") {
94
+ */
95
+ flattenPayload(payload, prefix = "") {
60
96
  const evalKey = (k) => prefix ? `${prefix}.${k}` : k;
61
97
  for (const [_key, value] of Object.entries(payload)) {
62
98
  const key = evalKey(_key);
63
99
  const flatType = this.collection.flatMap.get(key);
64
100
  const topLevelArray = flatType?.metadata?.get("db.__topLevelArray");
65
101
  if (typeof value === "object" && !Array.isArray(value) && topLevelArray && !flatType?.metadata?.has("db.json")) this.parseArrayPatch(key, value, flatType);
66
- else if (typeof value === "object" && flatType?.metadata?.get("db.patch.strategy") === "merge") this.flattenPayload(value, key);
67
- else if (key !== "_id") this._set(key, value);
102
+ else if (typeof value === "object" && flatType?.metadata?.get("db.patch.strategy") === "merge") this.flattenPayload(value, key);
103
+ else if (key !== "_id") {
104
+ const fieldOp = (0, _atscript_db.getDbFieldOp)(value);
105
+ if (fieldOp) this._set(key, this._fieldOpExpr(key, fieldOp.op, fieldOp.value));
106
+ else this._set(key, value);
107
+ }
68
108
  }
69
109
  return this.updatePipeline;
70
110
  }
@@ -75,13 +115,14 @@ else if (key !== "_id") this._set(key, value);
75
115
  * @param key Dotted path to the array field
76
116
  * @param value Payload slice for that field
77
117
  * @private
78
- */ parseArrayPatch(key, value, flatType) {
118
+ */
119
+ parseArrayPatch(key, value, flatType) {
79
120
  const toRemove = value.$remove;
80
121
  const toReplace = value.$replace;
81
122
  const toInsert = value.$insert;
82
123
  const toUpsert = value.$upsert;
83
124
  const toUpdate = value.$update;
84
- const keyProps = flatType.type.kind === "array" ? (0, __atscript_db.getKeyProps)(flatType) : new Set();
125
+ const keyProps = flatType.type.kind === "array" ? (0, _atscript_db.getKeyProps)(flatType) : /* @__PURE__ */ new Set();
85
126
  const keys = keyProps.size > 0 ? [...keyProps] : [];
86
127
  this._remove(key, toRemove, keys, flatType);
87
128
  this._replace(key, toReplace);
@@ -99,7 +140,8 @@ else if (key !== "_id") this._set(key, value);
99
140
  * @param keys Ordered list of key property names
100
141
  * @param left Base token for *left* expression (e.g. `"$$el"`)
101
142
  * @param right Base token for *right* expression (e.g. `"$$this"`)
102
- */ _keysEqual(keys, left, right) {
143
+ */
144
+ _keysEqual(keys, left, right) {
103
145
  const eqs = keys.map((k) => ({ $eq: [`${left}.${k}`, `${right}.${k}`] }));
104
146
  return eqs.length === 1 ? eqs[0] : { $and: eqs };
105
147
  }
@@ -109,24 +151,26 @@ else if (key !== "_id") this._set(key, value);
109
151
  * @param key Dotted path to the array
110
152
  * @param input New array value (may be `undefined`)
111
153
  * @private
112
- */ _replace(key, input) {
154
+ */
155
+ _replace(key, input) {
113
156
  if (input) this._set(key, input);
114
157
  }
115
158
  /**
116
159
  * `$insert`
117
160
  * - plain append → $concatArrays
118
161
  * - unique / keyed → delegate to _upsert (insert-or-update)
119
- */ _insert(key, input, keys, flatType) {
162
+ */
163
+ _insert(key, input, keys, flatType) {
120
164
  if (!input?.length) return;
121
- const uniqueItems = flatType.metadata?.has("expect.array.uniqueItems");
122
- if (uniqueItems || keys.length > 0) this._upsert(key, input, keys, flatType);
123
- else this._set(key, { $concatArrays: [{ $ifNull: [`$${key}`, []] }, input] });
165
+ if (flatType.metadata?.has("expect.array.uniqueItems") || keys.length > 0) this._upsert(key, input, keys, flatType);
166
+ else this._set(key, { $concatArrays: [{ $ifNull: [`$${key}`, []] }, input] });
124
167
  }
125
168
  /**
126
169
  * `$upsert`
127
170
  * - keyed → remove existing matching by key(s) then append candidate
128
171
  * - unique → $setUnion (deep equality)
129
- */ _upsert(key, input, keys, flatType) {
172
+ */
173
+ _upsert(key, input, keys, flatType) {
130
174
  if (!input?.length) return;
131
175
  if (keys.length > 0) {
132
176
  const mergeStrategy = flatType.metadata?.get("db.patch.strategy") === "merge";
@@ -167,7 +211,8 @@ else this._set(key, { $concatArrays: [{ $ifNull: [`$${key}`, []] }, input] });
167
211
  * `$update`
168
212
  * - keyed → map array and merge / replace matching element(s)
169
213
  * - non-keyed → behave like `$addToSet` (insert only when not present)
170
- */ _update(key, input, keys, flatType) {
214
+ */
215
+ _update(key, input, keys, flatType) {
171
216
  if (!input?.length) return;
172
217
  if (keys.length > 0) {
173
218
  const mergeStrategy = flatType.metadata?.get("db.patch.strategy") === "merge";
@@ -190,7 +235,8 @@ else this._set(key, { $concatArrays: [{ $ifNull: [`$${key}`, []] }, input] });
190
235
  * `$remove`
191
236
  * - keyed → filter out any element whose key set matches a payload item
192
237
  * - non-keyed → deep equality remove (`$setDifference`)
193
- */ _remove(key, input, keys, _flatType) {
238
+ */
239
+ _remove(key, input, keys, _flatType) {
194
240
  if (!input?.length) return;
195
241
  if (keys.length > 0) this._set(key, { $let: {
196
242
  vars: { rem: input },
@@ -204,46 +250,29 @@ else this._set(key, { $concatArrays: [{ $ifNull: [`$${key}`, []] }, input] });
204
250
  } } } }
205
251
  } }
206
252
  } });
207
- else this._set(key, { $setDifference: [{ $ifNull: [`$${key}`, []] }, input] });
208
- }
209
- constructor(collection, payload) {
210
- _define_property$1(this, "collection", void 0);
211
- _define_property$1(this, "payload", void 0);
212
- /**
213
- * Internal accumulator: filter passed to `updateOne()`.
214
- * Filled only with the `_id` field right now.
215
- */ _define_property$1(this, "filterObj", void 0);
216
- /** MongoDB *update* document being built. */ _define_property$1(this, "updatePipeline", void 0);
217
- /** Current `$set` stage being populated. */ _define_property$1(this, "currentSetStage", void 0);
218
- /** Additional *options* (mainly `arrayFilters`). */ _define_property$1(this, "optionsObj", void 0);
219
- this.collection = collection;
220
- this.payload = payload;
221
- this.filterObj = {};
222
- this.updatePipeline = [];
223
- this.currentSetStage = null;
224
- this.optionsObj = {};
253
+ else this._set(key, { $setDifference: [{ $ifNull: [`$${key}`, []] }, input] });
225
254
  }
226
255
  };
227
- _define_property$1(CollectionPatcher, "getKeyProps", __atscript_db.getKeyProps);
228
-
229
256
  //#endregion
230
- //#region packages/db-mongo/src/lib/mongo-types.ts
257
+ //#region src/lib/mongo-types.ts
231
258
  const INDEX_PREFIX = "atscript__";
232
259
  const DEFAULT_INDEX_NAME = "DEFAULT";
233
260
  const JOINED_PREFIX = "__joined_";
234
261
  function mongoIndexKey(type, name) {
235
- const cleanName = name.replace(/[^a-z0-9_.-]/gi, "_").replace(/_+/g, "_").slice(0, 127 - INDEX_PREFIX.length - type.length - 2);
236
- return `${INDEX_PREFIX}${type}__${cleanName}`;
262
+ return `${INDEX_PREFIX}${type}__${name.replace(/[^a-z0-9_.-]/gi, "_").replace(/_+/g, "_").slice(0, 117 - type.length - 2)}`;
237
263
  }
238
-
239
264
  //#endregion
240
- //#region packages/db-mongo/src/lib/mongo-relations.ts
265
+ //#region src/lib/mongo-relations.ts
241
266
  function buildPKKey(primaryKeys, doc) {
242
- if (primaryKeys.length === 1) return String(doc[primaryKeys[0]] ?? "");
267
+ if (primaryKeys.length === 1) {
268
+ const val = doc[primaryKeys[0]];
269
+ return val == null ? "" : `${val}`;
270
+ }
243
271
  let key = "";
244
272
  for (let i = 0; i < primaryKeys.length; i++) {
245
273
  if (i > 0) key += "\0";
246
- key += String(doc[primaryKeys[i]] ?? "");
274
+ const val = doc[primaryKeys[i]];
275
+ key += val == null ? "" : `${val}`;
247
276
  }
248
277
  return key;
249
278
  }
@@ -270,23 +299,23 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
270
299
  if (pkMatchFilter) {
271
300
  const pipeline = [{ $match: pkMatchFilter }];
272
301
  for (const meta of relMeta) pipeline.push(...meta.stages);
273
- const results = await host.collection.aggregate(pipeline, host._getSessionOpts()).toArray();
274
- mergeRelationResults(rows, results, primaryKeys, relMeta);
302
+ mergeRelationResults(rows, await host.collection.aggregate(pipeline, host._getSessionOpts()).toArray(), primaryKeys, relMeta);
275
303
  } else for (const row of rows) for (const meta of relMeta) row[meta.name] = meta.isArray ? [] : null;
276
304
  await loadNestedRelations(rows, relMeta, tableResolver);
277
305
  }
278
- /** Builds a $match filter to re-select source rows by PK. */ function buildPKMatchFilter(rows, primaryKeys) {
306
+ /** Builds a $match filter to re-select source rows by PK. */
307
+ function buildPKMatchFilter(rows, primaryKeys) {
279
308
  if (primaryKeys.length === 1) {
280
309
  const pk = primaryKeys[0];
281
- const values = new Set();
310
+ const values = /* @__PURE__ */ new Set();
282
311
  for (const row of rows) {
283
312
  const v = row[pk];
284
- if (v !== null && v !== undefined) values.add(v);
313
+ if (v !== null && v !== void 0) values.add(v);
285
314
  }
286
- if (values.size === 0) return undefined;
315
+ if (values.size === 0) return;
287
316
  return { [pk]: { $in: [...values] } };
288
317
  }
289
- const seen = new Set();
318
+ const seen = /* @__PURE__ */ new Set();
290
319
  const orFilters = [];
291
320
  for (const row of rows) {
292
321
  const key = buildPKKey(primaryKeys, row);
@@ -296,7 +325,7 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
296
325
  let valid = true;
297
326
  for (const pk of primaryKeys) {
298
327
  const val = row[pk];
299
- if (val === null || val === undefined) {
328
+ if (val === null || val === void 0) {
300
329
  valid = false;
301
330
  break;
302
331
  }
@@ -304,18 +333,20 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
304
333
  }
305
334
  if (valid) orFilters.push(condition);
306
335
  }
307
- if (orFilters.length === 0) return undefined;
336
+ if (orFilters.length === 0) return;
308
337
  return orFilters.length === 1 ? orFilters[0] : { $or: orFilters };
309
338
  }
310
- /** Dispatches to the correct $lookup builder based on relation direction. */ function buildRelationLookup(host, withRel, relation, foreignKeys, tableResolver) {
339
+ /** Dispatches to the correct $lookup builder based on relation direction. */
340
+ function buildRelationLookup(host, withRel, relation, foreignKeys, tableResolver) {
311
341
  switch (relation.direction) {
312
342
  case "to": return buildToLookup(withRel, relation, foreignKeys);
313
343
  case "from": return buildFromLookup(host, withRel, relation, tableResolver);
314
344
  case "via": return buildViaLookup(host, withRel, relation, tableResolver);
315
- default: return undefined;
345
+ default: return;
316
346
  }
317
347
  }
318
- /** Builds `let` variable bindings and the corresponding `$expr` match for `$lookup`. */ function buildLookupJoin(localFields, remoteFields, varPrefix) {
348
+ /** Builds `let` variable bindings and the corresponding `$expr` match for `$lookup`. */
349
+ function buildLookupJoin(localFields, remoteFields, varPrefix) {
319
350
  const letVars = {};
320
351
  for (let i = 0; i < localFields.length; i++) letVars[`${varPrefix}${i}`] = `$${localFields[i]}`;
321
352
  if (remoteFields.length === 1) return {
@@ -329,32 +360,33 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
329
360
  exprMatch: { $and: andClauses }
330
361
  };
331
362
  }
332
- /** $lookup for TO relations (FK is on this table → target). Always single-valued. */ function buildToLookup(withRel, relation, foreignKeys) {
363
+ /** $lookup for TO relations (FK is on this table → target). Always single-valued. */
364
+ function buildToLookup(withRel, relation, foreignKeys) {
333
365
  const fk = findFKForRelation(relation, foreignKeys);
334
- if (!fk) return undefined;
366
+ if (!fk) return;
335
367
  const innerPipeline = buildLookupInnerPipeline(withRel, fk.targetFields);
336
368
  const { letVars, exprMatch } = buildLookupJoin(fk.localFields, fk.targetFields, "fk_");
337
- const stages = [{ $lookup: {
338
- from: fk.targetTable,
339
- let: letVars,
340
- pipeline: [{ $match: { $expr: exprMatch } }, ...innerPipeline],
341
- as: withRel.name
342
- } }, { $unwind: {
343
- path: `$${withRel.name}`,
344
- preserveNullAndEmptyArrays: true
345
- } }];
346
369
  return {
347
- stages,
370
+ stages: [{ $lookup: {
371
+ from: fk.targetTable,
372
+ let: letVars,
373
+ pipeline: [{ $match: { $expr: exprMatch } }, ...innerPipeline],
374
+ as: withRel.name
375
+ } }, { $unwind: {
376
+ path: `$${withRel.name}`,
377
+ preserveNullAndEmptyArrays: true
378
+ } }],
348
379
  isArray: false
349
380
  };
350
381
  }
351
- /** $lookup for FROM relations (FK is on target → this table). */ function buildFromLookup(host, withRel, relation, tableResolver) {
382
+ /** $lookup for FROM relations (FK is on target → this table). */
383
+ function buildFromLookup(host, withRel, relation, tableResolver) {
352
384
  const targetType = relation.targetType();
353
- if (!targetType || !tableResolver) return undefined;
385
+ if (!targetType || !tableResolver) return;
354
386
  const targetMeta = tableResolver(targetType);
355
- if (!targetMeta) return undefined;
387
+ if (!targetMeta) return;
356
388
  const remoteFK = findRemoteFK(targetMeta, host._table.tableName, relation.alias);
357
- if (!remoteFK) return undefined;
389
+ if (!remoteFK) return;
358
390
  const targetTableName = resolveRelTargetTableName(relation);
359
391
  const innerPipeline = buildLookupInnerPipeline(withRel, remoteFK.fields);
360
392
  const { letVars, exprMatch } = buildLookupJoin(remoteFK.targetFields, remoteFK.fields, "pk_");
@@ -373,46 +405,47 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
373
405
  isArray: relation.isArray
374
406
  };
375
407
  }
376
- /** $lookup for VIA relations (M:N through junction table). Always array. */ function buildViaLookup(host, withRel, relation, tableResolver) {
377
- if (!relation.viaType || !tableResolver) return undefined;
408
+ /** $lookup for VIA relations (M:N through junction table). Always array. */
409
+ function buildViaLookup(host, withRel, relation, tableResolver) {
410
+ if (!relation.viaType || !tableResolver) return;
378
411
  const junctionType = relation.viaType();
379
- if (!junctionType) return undefined;
412
+ if (!junctionType) return;
380
413
  const junctionMeta = tableResolver(junctionType);
381
- if (!junctionMeta) return undefined;
414
+ if (!junctionMeta) return;
382
415
  const junctionTableName = junctionType.metadata?.get("db.table") || junctionType.id || "";
383
416
  const targetTableName = resolveRelTargetTableName(relation);
384
417
  const fkToThis = findRemoteFK(junctionMeta, host._table.tableName);
385
- if (!fkToThis) return undefined;
418
+ if (!fkToThis) return;
386
419
  const fkToTarget = findRemoteFK(junctionMeta, targetTableName);
387
- if (!fkToTarget) return undefined;
420
+ if (!fkToTarget) return;
388
421
  const innerPipeline = buildLookupInnerPipeline(withRel, fkToTarget.targetFields);
389
422
  const { letVars, exprMatch } = buildLookupJoin(fkToThis.targetFields, fkToThis.fields, "pk_");
390
- const stages = [{ $lookup: {
391
- from: junctionTableName,
392
- let: letVars,
393
- pipeline: [
394
- { $match: { $expr: exprMatch } },
395
- { $lookup: {
396
- from: targetTableName,
397
- localField: fkToTarget.fields[0],
398
- foreignField: fkToTarget.targetFields[0],
399
- pipeline: innerPipeline,
400
- as: "__target"
401
- } },
402
- { $unwind: {
403
- path: "$__target",
404
- preserveNullAndEmptyArrays: false
405
- } },
406
- { $replaceRoot: { newRoot: "$__target" } }
407
- ],
408
- as: withRel.name
409
- } }];
410
423
  return {
411
- stages,
424
+ stages: [{ $lookup: {
425
+ from: junctionTableName,
426
+ let: letVars,
427
+ pipeline: [
428
+ { $match: { $expr: exprMatch } },
429
+ { $lookup: {
430
+ from: targetTableName,
431
+ localField: fkToTarget.fields[0],
432
+ foreignField: fkToTarget.targetFields[0],
433
+ pipeline: innerPipeline,
434
+ as: "__target"
435
+ } },
436
+ { $unwind: {
437
+ path: "$__target",
438
+ preserveNullAndEmptyArrays: false
439
+ } },
440
+ { $replaceRoot: { newRoot: "$__target" } }
441
+ ],
442
+ as: withRel.name
443
+ } }],
412
444
  isArray: true
413
445
  };
414
446
  }
415
- /** Builds inner pipeline stages for relation controls ($sort, $limit, $skip, $select, filter). */ function buildLookupInnerPipeline(withRel, requiredFields) {
447
+ /** Builds inner pipeline stages for relation controls ($sort, $limit, $skip, $select, filter). */
448
+ function buildLookupInnerPipeline(withRel, requiredFields) {
416
449
  const pipeline = [];
417
450
  const flatRel = withRel;
418
451
  const nested = withRel.controls || {};
@@ -424,7 +457,7 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
424
457
  if (filter && Object.keys(filter).length > 0) pipeline.push({ $match: require_mongo_filter.buildMongoFilter(filter) });
425
458
  if (sort) pipeline.push({ $sort: sort });
426
459
  if (skip) pipeline.push({ $skip: skip });
427
- if (limit !== null && limit !== undefined) pipeline.push({ $limit: limit });
460
+ if (limit !== null && limit !== void 0) pipeline.push({ $limit: limit });
428
461
  if (select) {
429
462
  const projection = {};
430
463
  for (const f of select) projection[f] = 1;
@@ -434,13 +467,14 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
434
467
  }
435
468
  return pipeline;
436
469
  }
437
- /** Extracts nested $with from a WithRelation's controls. */ function extractNestedWith(withRel) {
470
+ /** Extracts nested $with from a WithRelation's controls. */
471
+ function extractNestedWith(withRel) {
438
472
  const flatRel = withRel;
439
- const nested = withRel.controls || {};
440
- const nestedWith = nested.$with || flatRel.$with;
441
- return nestedWith && nestedWith.length > 0 ? nestedWith : undefined;
473
+ const nestedWith = (withRel.controls || {}).$with || flatRel.$with;
474
+ return nestedWith && nestedWith.length > 0 ? nestedWith : void 0;
442
475
  }
443
- /** Post-processes nested $with by delegating to the target table's own relation loading. */ async function loadNestedRelations(rows, relMeta, tableResolver) {
476
+ /** Post-processes nested $with by delegating to the target table's own relation loading. */
477
+ async function loadNestedRelations(rows, relMeta, tableResolver) {
444
478
  if (!tableResolver) return;
445
479
  const tasks = [];
446
480
  for (const meta of relMeta) {
@@ -453,26 +487,28 @@ async function loadRelationsImpl(host, rows, withRelations, relations, foreignKe
453
487
  for (const row of rows) {
454
488
  const val = row[meta.name];
455
489
  if (meta.isArray && Array.isArray(val)) for (const item of val) subRows.push(item);
456
- else if (val && typeof val === "object") subRows.push(val);
490
+ else if (val && typeof val === "object") subRows.push(val);
457
491
  }
458
492
  if (subRows.length === 0) continue;
459
493
  tasks.push(targetTable.loadRelations(subRows, meta.nestedWith));
460
494
  }
461
495
  await Promise.all(tasks);
462
496
  }
463
- /** Merges aggregation results back onto the original rows by PK. */ function mergeRelationResults(rows, results, primaryKeys, relMeta) {
464
- const resultIndex = new Map();
497
+ /** Merges aggregation results back onto the original rows by PK. */
498
+ function mergeRelationResults(rows, results, primaryKeys, relMeta) {
499
+ const resultIndex = /* @__PURE__ */ new Map();
465
500
  for (const doc of results) resultIndex.set(buildPKKey(primaryKeys, doc), doc);
466
501
  for (const row of rows) {
467
502
  const enriched = resultIndex.get(buildPKKey(primaryKeys, row));
468
503
  for (const meta of relMeta) if (enriched) {
469
504
  const value = enriched[meta.name];
470
505
  if (!meta.isArray && Array.isArray(value)) row[meta.name] = value[0] ?? null;
471
- else row[meta.name] = value ?? (meta.isArray ? [] : null);
506
+ else row[meta.name] = value ?? (meta.isArray ? [] : null);
472
507
  } else row[meta.name] = meta.isArray ? [] : null;
473
508
  }
474
509
  }
475
- /** Finds FK entry for a TO relation from this table's foreignKeys map. */ function findFKForRelation(relation, foreignKeys) {
510
+ /** Finds FK entry for a TO relation from this table's foreignKeys map. */
511
+ function findFKForRelation(relation, foreignKeys) {
476
512
  const targetTableName = resolveRelTargetTableName(relation);
477
513
  for (const fk of foreignKeys.values()) if (relation.alias) {
478
514
  if (fk.alias === relation.alias) return {
@@ -485,22 +521,22 @@ else row[meta.name] = value ?? (meta.isArray ? [] : null);
485
521
  targetFields: fk.targetFields,
486
522
  targetTable: fk.targetTable
487
523
  };
488
- return undefined;
489
524
  }
490
- /** Finds a FK on a remote table that points back to the given table name. */ function findRemoteFK(target, thisTableName, alias) {
525
+ /** Finds a FK on a remote table that points back to the given table name. */
526
+ function findRemoteFK(target, thisTableName, alias) {
491
527
  for (const fk of target.foreignKeys.values()) {
492
528
  if (alias && fk.alias === alias && fk.targetTable === thisTableName) return fk;
493
529
  if (!alias && fk.targetTable === thisTableName) return fk;
494
530
  }
495
- return undefined;
496
531
  }
497
- /** Resolves the target table/collection name from a relation's target type. */ function resolveRelTargetTableName(relation) {
532
+ /** Resolves the target table/collection name from a relation's target type. */
533
+ function resolveRelTargetTableName(relation) {
498
534
  const targetType = relation.targetType();
499
535
  return targetType?.metadata?.get("db.table") || targetType?.id || "";
500
536
  }
501
-
502
537
  //#endregion
503
- //#region packages/db-mongo/src/lib/mongo-search.ts
538
+ //#region src/lib/mongo-search.ts
539
+ /** Returns available search indexes as generic metadata for UI. */
504
540
  function getSearchIndexesImpl(host) {
505
541
  const result = [];
506
542
  for (const [name, index] of host.getMongoSearchIndexes()) result.push({
@@ -510,40 +546,43 @@ function getSearchIndexesImpl(host) {
510
546
  });
511
547
  return result;
512
548
  }
549
+ /** Checks if any vector search index is available. */
513
550
  function isVectorSearchableImpl(host) {
514
551
  for (const index of host.getMongoSearchIndexes().values()) if (index.type === "vector") return true;
515
552
  return false;
516
553
  }
554
+ /** Text search via $search aggregation stage. */
517
555
  async function searchImpl(host, text, query, indexName) {
518
556
  const stage = buildSearchStage(host, text, indexName);
519
557
  if (!stage) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
520
558
  return runSearchPipeline(host, stage, query, "search");
521
559
  }
560
+ /** Text search with faceted count. */
522
561
  async function searchWithCountImpl(host, text, query, indexName) {
523
562
  const stage = buildSearchStage(host, text, indexName);
524
563
  if (!stage) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
525
564
  return runSearchWithCountPipeline(host, stage, query, "searchWithCount");
526
565
  }
566
+ /** Vector search via $vectorSearch aggregation stage. */
527
567
  async function vectorSearchImpl(host, vector, query, indexName) {
528
568
  const controls = query.controls || {};
529
- const stage = buildVectorSearchStage(host, vector, indexName, controls.$limit);
530
- const threshold = resolveThreshold(host, controls, indexName);
531
- return runSearchPipeline(host, stage, query, "vectorSearch", threshold);
569
+ return runSearchPipeline(host, buildVectorSearchStage(host, vector, indexName, controls.$limit), query, "vectorSearch", resolveThreshold(host, controls, indexName));
532
570
  }
571
+ /** Vector search with faceted count. */
533
572
  async function vectorSearchWithCountImpl(host, vector, query, indexName) {
534
573
  const controls = query.controls || {};
535
- const stage = buildVectorSearchStage(host, vector, indexName, controls.$limit);
536
- const threshold = resolveThreshold(host, controls, indexName);
537
- return runSearchWithCountPipeline(host, stage, query, "vectorSearchWithCount", threshold);
574
+ return runSearchWithCountPipeline(host, buildVectorSearchStage(host, vector, indexName, controls.$limit), query, "vectorSearchWithCount", resolveThreshold(host, controls, indexName));
538
575
  }
539
- /** Resolves the effective threshold: query-time $threshold > schema-level @db.search.vector.threshold. */ function resolveThreshold(host, controls, indexName) {
576
+ /** Resolves the effective threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
577
+ function resolveThreshold(host, controls, indexName) {
540
578
  const queryThreshold = controls.$threshold;
541
- if (queryThreshold !== undefined) return queryThreshold;
579
+ if (queryThreshold !== void 0) return queryThreshold;
542
580
  return host.getVectorThreshold(indexName);
543
581
  }
544
- /** Builds a MongoDB $search pipeline stage for text search. */ function buildSearchStage(host, text, indexName) {
582
+ /** Builds a MongoDB $search pipeline stage for text search. */
583
+ function buildSearchStage(host, text, indexName) {
545
584
  const index = host.getMongoSearchIndex(indexName);
546
- if (!index) return undefined;
585
+ if (!index) return;
547
586
  if (index.type === "vector") throw new Error("Vector indexes cannot be used with text search. Use vectorSearch() instead.");
548
587
  return { $search: {
549
588
  index: index.key,
@@ -553,7 +592,8 @@ async function vectorSearchWithCountImpl(host, vector, query, indexName) {
553
592
  }
554
593
  } };
555
594
  }
556
- /** Builds a $vectorSearch aggregation stage from a pre-computed vector. */ function buildVectorSearchStage(host, vector, indexName, limit) {
595
+ /** Builds a $vectorSearch aggregation stage from a pre-computed vector. */
596
+ function buildVectorSearchStage(host, vector, indexName, limit) {
557
597
  let index;
558
598
  if (indexName) {
559
599
  const found = host.getMongoSearchIndex(indexName);
@@ -580,11 +620,12 @@ async function vectorSearchWithCountImpl(host, vector, query, indexName) {
580
620
  limit: limit || 20
581
621
  } };
582
622
  }
583
- /** Runs a search/vector pipeline and returns results. Shared by search + vectorSearch. */ async function runSearchPipeline(host, stage, query, label, threshold) {
623
+ /** Runs a search/vector pipeline and returns results. Shared by search + vectorSearch. */
624
+ async function runSearchPipeline(host, stage, query, label, threshold) {
584
625
  const filter = require_mongo_filter.buildMongoFilter(query.filter);
585
626
  const controls = query.controls || {};
586
627
  const pipeline = [stage];
587
- if (threshold !== undefined) {
628
+ if (threshold !== void 0) {
588
629
  pipeline.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
589
630
  pipeline.push({ $match: { _score: { $gte: threshold } } });
590
631
  }
@@ -592,16 +633,17 @@ async function vectorSearchWithCountImpl(host, vector, query, indexName) {
592
633
  if (controls.$sort) pipeline.push({ $sort: controls.$sort });
593
634
  if (controls.$skip) pipeline.push({ $skip: controls.$skip });
594
635
  if (controls.$limit) pipeline.push({ $limit: controls.$limit });
595
- else pipeline.push({ $limit: 1e3 });
636
+ else pipeline.push({ $limit: 1e3 });
596
637
  if (controls.$select) pipeline.push({ $project: controls.$select.asProjection });
597
638
  host._log(`aggregate (${label})`, pipeline);
598
639
  return host.collection.aggregate(pipeline, host._getSessionOpts()).toArray();
599
640
  }
600
- /** Runs a search/vector pipeline with $facet for count. Shared by searchWithCount + vectorSearchWithCount. */ async function runSearchWithCountPipeline(host, stage, query, label, threshold) {
641
+ /** Runs a search/vector pipeline with $facet for count. Shared by searchWithCount + vectorSearchWithCount. */
642
+ async function runSearchWithCountPipeline(host, stage, query, label, threshold) {
601
643
  const filter = require_mongo_filter.buildMongoFilter(query.filter);
602
644
  const controls = query.controls || {};
603
645
  const preStages = [];
604
- if (threshold !== undefined) {
646
+ if (threshold !== void 0) {
605
647
  preStages.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
606
648
  preStages.push({ $match: { _score: { $gte: threshold } } });
607
649
  }
@@ -626,9 +668,8 @@ else pipeline.push({ $limit: 1e3 });
626
668
  count: result[0]?.meta[0]?.count || 0
627
669
  };
628
670
  }
629
-
630
671
  //#endregion
631
- //#region packages/db-mongo/src/lib/mongo-schema-sync.ts
672
+ //#region src/lib/mongo-schema-sync.ts
632
673
  const DESTRUCTIVE_OPTION_KEYS = new Set([
633
674
  "capped",
634
675
  "capped.size",
@@ -646,7 +687,7 @@ function getDesiredTableOptionsImpl(cappedOptions) {
646
687
  key: "capped.size",
647
688
  value: String(cappedOptions.size)
648
689
  }];
649
- if (cappedOptions.max !== undefined) opts.push({
690
+ if (cappedOptions.max !== void 0) opts.push({
650
691
  key: "capped.max",
651
692
  value: String(cappedOptions.max)
652
693
  });
@@ -661,23 +702,23 @@ async function getExistingTableOptionsImpl(host) {
661
702
  key: "capped",
662
703
  value: "true"
663
704
  }];
664
- if (collOpts.size !== undefined) opts.push({
705
+ if (collOpts.size !== void 0) opts.push({
665
706
  key: "capped.size",
666
707
  value: String(collOpts.size)
667
708
  });
668
- if (collOpts.max !== undefined) opts.push({
709
+ if (collOpts.max !== void 0) opts.push({
669
710
  key: "capped.max",
670
711
  value: String(collOpts.max)
671
712
  });
672
713
  return opts;
673
714
  }
674
715
  async function ensureTableImpl(host, table) {
675
- if (table instanceof __atscript_db.AtscriptDbView && !table.isExternal) return ensureView(host, table);
716
+ if (table instanceof _atscript_db.AtscriptDbView && !table.isExternal) return ensureView(host, table);
676
717
  return host.ensureCollectionExists();
677
718
  }
678
- /** Creates a MongoDB view from the AtscriptDbView's view plan. */ async function ensureView(host, view) {
679
- const exists = await host.collectionExists();
680
- if (exists) return;
719
+ /** Creates a MongoDB view from the AtscriptDbView's view plan. */
720
+ async function ensureView(host, view) {
721
+ if (await host.collectionExists()) return;
681
722
  const plan = view.viewPlan;
682
723
  const columns = view.getViewColumnMappings();
683
724
  const pipeline = [];
@@ -699,13 +740,14 @@ async function ensureTableImpl(host, table) {
699
740
  pipeline.push({ $match: matchExpr });
700
741
  }
701
742
  const hasAggregates = columns.some((c) => c.aggFn);
702
- /** Resolves a column to its MongoDB source field path. */ const colSourceField = (col) => col.sourceTable === plan.entryTable ? `$${col.sourceColumn}` : `$${JOINED_PREFIX}${col.sourceTable}.${col.sourceColumn}`;
743
+ /** Resolves a column to its MongoDB source field path. */
744
+ const colSourceField = (col) => col.sourceTable === plan.entryTable ? `$${col.sourceColumn}` : `$${JOINED_PREFIX}${col.sourceTable}.${col.sourceColumn}`;
703
745
  if (hasAggregates) {
704
746
  const group = { _id: {} };
705
747
  const project = { _id: 0 };
706
748
  for (const col of columns) if (col.aggFn) {
707
749
  if (col.aggFn === "count" && col.aggField === "*") group[col.viewColumn] = { $sum: 1 };
708
- else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
750
+ else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
709
751
  project[col.viewColumn] = `$${col.viewColumn}`;
710
752
  } else {
711
753
  group._id[col.viewColumn] = colSourceField(col);
@@ -728,11 +770,10 @@ else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
728
770
  pipeline
729
771
  });
730
772
  }
731
- /** Extracts localField/foreignField from a join condition. */ function resolveJoinFields(condition, entryTable, joinTable) {
732
- const comp = "$and" in condition ? condition.$and[0] : condition;
733
- const c = comp;
734
- const leftTable = c.left.type ? c.left.type()?.metadata?.get("db.table") || "" : entryTable;
735
- if (leftTable === joinTable) return {
773
+ /** Extracts localField/foreignField from a join condition. */
774
+ function resolveJoinFields(condition, entryTable, joinTable) {
775
+ const c = "$and" in condition ? condition.$and[0] : condition;
776
+ if ((c.left.type ? c.left.type()?.metadata?.get("db.table") || "" : entryTable) === joinTable) return {
736
777
  localField: c.right.field,
737
778
  foreignField: c.left.field
738
779
  };
@@ -741,7 +782,8 @@ else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
741
782
  foreignField: c.right.field
742
783
  };
743
784
  }
744
- /** Translates an AtscriptQueryNode to a MongoDB $match expression. */ function queryNodeToMatch(node, entryTable) {
785
+ /** Translates an AtscriptQueryNode to a MongoDB $match expression. */
786
+ function queryNodeToMatch(node, entryTable) {
745
787
  if ("$and" in node) {
746
788
  const items = node.$and;
747
789
  const result = [];
@@ -765,7 +807,8 @@ else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
765
807
  if (comp.op === "$ne") return { [fieldPath]: { $ne: comp.right } };
766
808
  return { [fieldPath]: { [comp.op]: comp.right } };
767
809
  }
768
- /** Resolves a field ref to a MongoDB dot path for view pipeline expressions. */ function resolveViewFieldPath(ref, entryTable) {
810
+ /** Resolves a field ref to a MongoDB dot path for view pipeline expressions. */
811
+ function resolveViewFieldPath(ref, entryTable) {
769
812
  if (!ref.type) return ref.field;
770
813
  const table = ref.type()?.metadata?.get("db.table") || "";
771
814
  if (table === entryTable) return ref.field;
@@ -774,7 +817,8 @@ else group[col.viewColumn] = { [`$${col.aggFn}`]: colSourceField(col) };
774
817
  /**
775
818
  * Translates an AtscriptQueryNode to a MongoDB $match for use after $group (HAVING).
776
819
  * After $group, aggregate fields are top-level and dimension fields are under _id.
777
- */ function queryNodeToHaving(node, columns) {
820
+ */
821
+ function queryNodeToHaving(node, columns) {
778
822
  const colMap = new Map(columns.map((c) => [c.viewColumn, c]));
779
823
  const resolveHavingField = (ref) => {
780
824
  if (!ref.type) {
@@ -855,7 +899,7 @@ async function syncColumnsImpl(host, diff) {
855
899
  const setSpec = {};
856
900
  for (const field of diff.added) {
857
901
  const defaultVal = resolveSyncDefault(field);
858
- if (defaultVal !== undefined) setSpec[field.physicalName] = defaultVal;
902
+ if (defaultVal !== void 0) setSpec[field.physicalName] = defaultVal;
859
903
  added.push(field.physicalName);
860
904
  }
861
905
  if (Object.keys(setSpec).length > 0) update.$set = setSpec;
@@ -872,14 +916,14 @@ async function dropColumnsImpl(host, columns) {
872
916
  for (const col of columns) unsetSpec[col] = "";
873
917
  await host.collection.updateMany({}, { $unset: unsetSpec }, host._getSessionOpts());
874
918
  }
875
- /** Resolves a field's default value for bulk $set during column sync. */ function resolveSyncDefault(field) {
876
- if (!field.defaultValue) return field.optional ? null : undefined;
919
+ /** Resolves a field's default value for bulk $set during column sync. */
920
+ function resolveSyncDefault(field) {
921
+ if (!field.defaultValue) return field.optional ? null : void 0;
877
922
  if (field.defaultValue.kind === "value") return field.defaultValue.value;
878
- return undefined;
879
923
  }
880
924
  async function syncIndexesImpl(host) {
881
925
  await host.ensureCollectionExists();
882
- const allIndexes = new Map();
926
+ const allIndexes = /* @__PURE__ */ new Map();
883
927
  for (const [key, index] of host._table.indexes.entries()) {
884
928
  const fields = {};
885
929
  const weights = {};
@@ -912,20 +956,19 @@ async function syncIndexesImpl(host) {
912
956
  const existingIndexes = await host.collection.listIndexes().toArray();
913
957
  const indexesToCreate = new Map(allIndexes);
914
958
  for (const remote of existingIndexes) {
915
- if (!remote.name.startsWith(INDEX_PREFIX)) continue;
959
+ if (!remote.name.startsWith("atscript__")) continue;
916
960
  if (indexesToCreate.has(remote.name)) {
917
961
  const local = indexesToCreate.get(remote.name);
918
962
  switch (local.type) {
919
963
  case "plain":
920
964
  case "unique":
921
- case "text": {
965
+ case "text":
922
966
  if ((local.type === "text" || objMatch(local.fields, remote.key)) && objMatch(local.weights || {}, remote.weights || {})) indexesToCreate.delete(remote.name);
923
- else {
967
+ else {
924
968
  host._log("dropIndex", remote.name);
925
969
  await host.collection.dropIndex(remote.name);
926
970
  }
927
971
  break;
928
- }
929
972
  default:
930
973
  }
931
974
  } else {
@@ -934,13 +977,12 @@ else {
934
977
  }
935
978
  }
936
979
  for (const [key, value] of allIndexes.entries()) switch (value.type) {
937
- case "plain": {
980
+ case "plain":
938
981
  if (!indexesToCreate.has(key)) continue;
939
982
  host._log("createIndex", key, value.fields);
940
983
  await host.collection.createIndex(value.fields, { name: key });
941
984
  break;
942
- }
943
- case "unique": {
985
+ case "unique":
944
986
  if (!indexesToCreate.has(key)) continue;
945
987
  host._log("createIndex (unique)", key, value.fields);
946
988
  await host.collection.createIndex(value.fields, {
@@ -948,8 +990,7 @@ else {
948
990
  unique: true
949
991
  });
950
992
  break;
951
- }
952
- case "text": {
993
+ case "text":
953
994
  if (!indexesToCreate.has(key)) continue;
954
995
  host._log("createIndex (text)", key, value.fields);
955
996
  await host.collection.createIndex(value.fields, {
@@ -957,14 +998,13 @@ else {
957
998
  name: key
958
999
  });
959
1000
  break;
960
- }
961
1001
  default:
962
1002
  }
963
1003
  try {
964
- const toUpdate = new Set();
1004
+ const toUpdate = /* @__PURE__ */ new Set();
965
1005
  const existingSearchIndexes = await host.collection.listSearchIndexes().toArray();
966
1006
  for (const remote of existingSearchIndexes) {
967
- if (!remote.name.startsWith(INDEX_PREFIX)) continue;
1007
+ if (!remote.name.startsWith("atscript__")) continue;
968
1008
  if (indexesToCreate.has(remote.name)) {
969
1009
  const local = indexesToCreate.get(remote.name);
970
1010
  const right = remote.latestDefinition;
@@ -973,14 +1013,13 @@ else {
973
1013
  case "search_text": {
974
1014
  const left = local.definition;
975
1015
  if (left.analyzer === right.analyzer && fieldsMatch(left.mappings.fields || {}, right.mappings.fields || {})) indexesToCreate.delete(remote.name);
976
- else toUpdate.add(remote.name);
1016
+ else toUpdate.add(remote.name);
977
1017
  break;
978
1018
  }
979
- case "vector": {
1019
+ case "vector":
980
1020
  if (vectorFieldsMatch(local.definition.fields || [], right.fields || [])) indexesToCreate.delete(remote.name);
981
- else toUpdate.add(remote.name);
1021
+ else toUpdate.add(remote.name);
982
1022
  break;
983
- }
984
1023
  default:
985
1024
  }
986
1025
  } else if (remote.status !== "DELETING") {
@@ -991,7 +1030,7 @@ else toUpdate.add(remote.name);
991
1030
  for (const [key, value] of indexesToCreate.entries()) switch (value.type) {
992
1031
  case "dynamic_text":
993
1032
  case "search_text":
994
- case "vector": {
1033
+ case "vector":
995
1034
  if (toUpdate.has(key)) {
996
1035
  host._log("updateSearchIndex", key, value.definition);
997
1036
  await host.collection.updateSearchIndex(key, value.definition);
@@ -1004,7 +1043,6 @@ else toUpdate.add(remote.name);
1004
1043
  });
1005
1044
  }
1006
1045
  break;
1007
- }
1008
1046
  default:
1009
1047
  }
1010
1048
  } catch {}
@@ -1029,7 +1067,7 @@ function fieldsMatch(left, right) {
1029
1067
  }
1030
1068
  function vectorFieldsMatch(left, right) {
1031
1069
  if (left.length !== (right || []).length) return false;
1032
- const rightMap = new Map();
1070
+ const rightMap = /* @__PURE__ */ new Map();
1033
1071
  for (const f of right || []) rightMap.set(f.path, f);
1034
1072
  for (const l of left) {
1035
1073
  const r = rightMap.get(l.path);
@@ -1038,35 +1076,52 @@ function vectorFieldsMatch(left, right) {
1038
1076
  }
1039
1077
  return true;
1040
1078
  }
1041
-
1042
1079
  //#endregion
1043
- //#region packages/db-mongo/src/lib/validate-plugins.ts
1080
+ //#region src/lib/validate-plugins.ts
1044
1081
  const validateMongoIdPlugin = (ctx, def, value) => {
1045
1082
  if (def.type.tags?.has("objectId")) {
1046
- if (ctx.path === "_id" && (value === undefined || value === null)) {
1083
+ if (ctx.path === "_id" && (value === void 0 || value === null)) {
1047
1084
  const dbCtx = ctx.context;
1048
1085
  if (dbCtx && (dbCtx.mode === "insert" || dbCtx.mode === "replace")) return true;
1049
1086
  }
1050
1087
  return ctx.validateAnnotatedType(def, value instanceof mongodb.ObjectId ? value.toString() : value);
1051
1088
  }
1052
1089
  };
1053
-
1054
1090
  //#endregion
1055
- //#region packages/db-mongo/src/lib/mongo-adapter.ts
1056
- function _define_property(obj, key, value) {
1057
- if (key in obj) Object.defineProperty(obj, key, {
1058
- value,
1059
- enumerable: true,
1060
- configurable: true,
1061
- writable: true
1062
- });
1063
- else obj[key] = value;
1064
- return obj;
1065
- }
1066
- var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1091
+ //#region src/lib/mongo-adapter.ts
1092
+ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
1093
+ _collection;
1094
+ /** MongoDB-specific indexes (search, vector) — separate from table.indexes. */
1095
+ _mongoIndexes = /* @__PURE__ */ new Map();
1096
+ /** Vector search filter associations built during flattening. */
1097
+ _vectorFilters = /* @__PURE__ */ new Map();
1098
+ /** Default similarity thresholds per vector index (from @db.search.vector.threshold). */
1099
+ _vectorThresholds = /* @__PURE__ */ new Map();
1100
+ /** Cached search index lookup. */
1101
+ _searchIndexesMap;
1102
+ /** Physical field names with @db.default.increment → optional start value. */
1103
+ _incrementFields = /* @__PURE__ */ new Map();
1104
+ /** Physical field names that have a non-binary collation (nocase/unicode). */
1105
+ _collateFields;
1106
+ /** Capped collection options from @db.mongo.capped. */
1107
+ _cappedOptions;
1108
+ /** Whether the schema explicitly defines _id (via @db.mongo.collection or manual _id field). */
1109
+ _hasExplicitId = false;
1110
+ /** Unique fields accumulated during onFieldScanned, returned via getMetadataOverrides. */
1111
+ _pendingUniqueFields = [];
1112
+ constructor(db, client) {
1113
+ super();
1114
+ this.db = db;
1115
+ this.client = client;
1116
+ }
1067
1117
  get _client() {
1068
1118
  return this.client;
1069
1119
  }
1120
+ /**
1121
+ * Per-client cache: whether transactions are unavailable (standalone MongoDB).
1122
+ * Shared across all adapter instances for the same client so topology is probed once.
1123
+ */
1124
+ static _txDisabledClients = /* @__PURE__ */ new WeakSet();
1070
1125
  get _txDisabled() {
1071
1126
  return this.client ? MongoAdapter._txDisabledClients.has(this.client) : true;
1072
1127
  }
@@ -1077,14 +1132,14 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1077
1132
  * Uses MongoDB's Convenient Transaction API (`session.withTransaction()`).
1078
1133
  * This handles txnNumber management and automatic retry for
1079
1134
  * `TransientTransactionError` / `UnknownTransactionCommitResult`.
1080
- */ async withTransaction(fn) {
1135
+ */
1136
+ async withTransaction(fn) {
1081
1137
  if (this._getTransactionState()) return fn();
1082
1138
  if (this._txDisabled || !this._client) return fn();
1083
1139
  try {
1084
1140
  const topology = this._client.topology;
1085
1141
  if (topology) {
1086
- const desc = topology.description ?? topology.s?.description;
1087
- const type = desc?.type;
1142
+ const type = (topology.description ?? topology.s?.description)?.type;
1088
1143
  if (type === "Single" || type === "Unknown") {
1089
1144
  this._txDisabled = true;
1090
1145
  return fn();
@@ -1107,7 +1162,9 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1107
1162
  } catch {}
1108
1163
  }
1109
1164
  }
1110
- /** Returns `{ session }` opts if inside a transaction, empty object otherwise. */ _getSessionOpts() {
1165
+ static _noSession = Object.freeze({});
1166
+ /** Returns `{ session }` opts if inside a transaction, empty object otherwise. */
1167
+ _getSessionOpts() {
1111
1168
  const session = this._getTransactionState();
1112
1169
  return session ? { session } : MongoAdapter._noSession;
1113
1170
  }
@@ -1119,13 +1176,11 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1119
1176
  return this.collection.aggregate(pipeline, this._getSessionOpts());
1120
1177
  }
1121
1178
  async aggregate(query) {
1122
- const { buildAggregatePipeline, buildCountPipeline } = await Promise.resolve().then(function() {
1123
- return require("./agg-FBVtOv9k.cjs");
1124
- });
1179
+ const { buildAggregatePipeline, buildCountPipeline } = await Promise.resolve().then(() => require("./agg.cjs"));
1125
1180
  if (query.controls?.$count) {
1126
- const pipeline$1 = buildCountPipeline(query);
1127
- this._log("aggregate (count)", pipeline$1);
1128
- const result = await this.aggregatePipeline(pipeline$1).toArray();
1181
+ const pipeline = buildCountPipeline(query);
1182
+ this._log("aggregate (count)", pipeline);
1183
+ const result = await this.aggregatePipeline(pipeline).toArray();
1129
1184
  return result.length > 0 ? result : [{ count: 0 }];
1130
1185
  }
1131
1186
  const pipeline = buildAggregatePipeline(query);
@@ -1139,19 +1194,20 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1139
1194
  if (idProp?.type.kind === "") return idProp.type.designType;
1140
1195
  return "objectId";
1141
1196
  }
1142
- prepareId(id, fieldType) {
1197
+ prepareId(id, _fieldType) {
1198
+ const fieldType = _fieldType;
1143
1199
  const tags = fieldType.type.tags;
1144
1200
  if (tags?.has("objectId") && tags?.has("mongo")) return id instanceof mongodb.ObjectId ? id : new mongodb.ObjectId(id);
1145
1201
  if (fieldType.type.kind === "") {
1146
- const dt = fieldType.type.designType;
1147
- if (dt === "number") return Number(id);
1202
+ if (fieldType.type.designType === "number") return Number(id);
1148
1203
  }
1149
1204
  return String(id);
1150
1205
  }
1151
1206
  /**
1152
1207
  * Convenience method that uses `idType` to transform an ID value.
1153
1208
  * For use in controllers that don't have access to the field type.
1154
- */ prepareIdFromIdType(id) {
1209
+ */
1210
+ prepareIdFromIdType(id) {
1155
1211
  switch (this.idType) {
1156
1212
  case "objectId": return id instanceof mongodb.ObjectId ? id : new mongodb.ObjectId(id);
1157
1213
  case "number": return Number(id);
@@ -1168,26 +1224,24 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1168
1224
  getValidatorPlugins() {
1169
1225
  return [validateMongoIdPlugin];
1170
1226
  }
1171
- getAdapterTableName(type) {
1172
- return undefined;
1173
- }
1227
+ getAdapterTableName(_type) {}
1174
1228
  supportsNativeRelations() {
1175
1229
  return true;
1176
1230
  }
1177
1231
  async loadRelations(rows, withRelations, relations, foreignKeys, tableResolver) {
1178
1232
  return loadRelationsImpl(this, rows, withRelations, relations, foreignKeys, tableResolver);
1179
1233
  }
1180
- /** Returns the context object used by CollectionPatcher. */ getPatcherContext() {
1234
+ /** Returns the context object used by CollectionPatcher. */
1235
+ getPatcherContext() {
1181
1236
  return {
1182
1237
  flatMap: this._table.flatMap,
1183
1238
  prepareId: (id) => this.prepareIdFromIdType(id),
1184
1239
  createValidator: (opts) => this._table.createValidator(opts)
1185
1240
  };
1186
1241
  }
1187
- async nativePatch(filter, patch) {
1242
+ async nativePatch(filter, patch, ops) {
1188
1243
  const mongoFilter = require_mongo_filter.buildMongoFilter(filter);
1189
- const patcher = new CollectionPatcher(this.getPatcherContext(), patch);
1190
- const { updateFilter, updateOptions } = patcher.preparePatch();
1244
+ const { updateFilter, updateOptions } = new CollectionPatcher(this.getPatcherContext(), patch, ops).preparePatch();
1191
1245
  this._log("updateOne (patch)", mongoFilter, updateFilter);
1192
1246
  const result = await this.collection.updateOne(mongoFilter, updateFilter, {
1193
1247
  ...updateOptions,
@@ -1198,8 +1252,8 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1198
1252
  modifiedCount: result.modifiedCount
1199
1253
  };
1200
1254
  }
1201
- onBeforeFlatten(type) {
1202
- const typeMeta = type.metadata;
1255
+ onBeforeFlatten(_type) {
1256
+ const typeMeta = _type.metadata;
1203
1257
  const capped = typeMeta.get("db.mongo.capped");
1204
1258
  if (capped) this._cappedOptions = {
1205
1259
  size: capped.size,
@@ -1217,7 +1271,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1217
1271
  text: { fuzzy: { maxEdits: textSearch.fuzzy || 0 } }
1218
1272
  });
1219
1273
  }
1220
- onFieldScanned(field, type, metadata) {
1274
+ onFieldScanned(field, _type, metadata) {
1221
1275
  if (field === "_id") this._hasExplicitId = true;
1222
1276
  if (field !== "_id" && metadata.has("meta.id")) {
1223
1277
  this._addMongoIndexField("unique", "__pk", field);
@@ -1226,7 +1280,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1226
1280
  if (metadata.has("db.default.increment")) {
1227
1281
  const physicalName = metadata.get("db.column") ?? field;
1228
1282
  const startValue = metadata.get("db.default.increment");
1229
- this._incrementFields.set(physicalName, typeof startValue === "number" ? startValue : undefined);
1283
+ this._incrementFields.set(physicalName, typeof startValue === "number" ? startValue : void 0);
1230
1284
  }
1231
1285
  for (const index of metadata.get("db.index.fulltext") || []) {
1232
1286
  const name = typeof index === "object" ? index.name || "" : "";
@@ -1244,7 +1298,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1244
1298
  numDimensions: vectorIndex.dimensions
1245
1299
  }] });
1246
1300
  const threshold = metadata.get("db.search.vector.threshold");
1247
- if (threshold !== undefined) this._vectorThresholds.set(mongoIndexKey("vector", indexName), threshold);
1301
+ if (threshold !== void 0) this._vectorThresholds.set(mongoIndexKey("vector", indexName), threshold);
1248
1302
  }
1249
1303
  for (const indexName of metadata.get("db.search.filter") || []) this._vectorFilters.set(mongoIndexKey("vector", indexName), field);
1250
1304
  }
@@ -1253,7 +1307,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1253
1307
  if (this._hasExplicitId) return {
1254
1308
  addPrimaryKeys: ["_id"],
1255
1309
  removePrimaryKeys: meta.originalMetaIdFields.filter((f) => f !== "_id"),
1256
- addUniqueFields: uniqueFields.length > 0 ? uniqueFields : undefined
1310
+ addUniqueFields: uniqueFields.length > 0 ? uniqueFields : void 0
1257
1311
  };
1258
1312
  uniqueFields.push("_id");
1259
1313
  return {
@@ -1266,7 +1320,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1266
1320
  designType: "string",
1267
1321
  tags: new Set(["objectId", "mongo"])
1268
1322
  },
1269
- metadata: new Map()
1323
+ metadata: /* @__PURE__ */ new Map()
1270
1324
  }
1271
1325
  }],
1272
1326
  addUniqueFields: uniqueFields
@@ -1281,14 +1335,15 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1281
1335
  });
1282
1336
  }
1283
1337
  for (const fd of this._table.fieldDescriptors) if (fd.collate && fd.collate !== "binary") {
1284
- if (!this._collateFields) this._collateFields = new Map();
1338
+ if (!this._collateFields) this._collateFields = /* @__PURE__ */ new Map();
1285
1339
  this._collateFields.set(fd.physicalName, fd.collate);
1286
1340
  }
1287
1341
  }
1288
- /** Returns MongoDB-specific search index map (internal). */ getMongoSearchIndexes() {
1342
+ /** Returns MongoDB-specific search index map (internal). */
1343
+ getMongoSearchIndexes() {
1289
1344
  if (!this._searchIndexesMap) {
1290
1345
  this._table.flatMap;
1291
- this._searchIndexesMap = new Map();
1346
+ this._searchIndexesMap = /* @__PURE__ */ new Map();
1292
1347
  let defaultIndex;
1293
1348
  for (const index of this._table.indexes.values()) if (index.type === "fulltext" && !defaultIndex) defaultIndex = {
1294
1349
  key: index.key,
@@ -1298,34 +1353,32 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1298
1353
  weights: Object.fromEntries(index.fields.filter((f) => f.weight).map((f) => [f.name, f.weight]))
1299
1354
  };
1300
1355
  for (const index of this._mongoIndexes.values()) switch (index.type) {
1301
- case "text": {
1356
+ case "text":
1302
1357
  if (!defaultIndex) defaultIndex = index;
1303
1358
  break;
1304
- }
1305
- case "dynamic_text": {
1359
+ case "dynamic_text":
1306
1360
  defaultIndex = index;
1307
1361
  break;
1308
- }
1309
- case "search_text": {
1362
+ case "search_text":
1310
1363
  if (!defaultIndex || defaultIndex.type === "text") defaultIndex = index;
1311
1364
  this._searchIndexesMap.set(index.name, index);
1312
1365
  break;
1313
- }
1314
- case "vector": {
1366
+ case "vector":
1315
1367
  this._searchIndexesMap.set(index.name, index);
1316
1368
  break;
1317
- }
1318
1369
  default:
1319
1370
  }
1320
- if (defaultIndex && !this._searchIndexesMap.has(DEFAULT_INDEX_NAME)) this._searchIndexesMap.set(DEFAULT_INDEX_NAME, defaultIndex);
1371
+ if (defaultIndex && !this._searchIndexesMap.has("DEFAULT")) this._searchIndexesMap.set(DEFAULT_INDEX_NAME, defaultIndex);
1321
1372
  }
1322
1373
  return this._searchIndexesMap;
1323
1374
  }
1324
- /** Returns a specific MongoDB search index by name. */ getMongoSearchIndex(name = DEFAULT_INDEX_NAME) {
1375
+ /** Returns a specific MongoDB search index by name. */
1376
+ getMongoSearchIndex(name = DEFAULT_INDEX_NAME) {
1325
1377
  return this.getMongoSearchIndexes().get(name);
1326
1378
  }
1327
- /** Returns the default similarity threshold for a vector index (from @db.search.vector.threshold). */ getVectorThreshold(indexName) {
1328
- const key = mongoIndexKey("vector", indexName || DEFAULT_INDEX_NAME);
1379
+ /** Returns the default similarity threshold for a vector index (from @db.search.vector.threshold). */
1380
+ getVectorThreshold(indexName) {
1381
+ const key = mongoIndexKey("vector", indexName || "DEFAULT");
1329
1382
  return this._vectorThresholds.get(key);
1330
1383
  }
1331
1384
  getSearchIndexes() {
@@ -1369,18 +1422,16 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1369
1422
  };
1370
1423
  }
1371
1424
  async collectionExists() {
1372
- const cols = await this.db.listCollections({ name: this._table.tableName }).toArray();
1373
- return cols.length > 0;
1425
+ return (await this.db.listCollections({ name: this._table.tableName }).toArray()).length > 0;
1374
1426
  }
1375
1427
  async ensureCollectionExists() {
1376
- const exists = await this.collectionExists();
1377
- if (!exists) {
1428
+ if (!await this.collectionExists()) {
1378
1429
  this._log("createCollection", this._table.tableName);
1379
1430
  const opts = { comment: "Created by Atscript Mongo Adapter" };
1380
1431
  if (this._cappedOptions) {
1381
1432
  opts.capped = true;
1382
1433
  opts.size = this._cappedOptions.size;
1383
- if (this._cappedOptions.max !== null && this._cappedOptions.max !== undefined) opts.max = this._cappedOptions.max;
1434
+ if (this._cappedOptions.max !== null && this._cappedOptions.max !== void 0) opts.max = this._cappedOptions.max;
1384
1435
  }
1385
1436
  await this.db.createCollection(this._table.tableName, opts);
1386
1437
  }
@@ -1388,17 +1439,15 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1388
1439
  /**
1389
1440
  * Wraps an async operation to catch MongoDB duplicate key errors
1390
1441
  * (code 11000) and rethrow as structured `DbError`.
1391
- */ async _wrapDuplicateKeyError(fn) {
1442
+ */
1443
+ async _wrapDuplicateKeyError(fn) {
1392
1444
  try {
1393
1445
  return await fn();
1394
1446
  } catch (error) {
1395
- if (error instanceof mongodb.MongoServerError && error.code === 11e3) {
1396
- const field = error.keyPattern ? Object.keys(error.keyPattern)[0] ?? "" : "";
1397
- throw new __atscript_db.DbError("CONFLICT", [{
1398
- path: field,
1399
- message: error.message
1400
- }]);
1401
- }
1447
+ if (error instanceof mongodb.MongoServerError && error.code === 11e3) throw new _atscript_db.DbError("CONFLICT", [{
1448
+ path: error.keyPattern ? Object.keys(error.keyPattern)[0] ?? "" : "",
1449
+ message: error.message
1450
+ }]);
1402
1451
  throw error;
1403
1452
  }
1404
1453
  }
@@ -1416,7 +1465,7 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1416
1465
  }
1417
1466
  async insertMany(data) {
1418
1467
  if (this._incrementFields.size > 0) {
1419
- const allFields = new Set();
1468
+ const allFields = /* @__PURE__ */ new Set();
1420
1469
  for (const item of data) for (const f of this._fieldsNeedingIncrement(item)) allFields.add(f);
1421
1470
  if (allFields.size > 0) await this._assignBatchIncrements(data, allFields);
1422
1471
  }
@@ -1455,10 +1504,11 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1455
1504
  ...this._getSessionOpts()
1456
1505
  });
1457
1506
  }
1458
- async updateOne(filter, data) {
1507
+ async updateOne(filter, data, ops) {
1459
1508
  const mongoFilter = require_mongo_filter.buildMongoFilter(filter);
1460
- this._log("updateOne", mongoFilter, { $set: data });
1461
- const result = await this.collection.updateOne(mongoFilter, { $set: data }, this._getSessionOpts());
1509
+ const updateDoc = buildMongoUpdateDoc(data, ops);
1510
+ this._log("updateOne", mongoFilter, updateDoc);
1511
+ const result = await this.collection.updateOne(mongoFilter, updateDoc, this._getSessionOpts());
1462
1512
  return {
1463
1513
  matchedCount: result.matchedCount,
1464
1514
  modifiedCount: result.modifiedCount
@@ -1476,13 +1526,13 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1476
1526
  async deleteOne(filter) {
1477
1527
  const mongoFilter = require_mongo_filter.buildMongoFilter(filter);
1478
1528
  this._log("deleteOne", mongoFilter);
1479
- const result = await this.collection.deleteOne(mongoFilter, this._getSessionOpts());
1480
- return { deletedCount: result.deletedCount };
1529
+ return { deletedCount: (await this.collection.deleteOne(mongoFilter, this._getSessionOpts())).deletedCount };
1481
1530
  }
1482
- async updateMany(filter, data) {
1531
+ async updateMany(filter, data, ops) {
1483
1532
  const mongoFilter = require_mongo_filter.buildMongoFilter(filter);
1484
- this._log("updateMany", mongoFilter, { $set: data });
1485
- const result = await this.collection.updateMany(mongoFilter, { $set: data }, this._getSessionOpts());
1533
+ const updateDoc = buildMongoUpdateDoc(data, ops);
1534
+ this._log("updateMany", mongoFilter, updateDoc);
1535
+ const result = await this.collection.updateMany(mongoFilter, updateDoc, this._getSessionOpts());
1486
1536
  return {
1487
1537
  matchedCount: result.matchedCount,
1488
1538
  modifiedCount: result.modifiedCount
@@ -1500,11 +1550,10 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1500
1550
  async deleteMany(filter) {
1501
1551
  const mongoFilter = require_mongo_filter.buildMongoFilter(filter);
1502
1552
  this._log("deleteMany", mongoFilter);
1503
- const result = await this.collection.deleteMany(mongoFilter, this._getSessionOpts());
1504
- return { deletedCount: result.deletedCount };
1553
+ return { deletedCount: (await this.collection.deleteMany(mongoFilter, this._getSessionOpts())).deletedCount };
1505
1554
  }
1506
1555
  clearCollectionCache() {
1507
- this._collection = undefined;
1556
+ this._collection = void 0;
1508
1557
  }
1509
1558
  async tableExists() {
1510
1559
  return tableExistsImpl(this);
@@ -1545,29 +1594,31 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1545
1594
  destructiveOptionKeys() {
1546
1595
  return DESTRUCTIVE_OPTION_KEYS;
1547
1596
  }
1548
- /** Returns the counters collection used for atomic auto-increment. */ get _countersCollection() {
1597
+ /** Returns the counters collection used for atomic auto-increment. */
1598
+ get _countersCollection() {
1549
1599
  return this.db.collection("__atscript_counters");
1550
1600
  }
1551
- /** Returns physical field names of increment fields that are undefined in the data. */ _fieldsNeedingIncrement(data) {
1601
+ /** Returns physical field names of increment fields that are undefined in the data. */
1602
+ _fieldsNeedingIncrement(data) {
1552
1603
  const result = [];
1553
- for (const physical of this._incrementFields.keys()) if (data[physical] === undefined || data[physical] === null) result.push(physical);
1604
+ for (const physical of this._incrementFields.keys()) if (data[physical] === void 0 || data[physical] === null) result.push(physical);
1554
1605
  return result;
1555
1606
  }
1556
1607
  /**
1557
1608
  * Atomically allocates `count` sequential values for each increment field
1558
1609
  * using a counter collection. Returns a map of field → first allocated value.
1559
- */ async _allocateIncrementValues(physicalFields, count) {
1610
+ */
1611
+ async _allocateIncrementValues(physicalFields, count) {
1560
1612
  const counters = this._countersCollection;
1561
1613
  const collectionName = this._table.tableName;
1562
- const result = new Map();
1614
+ const result = /* @__PURE__ */ new Map();
1563
1615
  for (const field of physicalFields) {
1564
1616
  const counterId = `${collectionName}.${field}`;
1565
1617
  const startValue = this._incrementFields.get(field);
1566
- const doc = await counters.findOneAndUpdate({ _id: counterId }, { $inc: { seq: count } }, {
1618
+ const seq = (await counters.findOneAndUpdate({ _id: counterId }, { $inc: { seq: count } }, {
1567
1619
  upsert: true,
1568
1620
  returnDocument: "after"
1569
- });
1570
- const seq = doc?.seq ?? count;
1621
+ }))?.seq ?? count;
1571
1622
  if (seq === count) {
1572
1623
  const currentMax = await this._getCurrentFieldMax(field);
1573
1624
  const minStart = typeof startValue === "number" ? startValue : 1;
@@ -1583,7 +1634,8 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1583
1634
  }
1584
1635
  return result;
1585
1636
  }
1586
- /** Reads current max value for a single field via $group aggregation. */ async _getCurrentFieldMax(field) {
1637
+ /** Reads current max value for a single field via $group aggregation. */
1638
+ async _getCurrentFieldMax(field) {
1587
1639
  const alias = `max__${field.replace(/\./g, "__")}`;
1588
1640
  const agg = await this.collection.aggregate([{ $group: {
1589
1641
  _id: null,
@@ -1595,19 +1647,20 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1595
1647
  }
1596
1648
  return 0;
1597
1649
  }
1598
- /** Allocates increment values for a batch of items, assigning in order. */ async _assignBatchIncrements(data, allFields) {
1599
- const fieldCounts = new Map();
1650
+ /** Allocates increment values for a batch of items, assigning in order. */
1651
+ async _assignBatchIncrements(data, allFields) {
1652
+ const fieldCounts = /* @__PURE__ */ new Map();
1600
1653
  for (const physical of allFields) {
1601
1654
  let count = 0;
1602
- for (const item of data) if (item[physical] === undefined || item[physical] === null) count++;
1655
+ for (const item of data) if (item[physical] === void 0 || item[physical] === null) count++;
1603
1656
  if (count > 0) fieldCounts.set(physical, count);
1604
1657
  }
1605
- const fieldCounters = new Map();
1658
+ const fieldCounters = /* @__PURE__ */ new Map();
1606
1659
  for (const [physical, count] of fieldCounts) {
1607
1660
  const allocated = await this._allocateIncrementValues([physical], count);
1608
1661
  fieldCounters.set(physical, allocated.get(physical) ?? 1);
1609
1662
  }
1610
- for (const item of data) for (const physical of allFields) if (item[physical] === undefined || item[physical] === null) {
1663
+ for (const item of data) for (const physical of allFields) if (item[physical] === void 0 || item[physical] === null) {
1611
1664
  const next = fieldCounters.get(physical) ?? 1;
1612
1665
  item[physical] = next;
1613
1666
  fieldCounters.set(physical, next + 1);
@@ -1626,9 +1679,10 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1626
1679
  * Returns MongoDB collation options if any filter field has a non-binary collation.
1627
1680
  * Uses pre-computed insights when available, falls back to computing them on demand.
1628
1681
  * Maps: nocase → strength 2 (case-insensitive), unicode → strength 1 (case+accent-insensitive).
1629
- */ _getCollationOpts(query) {
1630
- if (!this._collateFields) return undefined;
1631
- const insights = query.insights ?? (0, __atscript_db.computeInsights)(query.filter);
1682
+ */
1683
+ _getCollationOpts(query) {
1684
+ if (!this._collateFields) return;
1685
+ const insights = query.insights ?? (0, _atscript_db.computeInsights)(query.filter);
1632
1686
  let strength;
1633
1687
  for (const field of insights.keys()) {
1634
1688
  const collation = this._collateFields.get(field);
@@ -1641,14 +1695,14 @@ var MongoAdapter = class MongoAdapter extends __atscript_db.BaseDbAdapter {
1641
1695
  return strength ? { collation: {
1642
1696
  locale: "en",
1643
1697
  strength
1644
- } } : undefined;
1698
+ } } : void 0;
1645
1699
  }
1646
1700
  _addMongoIndexField(type, name, field, weight) {
1647
1701
  const key = mongoIndexKey(type, name);
1648
1702
  let index = this._mongoIndexes.get(key);
1649
1703
  const value = type === "text" ? "text" : 1;
1650
1704
  if (index) index.fields[field] = value;
1651
- else {
1705
+ else {
1652
1706
  index = {
1653
1707
  key,
1654
1708
  name,
@@ -1661,16 +1715,16 @@ else {
1661
1715
  if (weight) index.weights[field] = weight;
1662
1716
  }
1663
1717
  _setSearchIndex(type, name, definition) {
1664
- const key = mongoIndexKey(type, name || DEFAULT_INDEX_NAME);
1718
+ const key = mongoIndexKey(type, name || "DEFAULT");
1665
1719
  this._mongoIndexes.set(key, {
1666
1720
  key,
1667
- name: name || DEFAULT_INDEX_NAME,
1721
+ name: name || "DEFAULT",
1668
1722
  type,
1669
1723
  definition
1670
1724
  });
1671
1725
  }
1672
1726
  _addFieldToSearchIndex(type, _name, fieldName, analyzer) {
1673
- const name = _name || DEFAULT_INDEX_NAME;
1727
+ const name = _name || "DEFAULT";
1674
1728
  let index = this._mongoIndexes.get(mongoIndexKey(type, name));
1675
1729
  if (!index && type === "search_text") {
1676
1730
  this._setSearchIndex(type, name, {
@@ -1684,27 +1738,34 @@ else {
1684
1738
  if (analyzer) index.definition.mappings.fields[fieldName].analyzer = analyzer;
1685
1739
  }
1686
1740
  }
1687
- constructor(db, client) {
1688
- super(), _define_property(this, "db", void 0), _define_property(this, "client", void 0), _define_property(this, "_collection", void 0), _define_property(this, "_mongoIndexes", void 0), _define_property(this, "_vectorFilters", void 0), _define_property(this, "_vectorThresholds", void 0), _define_property(this, "_searchIndexesMap", void 0), _define_property(this, "_incrementFields", void 0), _define_property(this, "_collateFields", void 0), _define_property(this, "_cappedOptions", void 0), _define_property(this, "_hasExplicitId", void 0), _define_property(this, "_pendingUniqueFields", void 0), this.db = db, this.client = client, this._mongoIndexes = new Map(), this._vectorFilters = new Map(), this._vectorThresholds = new Map(), this._incrementFields = new Map(), this._hasExplicitId = false, this._pendingUniqueFields = [];
1689
- }
1690
1741
  };
1691
1742
  /**
1692
- * Per-client cache: whether transactions are unavailable (standalone MongoDB).
1693
- * Shared across all adapter instances for the same client so topology is probed once.
1694
- */ _define_property(MongoAdapter, "_txDisabledClients", new WeakSet());
1695
- _define_property(MongoAdapter, "_noSession", Object.freeze({}));
1696
-
1743
+ * Builds a MongoDB update document from a data object that may contain
1744
+ * field ops (`{ $inc: N }`, `{ $dec: N }`, `{ $mul: N }`).
1745
+ * Regular fields go into `$set`, ops go into `$inc` / `$mul`.
1746
+ */
1747
+ function buildMongoUpdateDoc(data, ops) {
1748
+ const updateDoc = {};
1749
+ let hasData = false;
1750
+ for (const _ in data) {
1751
+ hasData = true;
1752
+ break;
1753
+ }
1754
+ if (hasData) updateDoc.$set = data;
1755
+ if (ops?.inc) updateDoc.$inc = ops.inc;
1756
+ if (ops?.mul) updateDoc.$mul = ops.mul;
1757
+ return updateDoc;
1758
+ }
1697
1759
  //#endregion
1698
- //#region packages/db-mongo/src/lib/index.ts
1760
+ //#region src/lib/index.ts
1699
1761
  function createAdapter(connection, _options) {
1700
1762
  const client = new mongodb.MongoClient(connection);
1701
1763
  const db = client.db();
1702
- return new __atscript_db.DbSpace(() => new MongoAdapter(db, client));
1764
+ return new _atscript_db.DbSpace(() => new MongoAdapter(db, client));
1703
1765
  }
1704
-
1705
1766
  //#endregion
1706
- exports.CollectionPatcher = CollectionPatcher
1707
- exports.MongoAdapter = MongoAdapter
1708
- exports.buildMongoFilter = require_mongo_filter.buildMongoFilter
1709
- exports.createAdapter = createAdapter
1710
- exports.validateMongoIdPlugin = validateMongoIdPlugin
1767
+ exports.CollectionPatcher = CollectionPatcher;
1768
+ exports.MongoAdapter = MongoAdapter;
1769
+ exports.buildMongoFilter = require_mongo_filter.buildMongoFilter;
1770
+ exports.createAdapter = createAdapter;
1771
+ exports.validateMongoIdPlugin = validateMongoIdPlugin;