@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.
- package/db.js +161 -24
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
-
|
|
632
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
/**
|