@atscript/db 0.1.38 → 0.1.40

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.
Files changed (64) hide show
  1. package/README.md +42 -303
  2. package/dist/agg.cjs +8 -3
  3. package/dist/agg.d.cts +7 -0
  4. package/dist/agg.d.mts +7 -0
  5. package/dist/agg.mjs +7 -3
  6. package/dist/control-DRgryKeg.cjs +14 -0
  7. package/dist/{control_as-bjmwe24C.mjs → control-IANbnfjG.mjs} +6 -18
  8. package/dist/db-readable-BQQzfguJ.d.cts +1249 -0
  9. package/dist/db-readable-Bbr4CjMb.d.mts +1249 -0
  10. package/dist/db-space-BUrQ5BFm.d.mts +309 -0
  11. package/dist/db-space-Vxpcnyt5.d.cts +309 -0
  12. package/dist/db-validator-plugin-07kDiis2.d.cts +22 -0
  13. package/dist/db-validator-plugin-CiqsHTI_.d.mts +22 -0
  14. package/dist/db-view-BntnAmXO.cjs +3071 -0
  15. package/dist/db-view-ZsoN91-q.mjs +2970 -0
  16. package/dist/index.cjs +95 -2801
  17. package/dist/index.d.cts +137 -0
  18. package/dist/index.d.mts +137 -0
  19. package/dist/index.mjs +55 -2761
  20. package/dist/{nested-writer-BkqL7cp3.cjs → nested-writer-BDXsDMPP.cjs} +196 -150
  21. package/dist/{nested-writer-NEN51mnR.mjs → nested-writer-Dmm1gbZV.mjs} +118 -70
  22. package/dist/ops-BdRAFLKY.d.mts +67 -0
  23. package/dist/ops-DXJ4Zw0P.d.cts +67 -0
  24. package/dist/ops.cjs +123 -0
  25. package/dist/ops.d.cts +2 -0
  26. package/dist/ops.d.mts +2 -0
  27. package/dist/ops.mjs +112 -0
  28. package/dist/plugin.cjs +90 -109
  29. package/dist/plugin.d.cts +6 -0
  30. package/dist/plugin.d.mts +6 -0
  31. package/dist/plugin.mjs +29 -49
  32. package/dist/rel.cjs +20 -20
  33. package/dist/rel.d.cts +119 -0
  34. package/dist/rel.d.mts +119 -0
  35. package/dist/rel.mjs +4 -5
  36. package/dist/{relation-helpers-guFL_oRf.cjs → relation-helpers-BYvsE1tR.cjs} +26 -22
  37. package/dist/{relation-helpers-DyBIlQnB.mjs → relation-helpers-CLasawQq.mjs} +11 -6
  38. package/dist/{relation-loader-Dv7qXYq7.mjs → relation-loader-BEOTXNcq.mjs} +63 -43
  39. package/dist/{relation-loader-CpnDRf9k.cjs → relation-loader-CRC5LcqM.cjs} +74 -49
  40. package/dist/shared.cjs +13 -13
  41. package/dist/{shared.d.ts → shared.d.cts} +14 -13
  42. package/dist/shared.d.mts +71 -0
  43. package/dist/shared.mjs +2 -3
  44. package/dist/sync.cjs +300 -252
  45. package/dist/sync.d.cts +369 -0
  46. package/dist/sync.d.mts +369 -0
  47. package/dist/sync.mjs +284 -233
  48. package/dist/{validation-utils-DEoCMmEb.cjs → validation-utils-DVJDijnB.cjs} +141 -109
  49. package/dist/{validation-utils-DhR_mtKa.mjs → validation-utils-DhjIjP1-.mjs} +71 -37
  50. package/package.json +31 -30
  51. package/LICENSE +0 -21
  52. package/dist/agg-BJFJ3dFQ.mjs +0 -8
  53. package/dist/agg-DnUWAOK8.cjs +0 -14
  54. package/dist/agg.d.ts +0 -13
  55. package/dist/chunk-CrpGerW8.cjs +0 -31
  56. package/dist/control_as-BFPERAF_.cjs +0 -28
  57. package/dist/index.d.ts +0 -1706
  58. package/dist/logger-B7oxCfLQ.mjs +0 -12
  59. package/dist/logger-Dt2v_-wb.cjs +0 -18
  60. package/dist/plugin.d.ts +0 -5
  61. package/dist/rel.d.ts +0 -1305
  62. package/dist/relation-loader-D4mTw6yH.cjs +0 -4
  63. package/dist/relation-loader-Ggy1ujwR.mjs +0 -4
  64. package/dist/sync.d.ts +0 -1878
@@ -1,32 +1,31 @@
1
- import { resolveRelationTargetTable } from "./relation-helpers-DyBIlQnB.mjs";
1
+ import { r as resolveRelationTargetTable } from "./relation-helpers-CLasawQq.mjs";
2
2
  import { ValidatorError } from "@atscript/typescript/utils";
3
-
4
- //#region packages/db/src/db-error.ts
5
- function _define_property(obj, key, value) {
6
- if (key in obj) Object.defineProperty(obj, key, {
7
- value,
8
- enumerable: true,
9
- configurable: true,
10
- writable: true
11
- });
12
- else obj[key] = value;
13
- return obj;
14
- }
3
+ //#region src/db-error.ts
15
4
  var DbError = class extends Error {
5
+ name = "DbError";
16
6
  constructor(code, errors, message) {
17
- super(message ?? errors[0]?.message ?? "Database error"), _define_property(this, "code", void 0), _define_property(this, "errors", void 0), _define_property(this, "name", void 0), this.code = code, this.errors = errors, this.name = "DbError";
18
- this.stack = undefined;
7
+ super(message ?? errors[0]?.message ?? "Database error");
8
+ this.code = code;
9
+ this.errors = errors;
10
+ this.stack = void 0;
19
11
  }
20
12
  };
21
-
22
13
  //#endregion
23
- //#region packages/db/src/table/error-utils.ts
14
+ //#region src/table/error-utils.ts
15
+ /**
16
+ * Prefixes error paths with a nav field context.
17
+ * Ensures errors from child table operations (e.g., FK violations on a comment)
18
+ * get paths like `comments[0].authorId` instead of just `authorId`.
19
+ */
24
20
  function prefixErrorPaths(errors, prefix) {
25
21
  return errors.map((err) => ({
26
22
  ...err,
27
23
  path: err.path ? `${prefix}.${err.path}` : prefix
28
24
  }));
29
25
  }
26
+ /**
27
+ * Wraps an async nested operation and prefixes error paths with the nav field context.
28
+ */
30
29
  async function wrapNestedError(navField, fn) {
31
30
  try {
32
31
  return await fn();
@@ -36,6 +35,11 @@ async function wrapNestedError(navField, fn) {
36
35
  throw error;
37
36
  }
38
37
  }
38
+ /**
39
+ * Catches `DbError('FK_VIOLATION')` with empty paths (from adapters that
40
+ * enforce FKs natively but can't report which field failed) and enriches
41
+ * the error with all FK field names from table metadata.
42
+ */
39
43
  async function enrichFkViolation(meta, fn) {
40
44
  try {
41
45
  return await fn();
@@ -52,6 +56,10 @@ async function enrichFkViolation(meta, fn) {
52
56
  throw error;
53
57
  }
54
58
  }
59
+ /**
60
+ * Wraps a delete operation: catches native `FK_VIOLATION` errors (e.g. SQLite
61
+ * RESTRICT) and re-throws as `CONFLICT` (409) with a descriptive message.
62
+ */
55
63
  async function remapDeleteFkViolation(tableName, fn) {
56
64
  try {
57
65
  return await fn();
@@ -63,13 +71,20 @@ async function remapDeleteFkViolation(tableName, fn) {
63
71
  throw error;
64
72
  }
65
73
  }
66
-
67
74
  //#endregion
68
- //#region packages/db/src/rel/nested-writer.ts
75
+ //#region src/rel/nested-writer.ts
76
+ /**
77
+ * Checks if any payload contains navigational data that would be silently
78
+ * dropped because maxDepth is 0.
79
+ */
69
80
  function checkDepthOverflow(payloads, maxDepth, meta) {
70
81
  if (meta.navFields.size === 0) return;
71
- for (const payload of payloads) for (const navField of meta.navFields) if (payload[navField] !== undefined) throw new Error(`Nested data in '${navField}' exceeds maxDepth (${maxDepth}). ` + `Increase maxDepth or strip nested data before writing.`);
82
+ for (const payload of payloads) for (const navField of meta.navFields) if (payload[navField] !== void 0) throw new Error(`Nested data in '${navField}' exceeds maxDepth (${maxDepth}). Increase maxDepth or strip nested data before writing.`);
72
83
  }
84
+ /**
85
+ * Validates a batch of items using the given validator and context.
86
+ * Wraps per-item validation errors with array index paths for batch operations.
87
+ */
73
88
  function validateBatch(validator, items, ctx) {
74
89
  for (let i = 0; i < items.length; i++) try {
75
90
  validator.validate(items[i], false, ctx);
@@ -81,6 +96,10 @@ function validateBatch(validator, items, ctx) {
81
96
  throw error;
82
97
  }
83
98
  }
99
+ /**
100
+ * Pre-validates FROM children (type + FK constraints) before the main insert.
101
+ * Catches errors early before the parent record is committed.
102
+ */
84
103
  async function preValidateNestedFrom(host, originals) {
85
104
  for (const [navField, relation] of host._meta.relations) {
86
105
  if (relation.direction !== "from") continue;
@@ -104,6 +123,9 @@ async function preValidateNestedFrom(host, originals) {
104
123
  await wrapNestedError(navField, () => targetTable.preValidateItems(allChildren, { excludeFkTargetTable: host.tableName }));
105
124
  }
106
125
  }
126
+ /**
127
+ * Batch-creates TO dependencies before the main insert.
128
+ */
107
129
  async function batchInsertNestedTo(host, items, maxDepth, depth) {
108
130
  for (const [navField, relation] of host._meta.relations) {
109
131
  if (relation.direction !== "to") continue;
@@ -128,6 +150,9 @@ async function batchInsertNestedTo(host, items, maxDepth, depth) {
128
150
  for (let j = 0; j < sourceIndices.length; j++) if (fk.localFields.length === 1) items[sourceIndices[j]][fk.localFields[0]] = result.insertedIds[j];
129
151
  }
130
152
  }
153
+ /**
154
+ * Batch-creates FROM dependents after the main insert.
155
+ */
131
156
  async function batchInsertNestedFrom(host, originals, parentIds, maxDepth, depth) {
132
157
  for (const [navField, relation] of host._meta.relations) {
133
158
  if (relation.direction !== "from") continue;
@@ -152,6 +177,9 @@ async function batchInsertNestedFrom(host, originals, parentIds, maxDepth, depth
152
177
  }));
153
178
  }
154
179
  }
180
+ /**
181
+ * Batch-creates VIA (M:N) targets and junction entries after the main insert.
182
+ */
155
183
  async function batchInsertNestedVia(host, originals, parentIds, maxDepth, depth) {
156
184
  for (const [navField, relation] of host._meta.relations) {
157
185
  if (relation.direction !== "via" || !relation.viaType) continue;
@@ -170,14 +198,14 @@ async function batchInsertNestedVia(host, originals, parentIds, maxDepth, depth)
170
198
  const targets = originals[i][navField];
171
199
  if (!Array.isArray(targets) || targets.length === 0) continue;
172
200
  const parentPK = parentIds[i];
173
- if (parentPK === undefined) continue;
201
+ if (parentPK === void 0) continue;
174
202
  const newTargets = [];
175
203
  const existingIds = [];
176
204
  for (const t of targets) {
177
205
  const rec = t;
178
206
  const pk = rec[targetPKField];
179
- if (pk !== undefined && pk !== null) existingIds.push(pk);
180
- else newTargets.push({ ...rec });
207
+ if (pk !== void 0 && pk !== null) existingIds.push(pk);
208
+ else newTargets.push({ ...rec });
181
209
  }
182
210
  const allTargetIds = [...existingIds];
183
211
  if (newTargets.length > 0) {
@@ -197,6 +225,9 @@ else newTargets.push({ ...rec });
197
225
  }
198
226
  }
199
227
  }
228
+ /**
229
+ * Batch-replaces TO dependencies before the main replace.
230
+ */
200
231
  async function batchReplaceNestedTo(host, items, maxDepth, depth) {
201
232
  for (const [navField, relation] of host._meta.relations) {
202
233
  if (relation.direction !== "to") continue;
@@ -221,6 +252,9 @@ async function batchReplaceNestedTo(host, items, maxDepth, depth) {
221
252
  for (let j = 0; j < sourceIndices.length; j++) if (fk.localFields.length === 1 && fk.targetFields.length === 1) items[sourceIndices[j]][fk.localFields[0]] = parents[j][fk.targetFields[0]];
222
253
  }
223
254
  }
255
+ /**
256
+ * Batch-replaces FROM dependents after the main replace.
257
+ */
224
258
  async function batchReplaceNestedFrom(host, originals, maxDepth, depth) {
225
259
  for (const [navField, relation] of host._meta.relations) {
226
260
  if (relation.direction !== "from") continue;
@@ -232,12 +266,15 @@ async function batchReplaceNestedFrom(host, originals, maxDepth, depth) {
232
266
  for (const original of originals) {
233
267
  const children = original[navField];
234
268
  if (!Array.isArray(children)) continue;
235
- const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : undefined;
236
- if (parentPK === undefined || remoteFK.fields.length !== 1) continue;
269
+ const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : void 0;
270
+ if (parentPK === void 0 || remoteFK.fields.length !== 1) continue;
237
271
  await fromReplace(targetTable, children, parentPK, remoteFK.fields[0], childPKs, navField, maxDepth, depth);
238
272
  }
239
273
  }
240
274
  }
275
+ /**
276
+ * Handles VIA (M:N) relations during replace.
277
+ */
241
278
  async function batchReplaceNestedVia(host, originals, maxDepth, depth) {
242
279
  for (const [navField, relation] of host._meta.relations) {
243
280
  if (relation.direction !== "via" || !relation.viaType) continue;
@@ -255,12 +292,16 @@ async function batchReplaceNestedVia(host, originals, maxDepth, depth) {
255
292
  for (const original of originals) {
256
293
  const targets = original[navField];
257
294
  if (!Array.isArray(targets)) continue;
258
- const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : undefined;
259
- if (parentPK === undefined) continue;
295
+ const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : void 0;
296
+ if (parentPK === void 0) continue;
260
297
  await viaReplace(targetTable, junctionTable, targets, parentPK, targetPKField, fkToThis.fields[0], fkToTarget.fields[0], maxDepth, depth);
261
298
  }
262
299
  }
263
300
  }
301
+ /**
302
+ * Batch-patches TO dependencies before the main patch.
303
+ * Reads FK values from DB if not present in the payload.
304
+ */
264
305
  async function batchPatchNestedTo(host, items, maxDepth, depth) {
265
306
  for (const [navField, relation] of host._meta.relations) {
266
307
  if (relation.direction !== "to") continue;
@@ -273,8 +314,8 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
273
314
  const nested = item[navField];
274
315
  if (!nested || typeof nested !== "object" || Array.isArray(nested)) continue;
275
316
  const patch = { ...nested };
276
- let fkValue = fk.localFields.length === 1 ? item[fk.localFields[0]] : undefined;
277
- if (fkValue === undefined) {
317
+ let fkValue = fk.localFields.length === 1 ? item[fk.localFields[0]] : void 0;
318
+ if (fkValue === void 0) {
278
319
  const pkFilter = host._extractPrimaryKeyFilter(item);
279
320
  const current = await host.findOne({
280
321
  filter: pkFilter,
@@ -284,9 +325,9 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
284
325
  path: navField,
285
326
  message: `Cannot patch relation '${navField}' — source record not found`
286
327
  }]);
287
- fkValue = fk.localFields.length === 1 ? current[fk.localFields[0]] : undefined;
328
+ fkValue = fk.localFields.length === 1 ? current[fk.localFields[0]] : void 0;
288
329
  }
289
- if (fkValue === null || fkValue === undefined) throw new DbError("FK_VIOLATION", [{
330
+ if (fkValue === null || fkValue === void 0) throw new DbError("FK_VIOLATION", [{
290
331
  path: fk.localFields[0],
291
332
  message: `Cannot patch relation '${navField}' — foreign key '${fk.localFields[0]}' is null`
292
333
  }]);
@@ -300,6 +341,10 @@ async function batchPatchNestedTo(host, items, maxDepth, depth) {
300
341
  });
301
342
  }
302
343
  }
344
+ /**
345
+ * Batch-patches FROM (1:N) dependencies after the main patch.
346
+ * Supports patch operators: $replace, $insert, $remove, $update, $upsert.
347
+ */
303
348
  async function batchPatchNestedFrom(host, originals, maxDepth, depth) {
304
349
  for (const [navField, relation] of host._meta.relations) {
305
350
  if (relation.direction !== "from") continue;
@@ -310,9 +355,9 @@ async function batchPatchNestedFrom(host, originals, maxDepth, depth) {
310
355
  const childPKs = [...targetTable.primaryKeys];
311
356
  for (const original of originals) {
312
357
  const navValue = original[navField];
313
- if (navValue === undefined || navValue === null) continue;
314
- const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : undefined;
315
- if (parentPK === undefined || remoteFK.fields.length !== 1) continue;
358
+ if (navValue === void 0 || navValue === null) continue;
359
+ const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : void 0;
360
+ if (parentPK === void 0 || remoteFK.fields.length !== 1) continue;
316
361
  const fkField = remoteFK.fields[0];
317
362
  const ops = extractNavPatchOps(navValue);
318
363
  if (ops.replace) await fromReplace(targetTable, ops.replace, parentPK, fkField, childPKs, navField, maxDepth, depth);
@@ -325,7 +370,7 @@ async function batchPatchNestedFrom(host, originals, maxDepth, depth) {
325
370
  return f;
326
371
  });
327
372
  if (removeFilters.length === 1) await targetTable.deleteMany(removeFilters[0]);
328
- else await targetTable.deleteMany({ $or: removeFilters });
373
+ else await targetTable.deleteMany({ $or: removeFilters });
329
374
  }
330
375
  if (ops.update && ops.update.length > 0) {
331
376
  const items = ops.update.map((child) => {
@@ -344,9 +389,8 @@ else await targetTable.deleteMany({ $or: removeFilters });
344
389
  for (const child of ops.upsert) {
345
390
  const rec = { ...child };
346
391
  rec[fkField] = parentPK;
347
- const hasPK = childPKs.length > 0 && childPKs.every((pk) => rec[pk] !== undefined);
348
- if (hasPK) toUpdate.push(rec);
349
- else toInsert.push(rec);
392
+ if (childPKs.length > 0 && childPKs.every((pk) => rec[pk] !== void 0)) toUpdate.push(rec);
393
+ else toInsert.push(rec);
350
394
  }
351
395
  if (toUpdate.length > 0) await wrapNestedError(navField, () => targetTable.bulkUpdate(toUpdate, {
352
396
  maxDepth,
@@ -358,11 +402,12 @@ else toInsert.push(rec);
358
402
  }));
359
403
  }
360
404
  if (ops.insert && ops.insert.length > 0) {
361
- const items = ops.insert.map((child) => {
362
- const rec = { ...child };
405
+ const items = [];
406
+ for (let i = 0; i < ops.insert.length; i++) {
407
+ const rec = { ...ops.insert[i] };
363
408
  rec[fkField] = parentPK;
364
- return rec;
365
- });
409
+ items.push(rec);
410
+ }
366
411
  await wrapNestedError(navField, () => targetTable.insertMany(items, {
367
412
  maxDepth,
368
413
  _depth: depth + 1
@@ -371,6 +416,10 @@ else toInsert.push(rec);
371
416
  }
372
417
  }
373
418
  }
419
+ /**
420
+ * Batch-patches VIA (M:N) dependencies after the main patch.
421
+ * Supports patch operators: $replace, $insert, $remove, $update, $upsert.
422
+ */
374
423
  async function batchPatchNestedVia(host, originals, maxDepth, depth) {
375
424
  for (const [navField, relation] of host._meta.relations) {
376
425
  if (relation.direction !== "via" || !relation.viaType) continue;
@@ -387,18 +436,18 @@ async function batchPatchNestedVia(host, originals, maxDepth, depth) {
387
436
  if (!targetPKField || fkToTarget.fields.length !== 1 || fkToThis.fields.length !== 1) continue;
388
437
  for (const original of originals) {
389
438
  const navValue = original[navField];
390
- if (navValue === undefined || navValue === null) continue;
391
- const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : undefined;
392
- if (parentPK === undefined) continue;
439
+ if (navValue === void 0 || navValue === null) continue;
440
+ const parentPK = host._meta.primaryKeys.length === 1 ? original[host._meta.primaryKeys[0]] : void 0;
441
+ if (parentPK === void 0) continue;
393
442
  const ops = extractNavPatchOps(navValue);
394
443
  if (ops.replace) await viaReplace(targetTable, junctionTable, ops.replace, parentPK, targetPKField, fkToThis.fields[0], fkToTarget.fields[0], maxDepth, depth);
395
444
  if (ops.remove && ops.remove.length > 0) {
396
- const targetPKs = ops.remove.map((t) => t[targetPKField]).filter((pk) => pk !== undefined && pk !== null);
445
+ const targetPKs = ops.remove.map((t) => t[targetPKField]).filter((pk) => pk !== void 0 && pk !== null);
397
446
  if (targetPKs.length === 1) await junctionTable.deleteMany({
398
447
  [fkToThis.fields[0]]: parentPK,
399
448
  [fkToTarget.fields[0]]: targetPKs[0]
400
449
  });
401
- else if (targetPKs.length > 1) await junctionTable.deleteMany({
450
+ else if (targetPKs.length > 1) await junctionTable.deleteMany({
402
451
  [fkToThis.fields[0]]: parentPK,
403
452
  [fkToTarget.fields[0]]: { $in: targetPKs }
404
453
  });
@@ -414,7 +463,7 @@ else if (targetPKs.length > 1) await junctionTable.deleteMany({
414
463
  for (const target of ops.upsert) {
415
464
  const rec = { ...target };
416
465
  const pk = rec[targetPKField];
417
- if (pk !== undefined && pk !== null) {
466
+ if (pk !== void 0 && pk !== null) {
418
467
  toUpdate.push(rec);
419
468
  existingPKs.push(pk);
420
469
  } else toInsert.push(rec);
@@ -442,11 +491,10 @@ else if (targetPKs.length > 1) await junctionTable.deleteMany({
442
491
  }
443
492
  }
444
493
  if (toInsert.length > 0) {
445
- const insertResult = await targetTable.insertMany(toInsert, {
494
+ const junctionRows = (await targetTable.insertMany(toInsert, {
446
495
  maxDepth,
447
496
  _depth: depth + 1
448
- });
449
- const junctionRows = insertResult.insertedIds.map((newId) => ({
497
+ })).insertedIds.map((newId) => ({
450
498
  [fkToThis.fields[0]]: parentPK,
451
499
  [fkToTarget.fields[0]]: newId
452
500
  }));
@@ -459,8 +507,8 @@ else if (targetPKs.length > 1) await junctionTable.deleteMany({
459
507
  for (const target of ops.insert) {
460
508
  const rec = { ...target };
461
509
  const pk = rec[targetPKField];
462
- if (pk !== undefined && pk !== null) existingIds.push(pk);
463
- else toInsert.push(rec);
510
+ if (pk !== void 0 && pk !== null) existingIds.push(pk);
511
+ else toInsert.push(rec);
464
512
  }
465
513
  const allIds = [...existingIds];
466
514
  if (toInsert.length > 0) {
@@ -484,29 +532,30 @@ else toInsert.push(rec);
484
532
  /**
485
533
  * Extracts patch operations from a nav field value.
486
534
  * Plain array → $replace. Object with $insert, $remove, etc. → individual ops.
487
- */ function extractNavPatchOps(navValue) {
535
+ */
536
+ function extractNavPatchOps(navValue) {
488
537
  if (Array.isArray(navValue)) return { replace: navValue };
489
538
  if (typeof navValue !== "object" || navValue === null) return {};
490
539
  const obj = navValue;
491
540
  return {
492
- replace: obj.$replace !== undefined ? obj.$replace : undefined,
493
- insert: obj.$insert !== undefined ? obj.$insert : undefined,
494
- remove: obj.$remove !== undefined ? obj.$remove : undefined,
495
- update: obj.$update !== undefined ? obj.$update : undefined,
496
- upsert: obj.$upsert !== undefined ? obj.$upsert : undefined
541
+ replace: obj.$replace !== void 0 ? obj.$replace : void 0,
542
+ insert: obj.$insert !== void 0 ? obj.$insert : void 0,
543
+ remove: obj.$remove !== void 0 ? obj.$remove : void 0,
544
+ update: obj.$update !== void 0 ? obj.$update : void 0,
545
+ upsert: obj.$upsert !== void 0 ? obj.$upsert : void 0
497
546
  };
498
547
  }
499
548
  /**
500
549
  * FROM $replace helper: delete orphans, replace existing, insert new.
501
- */ async function fromReplace(targetTable, children, parentPK, fkField, childPKs, navField, maxDepth, depth) {
550
+ */
551
+ async function fromReplace(targetTable, children, parentPK, fkField, childPKs, navField, maxDepth, depth) {
502
552
  const toReplace = [];
503
553
  const toInsert = [];
504
- const newPKSet = new Set();
554
+ const newPKSet = /* @__PURE__ */ new Set();
505
555
  for (const child of children) {
506
556
  const childData = { ...child };
507
557
  childData[fkField] = parentPK;
508
- const hasPK = childPKs.length > 0 && childPKs.every((pk) => childData[pk] !== undefined);
509
- if (hasPK) {
558
+ if (childPKs.length > 0 && childPKs.every((pk) => childData[pk] !== void 0)) {
510
559
  newPKSet.add(childPKs.map((pk) => String(childData[pk])).join("\0"));
511
560
  toReplace.push(childData);
512
561
  } else toInsert.push(childData);
@@ -525,7 +574,7 @@ else toInsert.push(rec);
525
574
  }
526
575
  }
527
576
  if (orphanFilters.length === 1) await targetTable.deleteMany(orphanFilters[0]);
528
- else if (orphanFilters.length > 1) await targetTable.deleteMany({ $or: orphanFilters });
577
+ else if (orphanFilters.length > 1) await targetTable.deleteMany({ $or: orphanFilters });
529
578
  if (toReplace.length > 0) await wrapNestedError(navField, () => targetTable.bulkReplace(toReplace, {
530
579
  maxDepth,
531
580
  _depth: depth + 1
@@ -537,7 +586,8 @@ else if (orphanFilters.length > 1) await targetTable.deleteMany({ $or: orphanFil
537
586
  }
538
587
  /**
539
588
  * VIA $replace helper: clear junctions, replace/insert targets, rebuild junctions.
540
- */ async function viaReplace(targetTable, junctionTable, targets, parentPK, targetPKField, fkToThisField, fkToTargetField, maxDepth, depth) {
589
+ */
590
+ async function viaReplace(targetTable, junctionTable, targets, parentPK, targetPKField, fkToThisField, fkToTargetField, maxDepth, depth) {
541
591
  await junctionTable.deleteMany({ [fkToThisField]: parentPK });
542
592
  const toReplace = [];
543
593
  const toInsert = [];
@@ -545,9 +595,8 @@ else if (orphanFilters.length > 1) await targetTable.deleteMany({ $or: orphanFil
545
595
  for (const t of targets) {
546
596
  const rec = t;
547
597
  const pk = rec[targetPKField];
548
- if (pk !== undefined && pk !== null) {
549
- const keys = Object.keys(rec).filter((k) => k !== targetPKField);
550
- if (keys.length > 0) toReplace.push({ ...rec });
598
+ if (pk !== void 0 && pk !== null) {
599
+ if (Object.keys(rec).filter((k) => k !== targetPKField).length > 0) toReplace.push({ ...rec });
551
600
  existingIds.push(pk);
552
601
  } else toInsert.push({ ...rec });
553
602
  }
@@ -571,6 +620,5 @@ else if (orphanFilters.length > 1) await targetTable.deleteMany({ $or: orphanFil
571
620
  await junctionTable.insertMany(junctionRows, { maxDepth: 0 });
572
621
  }
573
622
  }
574
-
575
623
  //#endregion
576
- export { DbError, batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, enrichFkViolation, preValidateNestedFrom, remapDeleteFkViolation, validateBatch };
624
+ export { batchPatchNestedTo as a, batchReplaceNestedTo as c, preValidateNestedFrom as d, validateBatch as f, DbError as h, batchPatchNestedFrom as i, batchReplaceNestedVia as l, remapDeleteFkViolation as m, batchInsertNestedTo as n, batchPatchNestedVia as o, enrichFkViolation as p, batchInsertNestedVia as r, batchReplaceNestedFrom as s, batchInsertNestedFrom as t, checkDepthOverflow as u };
@@ -0,0 +1,67 @@
1
+ //#region src/ops.d.ts
2
+ /** A numeric field operation (increment, decrement, or multiply). */
3
+ interface TDbFieldOp {
4
+ $inc?: number;
5
+ $dec?: number;
6
+ $mul?: number;
7
+ }
8
+ /** Increment a numeric field by `value` (default 1). */
9
+ declare function $inc(value?: number): TDbFieldOp;
10
+ /** Decrement a numeric field by `value` (default 1). */
11
+ declare function $dec(value?: number): TDbFieldOp;
12
+ /** Multiply a numeric field by `value`. */
13
+ declare function $mul(value: number): TDbFieldOp;
14
+ /** Replace the entire array. */
15
+ declare function $replace<T>(items: T[]): {
16
+ $replace: T[];
17
+ };
18
+ /** Append items to an array. */
19
+ declare function $insert<T>(items: T[]): {
20
+ $insert: T[];
21
+ };
22
+ /** Insert-or-update items by key. */
23
+ declare function $upsert<T>(items: T[]): {
24
+ $upsert: T[];
25
+ };
26
+ /** Update existing items matched by key. */
27
+ declare function $update<T>(items: Partial<T>[]): {
28
+ $update: Partial<T>[];
29
+ };
30
+ /** Remove items matched by key or value. */
31
+ declare function $remove<T>(items: Partial<T>[]): {
32
+ $remove: Partial<T>[];
33
+ };
34
+ /** Pre-separated field operations, ready for adapters. */
35
+ interface TFieldOps {
36
+ inc?: Record<string, number>;
37
+ mul?: Record<string, number>;
38
+ }
39
+ /**
40
+ * Returns `true` when `value` is a field operation object
41
+ * (`{ $inc: N }`, `{ $dec: N }`, or `{ $mul: N }`).
42
+ */
43
+ declare function isDbFieldOp(value: unknown): value is TDbFieldOp;
44
+ /**
45
+ * Extracts the normalized operation from a field op value.
46
+ * Returns `undefined` when `value` is not a field op.
47
+ *
48
+ * `$dec` is normalized to `{ op: 'inc', value: -N }` so consumers
49
+ * only need to handle `inc` and `mul`.
50
+ */
51
+ declare function getDbFieldOp(value: unknown): {
52
+ op: "inc" | "mul";
53
+ value: number;
54
+ } | undefined;
55
+ /**
56
+ * Separates field operations from a data payload.
57
+ *
58
+ * Mutates `data` in-place (removes op entries) and returns the separated ops.
59
+ * When no ops are found, returns `undefined` — zero allocation for the
60
+ * common non-op case.
61
+ *
62
+ * Hot path: uses `for...in` (no array allocation), inlines detection to
63
+ * avoid intermediate `{ op, value }` objects, and short-circuits on typeof.
64
+ */
65
+ declare function separateFieldOps(data: Record<string, unknown>): TFieldOps | undefined;
66
+ //#endregion
67
+ export { $remove as a, $upsert as c, getDbFieldOp as d, isDbFieldOp as f, $mul as i, TDbFieldOp as l, $inc as n, $replace as o, separateFieldOps as p, $insert as r, $update as s, $dec as t, TFieldOps as u };
@@ -0,0 +1,67 @@
1
+ //#region src/ops.d.ts
2
+ /** A numeric field operation (increment, decrement, or multiply). */
3
+ interface TDbFieldOp {
4
+ $inc?: number;
5
+ $dec?: number;
6
+ $mul?: number;
7
+ }
8
+ /** Increment a numeric field by `value` (default 1). */
9
+ declare function $inc(value?: number): TDbFieldOp;
10
+ /** Decrement a numeric field by `value` (default 1). */
11
+ declare function $dec(value?: number): TDbFieldOp;
12
+ /** Multiply a numeric field by `value`. */
13
+ declare function $mul(value: number): TDbFieldOp;
14
+ /** Replace the entire array. */
15
+ declare function $replace<T>(items: T[]): {
16
+ $replace: T[];
17
+ };
18
+ /** Append items to an array. */
19
+ declare function $insert<T>(items: T[]): {
20
+ $insert: T[];
21
+ };
22
+ /** Insert-or-update items by key. */
23
+ declare function $upsert<T>(items: T[]): {
24
+ $upsert: T[];
25
+ };
26
+ /** Update existing items matched by key. */
27
+ declare function $update<T>(items: Partial<T>[]): {
28
+ $update: Partial<T>[];
29
+ };
30
+ /** Remove items matched by key or value. */
31
+ declare function $remove<T>(items: Partial<T>[]): {
32
+ $remove: Partial<T>[];
33
+ };
34
+ /** Pre-separated field operations, ready for adapters. */
35
+ interface TFieldOps {
36
+ inc?: Record<string, number>;
37
+ mul?: Record<string, number>;
38
+ }
39
+ /**
40
+ * Returns `true` when `value` is a field operation object
41
+ * (`{ $inc: N }`, `{ $dec: N }`, or `{ $mul: N }`).
42
+ */
43
+ declare function isDbFieldOp(value: unknown): value is TDbFieldOp;
44
+ /**
45
+ * Extracts the normalized operation from a field op value.
46
+ * Returns `undefined` when `value` is not a field op.
47
+ *
48
+ * `$dec` is normalized to `{ op: 'inc', value: -N }` so consumers
49
+ * only need to handle `inc` and `mul`.
50
+ */
51
+ declare function getDbFieldOp(value: unknown): {
52
+ op: "inc" | "mul";
53
+ value: number;
54
+ } | undefined;
55
+ /**
56
+ * Separates field operations from a data payload.
57
+ *
58
+ * Mutates `data` in-place (removes op entries) and returns the separated ops.
59
+ * When no ops are found, returns `undefined` — zero allocation for the
60
+ * common non-op case.
61
+ *
62
+ * Hot path: uses `for...in` (no array allocation), inlines detection to
63
+ * avoid intermediate `{ op, value }` objects, and short-circuits on typeof.
64
+ */
65
+ declare function separateFieldOps(data: Record<string, unknown>): TFieldOps | undefined;
66
+ //#endregion
67
+ export { $remove as a, $upsert as c, getDbFieldOp as d, isDbFieldOp as f, $mul as i, TDbFieldOp as l, $inc as n, $replace as o, separateFieldOps as p, $insert as r, $update as s, $dec as t, TFieldOps as u };