@frogfish/k2db 3.0.4 → 3.0.7

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 (2) hide show
  1. package/db.js +161 -24
  2. package/package.json +1 -1
package/db.js CHANGED
@@ -457,7 +457,23 @@ export class K2DB {
457
457
  const msg = err instanceof Error
458
458
  ? `Failed to connect to MongoDB: ${err.message}`
459
459
  : `Failed to connect to MongoDB: ${String(err)}`;
460
- throw wrap(err, ServiceError.SERVICE_UNAVAILABLE, "sys_mdb_init", msg);
460
+ // Preserve existing K2Error severity if already typed; otherwise map to SERVICE_UNAVAILABLE.
461
+ const sev = err instanceof K2Error ? undefined : ServiceError.SERVICE_UNAVAILABLE;
462
+ const k2 = chain(err, "sys_mdb_init", msg, sev, "k2db.init")
463
+ .setSensitive({
464
+ op: "init",
465
+ db: this.conf?.name,
466
+ // safeConnectUrl is already masked (no credentials)
467
+ connectUrl: safeConnectUrl,
468
+ options: summariseValueShape(options),
469
+ mongo: normaliseMongoError(err),
470
+ });
471
+ // Emit once (deduped per error instance)
472
+ emitDbError(k2, {
473
+ op: "init",
474
+ db: this.conf?.name,
475
+ });
476
+ throw k2;
461
477
  }
462
478
  })().finally(() => {
463
479
  // Allow retry after failure; once initialized, subsequent calls return early.
@@ -540,12 +556,16 @@ export class K2DB {
540
556
  }
541
557
  catch (err) {
542
558
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
543
- throw chain(err, "sys_mdb_gc", `Error getting collection: ${collectionName}`, sev, "k2db.getCollection")
544
- .setSensitive({
559
+ const k2 = chain(err, "sys_mdb_gc", `Error getting collection: ${collectionName}`, sev, "k2db.getCollection").setSensitive({
545
560
  op: "getCollection",
546
561
  collection: collectionName,
547
562
  mongo: normaliseMongoError(err),
548
563
  });
564
+ emitDbError(k2, {
565
+ op: "getCollection",
566
+ collection: collectionName,
567
+ });
568
+ throw k2;
549
569
  }
550
570
  }
551
571
  /**
@@ -557,10 +577,35 @@ export class K2DB {
557
577
  async get(collectionName, uuid, scope) {
558
578
  const id = K2DB.normalizeId(uuid);
559
579
  // Note: findOne() decrypts secure-prefixed fields for single-record reads when encryption is enabled.
560
- const res = await this.findOne(collectionName, {
561
- _uuid: id,
562
- _deleted: { $ne: true },
563
- }, undefined, scope);
580
+ let res;
581
+ try {
582
+ res = await this.findOne(collectionName, {
583
+ _uuid: id,
584
+ _deleted: { $ne: true },
585
+ }, undefined, scope);
586
+ }
587
+ catch (err) {
588
+ const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
589
+ const k2 = chain(err, "sys_mdb_get", "Error getting document", sev, "k2db.get");
590
+ // Merge/attach sensitive diagnostics without stomping any existing sensitive payload.
591
+ const prevSensitive = k2.sensitive;
592
+ const mergedSensitive = prevSensitive && typeof prevSensitive === "object" ? { ...prevSensitive } : {};
593
+ Object.assign(mergedSensitive, {
594
+ op: "get",
595
+ collection: collectionName,
596
+ uuid: id,
597
+ scope,
598
+ mongo: normaliseMongoError(err),
599
+ });
600
+ k2.setSensitive?.(mergedSensitive);
601
+ // Emit once (deduped per error instance)
602
+ emitDbError(k2, {
603
+ op: "get",
604
+ collection: collectionName,
605
+ uuid: id,
606
+ });
607
+ throw k2;
608
+ }
564
609
  if (!res) {
565
610
  throw new K2Error(ServiceError.NOT_FOUND, "Document not found", "sys_mdb_get_not_found");
566
611
  }
@@ -628,8 +673,11 @@ export class K2DB {
628
673
  }
629
674
  catch (err) {
630
675
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
631
- throw chain(err, "sys_mdb_fo", "Error finding document", sev, "k2db.findOne")
632
- .setSensitive({
676
+ const k2 = chain(err, "sys_mdb_fo", "Error finding document", sev, "k2db.findOne");
677
+ // Merge/attach sensitive diagnostics without stomping any existing sensitive payload.
678
+ const prevSensitive = k2.sensitive;
679
+ const mergedSensitive = prevSensitive && typeof prevSensitive === "object" ? { ...prevSensitive } : {};
680
+ Object.assign(mergedSensitive, {
633
681
  op: "findOne",
634
682
  collection: collectionName,
635
683
  scope,
@@ -640,6 +688,12 @@ export class K2DB {
640
688
  projectionPreview: redactShallowSecrets(projection),
641
689
  mongo: normaliseMongoError(err),
642
690
  });
691
+ k2.setSensitive?.(mergedSensitive);
692
+ emitDbError(k2, {
693
+ op: "findOne",
694
+ collection: collectionName,
695
+ });
696
+ throw k2;
643
697
  }
644
698
  }
645
699
  /**
@@ -720,7 +774,7 @@ export class K2DB {
720
774
  }
721
775
  catch (err) {
722
776
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
723
- throw chain(err, "sys_mdb_find_error", "Error executing find query", sev, "k2db.find")
777
+ const k2 = chain(err, "sys_mdb_find_error", "Error executing find query", sev, "k2db.find")
724
778
  .setSensitive({
725
779
  op: "find",
726
780
  collection: collectionName,
@@ -736,6 +790,13 @@ export class K2DB {
736
790
  projectionPreview: redactShallowSecrets(projection),
737
791
  mongo: normaliseMongoError(err),
738
792
  });
793
+ emitDbError(k2, {
794
+ op: "find",
795
+ collection: collectionName,
796
+ skip,
797
+ limit,
798
+ });
799
+ throw k2;
739
800
  }
740
801
  }
741
802
  /**
@@ -1182,8 +1243,10 @@ export class K2DB {
1182
1243
  }
1183
1244
  else if (lu.localField && lu.foreignField) {
1184
1245
  // Convert simple lookup to pipeline lookup so we can enforce owner scope (and deleted) in foreign coll.
1185
- const localVar = "__lk";
1186
- const ownerVar = "__own";
1246
+ // NOTE: MongoDB aggregation user variables must start with a letter.
1247
+ // Avoid leading underscores (e.g. "__lk") which fail to parse on MongoDB.
1248
+ const localVar = "k2lk";
1249
+ const ownerVar = "k2own";
1187
1250
  lu.let = { [localVar]: `$${lu.localField}`, [ownerVar]: normalizedScope };
1188
1251
  lu.pipeline = [
1189
1252
  {
@@ -1276,7 +1339,9 @@ export class K2DB {
1276
1339
  }
1277
1340
  else if (lu.localField && lu.foreignField) {
1278
1341
  // Convert simple lookup to pipeline lookup to filter _deleted
1279
- const localVar = "__lk";
1342
+ // NOTE: MongoDB aggregation user variables must start with a letter.
1343
+ // Avoid leading underscores (e.g. "__lk") which fail to parse on MongoDB.
1344
+ const localVar = "k2lk";
1280
1345
  lu.let = { [localVar]: `$${lu.localField}` };
1281
1346
  lu.pipeline = [
1282
1347
  {
@@ -1381,7 +1446,7 @@ export class K2DB {
1381
1446
  throw new K2Error(ServiceError.ALREADY_EXISTS, `A document with _uuid ${document._uuid} already exists.`, "sys_mdb_crv3");
1382
1447
  }
1383
1448
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1384
- throw chain(err, "sys_mdb_sav", "Error saving object to database", sev, "k2db.create")
1449
+ const k2 = chain(err, "sys_mdb_sav", "Error saving object to database", sev, "k2db.create")
1385
1450
  .setSensitive({
1386
1451
  op: "insertOne",
1387
1452
  collection: collectionName,
@@ -1391,6 +1456,12 @@ export class K2DB {
1391
1456
  userFieldPreview: redactShallowSecrets(safeData),
1392
1457
  mongo: normaliseMongoError(err),
1393
1458
  });
1459
+ emitDbError(k2, {
1460
+ op: "create",
1461
+ collection: collectionName,
1462
+ uuid: document._uuid,
1463
+ });
1464
+ throw k2;
1394
1465
  }
1395
1466
  }
1396
1467
  /**
@@ -1434,7 +1505,7 @@ export class K2DB {
1434
1505
  }
1435
1506
  catch (err) {
1436
1507
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1437
- throw chain(err, "sys_mdb_update1", `Error updating ${collectionName}`, sev, "k2db.updateAll")
1508
+ const k2 = chain(err, "sys_mdb_update1", `Error updating ${collectionName}`, sev, "k2db.updateAll")
1438
1509
  .setSensitive({
1439
1510
  op: "updateMany",
1440
1511
  collection: collectionName,
@@ -1446,6 +1517,11 @@ export class K2DB {
1446
1517
  valuesPreview: redactShallowSecrets(values),
1447
1518
  mongo: normaliseMongoError(err),
1448
1519
  });
1520
+ emitDbError(k2, {
1521
+ op: "updateAll",
1522
+ collection: collectionName,
1523
+ });
1524
+ throw k2;
1449
1525
  }
1450
1526
  }
1451
1527
  /**
@@ -1502,7 +1578,7 @@ export class K2DB {
1502
1578
  }
1503
1579
  catch (err) {
1504
1580
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1505
- throw chain(err, "sys_mdb_update_error", `Error updating ${collectionName}`, sev, "k2db.update")
1581
+ const k2 = chain(err, "sys_mdb_update_error", `Error updating ${collectionName}`, sev, "k2db.update")
1506
1582
  .setSensitive({
1507
1583
  op: replace ? "replaceOne" : "updateOne",
1508
1584
  collection: collectionName,
@@ -1513,6 +1589,13 @@ export class K2DB {
1513
1589
  dataPreview: redactShallowSecrets(data),
1514
1590
  mongo: normaliseMongoError(err),
1515
1591
  });
1592
+ emitDbError(k2, {
1593
+ op: "update",
1594
+ collection: collectionName,
1595
+ uuid: id,
1596
+ replace,
1597
+ });
1598
+ throw k2;
1516
1599
  }
1517
1600
  }
1518
1601
  /**
@@ -1531,8 +1614,7 @@ export class K2DB {
1531
1614
  }
1532
1615
  catch (err) {
1533
1616
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1534
- throw chain(err, "sys_mdb_deleteall_update", `Error deleting from ${collectionName}`, sev, "k2db.deleteAll")
1535
- .setSensitive({
1617
+ const k2 = chain(err, "sys_mdb_deleteall_update", `Error deleting from ${collectionName}`, sev, "k2db.deleteAll").setSensitive({
1536
1618
  op: "softDeleteMany",
1537
1619
  collection: collectionName,
1538
1620
  scope,
@@ -1540,6 +1622,11 @@ export class K2DB {
1540
1622
  criteriaPreview: redactShallowSecrets(criteria),
1541
1623
  mongo: normaliseMongoError(err),
1542
1624
  });
1625
+ emitDbError(k2, {
1626
+ op: "deleteAll",
1627
+ collection: collectionName,
1628
+ });
1629
+ throw k2;
1543
1630
  }
1544
1631
  }
1545
1632
  /**
@@ -1569,14 +1656,19 @@ export class K2DB {
1569
1656
  }
1570
1657
  catch (err) {
1571
1658
  const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1572
- throw chain(err, "sys_mdb_remove_upd", "Error removing object from collection", sev, "k2db.delete")
1573
- .setSensitive({
1659
+ const k2 = chain(err, "sys_mdb_remove_upd", "Error removing object from collection", sev, "k2db.delete").setSensitive({
1574
1660
  op: "softDeleteOne",
1575
1661
  collection: collectionName,
1576
1662
  uuid: id,
1577
1663
  scope,
1578
1664
  mongo: normaliseMongoError(err),
1579
1665
  });
1666
+ emitDbError(k2, {
1667
+ op: "delete",
1668
+ collection: collectionName,
1669
+ uuid: id,
1670
+ });
1671
+ throw k2;
1580
1672
  }
1581
1673
  }
1582
1674
  /**
@@ -1637,8 +1729,9 @@ export class K2DB {
1637
1729
  }
1638
1730
  const collection = await this.getCollection(collectionName);
1639
1731
  const cutoff = Date.now() - olderThanMs;
1732
+ let delFilter;
1640
1733
  try {
1641
- const delFilter = this.applyScopeToCriteria({
1734
+ delFilter = this.applyScopeToCriteria({
1642
1735
  _deleted: true,
1643
1736
  _updated: { $lte: cutoff },
1644
1737
  }, scope);
@@ -1646,7 +1739,23 @@ export class K2DB {
1646
1739
  return { purged: res.deletedCount ?? 0 };
1647
1740
  }
1648
1741
  catch (err) {
1649
- throw wrap(err, ServiceError.SYSTEM_ERROR, 'sys_mdb_purge_older', 'Error purging deleted items by age');
1742
+ const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1743
+ const k2 = chain(err, 'sys_mdb_purge_older', 'Error purging deleted items by age', sev, 'k2db.purgeDeletedOlderThan').setSensitive({
1744
+ op: 'purgeDeletedOlderThan',
1745
+ collection: collectionName,
1746
+ scope,
1747
+ olderThanMs,
1748
+ cutoff,
1749
+ delFilterShape: summariseValueShape(delFilter),
1750
+ delFilterPreview: redactShallowSecrets(delFilter),
1751
+ mongo: normaliseMongoError(err),
1752
+ });
1753
+ emitDbError(k2, {
1754
+ op: 'purgeDeletedOlderThan',
1755
+ collection: collectionName,
1756
+ olderThanMs,
1757
+ });
1758
+ throw k2;
1650
1759
  }
1651
1760
  }
1652
1761
  /**
@@ -1668,7 +1777,23 @@ export class K2DB {
1668
1777
  return { status: "restored", modified: res.modifiedCount };
1669
1778
  }
1670
1779
  catch (err) {
1671
- throw wrap(err, ServiceError.SYSTEM_ERROR, "sys_mdb_pres", "Error restoring a deleted item");
1780
+ const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1781
+ const k2 = chain(err, "sys_mdb_pres", "Error restoring a deleted item", sev, "k2db.restore")
1782
+ .setSensitive({
1783
+ op: "restore",
1784
+ collection: collectionName,
1785
+ scope,
1786
+ criteriaShape: summariseValueShape(crit),
1787
+ criteriaPreview: redactShallowSecrets(crit),
1788
+ queryShape: summariseValueShape(query),
1789
+ queryPreview: redactShallowSecrets(query),
1790
+ mongo: normaliseMongoError(err),
1791
+ });
1792
+ emitDbError(k2, {
1793
+ op: "restore",
1794
+ collection: collectionName,
1795
+ });
1796
+ throw k2;
1672
1797
  }
1673
1798
  }
1674
1799
  /**
@@ -1731,7 +1856,19 @@ export class K2DB {
1731
1856
  return { status: "ok" };
1732
1857
  }
1733
1858
  catch (err) {
1734
- throw wrap(err, ServiceError.SYSTEM_ERROR, "sys_mdb_drop", "Error dropping collection");
1859
+ const sev = err instanceof K2Error ? undefined : ServiceError.SYSTEM_ERROR;
1860
+ const k2 = chain(err, "sys_mdb_drop", "Error dropping collection", sev, "k2db.drop").setSensitive({
1861
+ op: "drop",
1862
+ collection: collectionName,
1863
+ scope,
1864
+ normalizedScope,
1865
+ mongo: normaliseMongoError(err),
1866
+ });
1867
+ emitDbError(k2, {
1868
+ op: "drop",
1869
+ collection: collectionName,
1870
+ });
1871
+ throw k2;
1735
1872
  }
1736
1873
  }
1737
1874
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frogfish/k2db",
3
- "version": "3.0.4",
3
+ "version": "3.0.7",
4
4
  "description": "A data handling library for K2 applications.",
5
5
  "type": "module",
6
6
  "main": "data.js",