@utaba/deep-memory-storage-cosmosdb 0.5.0 → 0.6.0

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
@@ -1319,6 +1319,23 @@ var CosmosDbProvider = class {
1319
1319
  rejectUnauthorized: config.rejectUnauthorized
1320
1320
  });
1321
1321
  }
1322
+ /**
1323
+ * Reject any repositoryId that is not a valid v4 UUID.
1324
+ *
1325
+ * Every partition-scoped query filters on repositoryId. Since repositoryId is
1326
+ * the CosmosDB partition key, a predicate with a different value would target
1327
+ * a different partition. Strict format validation at the provider boundary
1328
+ * prevents injection (e.g. strings containing quotes or Gremlin syntax) from
1329
+ * ever reaching query construction.
1330
+ */
1331
+ assertValidRepositoryId(repositoryId) {
1332
+ if (!(0, import_deep_memory5.isValidUuid)(repositoryId)) {
1333
+ throw new import_deep_memory5.InvalidInputError(
1334
+ "repositoryId",
1335
+ `repositoryId must be a valid v4 UUID; got '${repositoryId}'`
1336
+ );
1337
+ }
1338
+ }
1322
1339
  // ─── Lifecycle ─────────────────────────────────────────────────────
1323
1340
  async initialize() {
1324
1341
  await this.conn.connect();
@@ -1404,93 +1421,120 @@ var CosmosDbProvider = class {
1404
1421
  }
1405
1422
  // ─── Repository ────────────────────────────────────────────────────
1406
1423
  async createRepository(config) {
1424
+ this.assertValidRepositoryId(config.repositoryId);
1407
1425
  return createRepository(this.conn, config);
1408
1426
  }
1409
1427
  async getRepository(repositoryId) {
1428
+ this.assertValidRepositoryId(repositoryId);
1410
1429
  return getRepository(this.conn, repositoryId);
1411
1430
  }
1412
1431
  async listRepositories(filter) {
1413
1432
  return listRepositories(this.conn, filter);
1414
1433
  }
1415
1434
  async updateRepository(repositoryId, updates) {
1435
+ this.assertValidRepositoryId(repositoryId);
1416
1436
  return updateRepository(this.conn, repositoryId, updates);
1417
1437
  }
1418
1438
  async deleteRepository(repositoryId, onProgress) {
1439
+ this.assertValidRepositoryId(repositoryId);
1419
1440
  return deleteRepository(this.conn, repositoryId, onProgress);
1420
1441
  }
1421
1442
  async deleteAllContents(repositoryId, onProgress) {
1443
+ this.assertValidRepositoryId(repositoryId);
1422
1444
  return deleteAllContents(this.conn, repositoryId, onProgress);
1423
1445
  }
1424
1446
  async getRepositoryStats(repositoryId) {
1447
+ this.assertValidRepositoryId(repositoryId);
1425
1448
  return getRepositoryStats(this.conn, repositoryId);
1426
1449
  }
1427
1450
  // ─── Vocabulary ────────────────────────────────────────────────────
1428
1451
  async getVocabulary(repositoryId) {
1452
+ this.assertValidRepositoryId(repositoryId);
1429
1453
  return getVocabulary(this.conn, repositoryId);
1430
1454
  }
1431
1455
  async saveVocabulary(repositoryId, vocabulary) {
1456
+ this.assertValidRepositoryId(repositoryId);
1432
1457
  return saveVocabulary(this.conn, repositoryId, vocabulary);
1433
1458
  }
1434
1459
  async getVocabularyChangeLog(repositoryId, options) {
1460
+ this.assertValidRepositoryId(repositoryId);
1435
1461
  return getVocabularyChangeLog(this.conn, repositoryId, options);
1436
1462
  }
1437
1463
  // ─── Entities ──────────────────────────────────────────────────────
1438
1464
  async createEntity(repositoryId, entity) {
1465
+ this.assertValidRepositoryId(repositoryId);
1439
1466
  return createEntity(this.conn, repositoryId, entity);
1440
1467
  }
1441
1468
  async getEntity(repositoryId, entityId) {
1469
+ this.assertValidRepositoryId(repositoryId);
1442
1470
  return getEntity(this.conn, repositoryId, entityId);
1443
1471
  }
1444
1472
  async getEntityBySlug(repositoryId, slug) {
1473
+ this.assertValidRepositoryId(repositoryId);
1445
1474
  return getEntityBySlug(this.conn, repositoryId, slug);
1446
1475
  }
1447
1476
  async getEntities(repositoryId, entityIds) {
1477
+ this.assertValidRepositoryId(repositoryId);
1448
1478
  return getEntities(this.conn, repositoryId, entityIds);
1449
1479
  }
1450
1480
  async updateEntity(repositoryId, entityId, updates) {
1481
+ this.assertValidRepositoryId(repositoryId);
1451
1482
  return updateEntity(this.conn, repositoryId, entityId, updates);
1452
1483
  }
1453
1484
  async deleteEntity(repositoryId, entityId) {
1485
+ this.assertValidRepositoryId(repositoryId);
1454
1486
  return deleteEntity(this.conn, repositoryId, entityId);
1455
1487
  }
1456
1488
  async deleteEntitiesByType(repositoryId, entityType) {
1489
+ this.assertValidRepositoryId(repositoryId);
1457
1490
  return deleteEntitiesByType(this.conn, repositoryId, entityType);
1458
1491
  }
1459
1492
  async findEntities(repositoryId, query) {
1493
+ this.assertValidRepositoryId(repositoryId);
1460
1494
  return findEntities(this.conn, repositoryId, query);
1461
1495
  }
1462
1496
  // ─── Relationships ─────────────────────────────────────────────────
1463
1497
  async createRelationship(repositoryId, relationship) {
1498
+ this.assertValidRepositoryId(repositoryId);
1464
1499
  return createRelationship(this.conn, repositoryId, relationship);
1465
1500
  }
1466
1501
  async getRelationship(repositoryId, relationshipId) {
1502
+ this.assertValidRepositoryId(repositoryId);
1467
1503
  return getRelationship(this.conn, repositoryId, relationshipId);
1468
1504
  }
1469
1505
  async getEntityRelationships(repositoryId, entityId, options) {
1506
+ this.assertValidRepositoryId(repositoryId);
1470
1507
  return getEntityRelationships(this.conn, repositoryId, entityId, options);
1471
1508
  }
1472
1509
  async deleteRelationship(repositoryId, relationshipId) {
1510
+ this.assertValidRepositoryId(repositoryId);
1473
1511
  return deleteRelationship(this.conn, repositoryId, relationshipId);
1474
1512
  }
1475
1513
  async deleteRelationshipsByType(repositoryId, relationshipType) {
1514
+ this.assertValidRepositoryId(repositoryId);
1476
1515
  return deleteRelationshipsByType(this.conn, repositoryId, relationshipType);
1477
1516
  }
1478
1517
  // ─── Graph Traversal (StorageProvider) ─────────────────────────────
1479
1518
  async exploreNeighborhood(repositoryId, entityId, options) {
1519
+ this.assertValidRepositoryId(repositoryId);
1480
1520
  return exploreNeighborhood(this.conn, repositoryId, entityId, options);
1481
1521
  }
1482
1522
  async findPaths(repositoryId, sourceId, targetId, options) {
1523
+ this.assertValidRepositoryId(repositoryId);
1483
1524
  return findPaths(this.conn, repositoryId, sourceId, targetId, options);
1484
1525
  }
1485
1526
  // ─── Timeline ──────────────────────────────────────────────────────
1486
1527
  async getTimeline(repositoryId, entityId, options) {
1528
+ this.assertValidRepositoryId(repositoryId);
1487
1529
  return getTimeline(this.conn, repositoryId, entityId, options);
1488
1530
  }
1489
1531
  // ─── Bulk Operations ───────────────────────────────────────────────
1490
1532
  exportAll(repositoryId) {
1533
+ this.assertValidRepositoryId(repositoryId);
1491
1534
  return exportAll(this.conn, repositoryId);
1492
1535
  }
1493
1536
  async importBulk(repositoryId, data, options) {
1537
+ this.assertValidRepositoryId(repositoryId);
1494
1538
  return importBulk(this.conn, repositoryId, data, options);
1495
1539
  }
1496
1540
  // ─── GraphTraversalProvider ────────────────────────────────────────
@@ -1507,17 +1551,20 @@ var CosmosDbProvider = class {
1507
1551
  supportsRelationshipSummary: false
1508
1552
  };
1509
1553
  }
1510
- async traverse(repositoryId, spec, compiledQuery) {
1554
+ async traverse(repositoryId, spec) {
1555
+ this.assertValidRepositoryId(repositoryId);
1511
1556
  const startTime = Date.now();
1512
1557
  const vocabulary = await this.getVocabulary(repositoryId);
1513
- const compiled = compiledQuery ? { query: compiledQuery, params: {}, estimatedFanOut: 100 } : this.compiler.compile(spec, vocabulary);
1558
+ const compiled = this.compiler.compile(spec, vocabulary);
1514
1559
  const scopedQuery = compiled.query.replace(
1515
1560
  "g.V()",
1516
- `g.V().has('repositoryId', '${repositoryId}')`
1561
+ "g.V().has('repositoryId', pRid)"
1517
1562
  );
1563
+ const scopedParams = { ...compiled.params, pRid: repositoryId };
1518
1564
  try {
1519
- const result = await this.conn.submit(scopedQuery, compiled.params);
1565
+ const result = await this.conn.submit(scopedQuery, scopedParams);
1520
1566
  const executionTimeMs = Date.now() - startTime;
1567
+ const detailLevel = spec.detailLevel ?? "summary";
1521
1568
  const entities = [];
1522
1569
  const relationships = [];
1523
1570
  const paths = [];
@@ -1529,7 +1576,8 @@ var CosmosDbProvider = class {
1529
1576
  for (const obj of pathData.objects) {
1530
1577
  const props = obj;
1531
1578
  if (props["entityType"]) {
1532
- pathEntities.push(entityFromGremlin(props));
1579
+ const stored = entityFromGremlin(props);
1580
+ pathEntities.push((0, import_deep_memory5.projectEntity)(stored, detailLevel));
1533
1581
  }
1534
1582
  }
1535
1583
  if (pathEntities.length > 0) {
@@ -1543,8 +1591,8 @@ var CosmosDbProvider = class {
1543
1591
  }
1544
1592
  } else {
1545
1593
  for (const item of result.items) {
1546
- const entity = entityFromGremlin(item);
1547
- entities.push(entity);
1594
+ const stored = entityFromGremlin(item);
1595
+ entities.push((0, import_deep_memory5.projectEntity)(stored, detailLevel));
1548
1596
  }
1549
1597
  }
1550
1598
  const limit = spec.limit ?? 50;
@@ -1576,6 +1624,27 @@ var CosmosDbProvider = class {
1576
1624
  );
1577
1625
  }
1578
1626
  }
1627
+ /**
1628
+ * Execute a raw Gremlin query with caller-supplied bindings.
1629
+ *
1630
+ * ⚠️ ELEVATED PRIVILEGE — SYSTEM-LEVEL OPERATION ⚠️
1631
+ *
1632
+ * This method is an unscoped pass-through: it does not filter by repository,
1633
+ * does not inject the partition key, and performs no validation on the query
1634
+ * string. A single call can read or mutate any vertex or edge in the
1635
+ * container regardless of which repository (partition) it belongs to.
1636
+ *
1637
+ * DO NOT expose this method to AI agents, end users, or any untrusted caller.
1638
+ * It is intended for:
1639
+ * - administrative tooling (migrations, diagnostics, repairs)
1640
+ * - internal library operations that need cross-partition reach
1641
+ *
1642
+ * `repositoryId` is accepted for interface symmetry but is intentionally
1643
+ * ignored here — the caller is trusted to scope the query themselves.
1644
+ *
1645
+ * For agent-facing graph queries use {@link traverse}, which enforces the
1646
+ * repositoryId partition predicate.
1647
+ */
1579
1648
  async executeNativeQuery(_repositoryId, query, params) {
1580
1649
  const startTime = Date.now();
1581
1650
  try {