@utaba/deep-memory 0.17.0 → 0.19.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.
@@ -1,4 +1,4 @@
1
- import { au as StorageRepositoryConfig, aB as StoredRepository, ad as RepositoryFilter, T as PaginatedResult, aC as StoredRepositorySummary, ah as RepositoryUpdate, g as DeleteProgressCallback, af as RepositoryStats, O as MemoryVocabulary, U as PaginationOptions, aM as VocabularyChangeRecord, ay as StoredEntity, az as StoredEntityUpdate, an as StorageFindQuery, aA as StoredRelationship, a5 as RelationshipQueryOptions, am as StorageExploreOptions, ao as StorageNeighborhood, as as StoragePathOptions, at as StoragePathResult, aw as StorageTimelineOptions, ax as StorageTimelineResult, s as ExportChunk, J as ImportChunk, B as BulkImportOptions, d as BulkImportResult } from './portability-Dbm6vCdD.js';
1
+ import { au as StorageRepositoryConfig, aB as StoredRepository, ad as RepositoryFilter, T as PaginatedResult, aC as StoredRepositorySummary, ah as RepositoryUpdate, g as DeleteProgressCallback, af as RepositoryStats, O as MemoryVocabulary, U as PaginationOptions, aM as VocabularyChangeRecord, ay as StoredEntity, az as StoredEntityUpdate, an as StorageFindQuery, aA as StoredRelationship, a5 as RelationshipQueryOptions, am as StorageExploreOptions, ao as StorageNeighborhood, as as StoragePathOptions, at as StoragePathResult, aw as StorageTimelineOptions, ax as StorageTimelineResult, s as ExportChunk, J as ImportChunk, B as BulkImportOptions, d as BulkImportResult } from './portability-BHyXmv5-.js';
2
2
 
3
3
  /** Result returned by ensureSchema describing what actions were taken. */
4
4
  interface EnsureSchemaResult {
@@ -1,4 +1,4 @@
1
- import { au as StorageRepositoryConfig, aB as StoredRepository, ad as RepositoryFilter, T as PaginatedResult, aC as StoredRepositorySummary, ah as RepositoryUpdate, g as DeleteProgressCallback, af as RepositoryStats, O as MemoryVocabulary, U as PaginationOptions, aM as VocabularyChangeRecord, ay as StoredEntity, az as StoredEntityUpdate, an as StorageFindQuery, aA as StoredRelationship, a5 as RelationshipQueryOptions, am as StorageExploreOptions, ao as StorageNeighborhood, as as StoragePathOptions, at as StoragePathResult, aw as StorageTimelineOptions, ax as StorageTimelineResult, s as ExportChunk, J as ImportChunk, B as BulkImportOptions, d as BulkImportResult } from './portability-Dbm6vCdD.cjs';
1
+ import { au as StorageRepositoryConfig, aB as StoredRepository, ad as RepositoryFilter, T as PaginatedResult, aC as StoredRepositorySummary, ah as RepositoryUpdate, g as DeleteProgressCallback, af as RepositoryStats, O as MemoryVocabulary, U as PaginationOptions, aM as VocabularyChangeRecord, ay as StoredEntity, az as StoredEntityUpdate, an as StorageFindQuery, aA as StoredRelationship, a5 as RelationshipQueryOptions, am as StorageExploreOptions, ao as StorageNeighborhood, as as StoragePathOptions, at as StoragePathResult, aw as StorageTimelineOptions, ax as StorageTimelineResult, s as ExportChunk, J as ImportChunk, B as BulkImportOptions, d as BulkImportResult } from './portability-BHyXmv5-.cjs';
2
2
 
3
3
  /** Result returned by ensureSchema describing what actions were taken. */
4
4
  interface EnsureSchemaResult {
package/dist/index.cjs CHANGED
@@ -1438,13 +1438,36 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1438
1438
  unionFullLength = unionElements.length;
1439
1439
  const pageSlice = unionElements.slice(offset, offset + limit);
1440
1440
  const pageEntries = [];
1441
+ const pageEntityIds = /* @__PURE__ */ new Set();
1441
1442
  for (const element of pageSlice) {
1442
1443
  if (element.kind === "entity") {
1443
- pageEntries.push(element.entry);
1444
+ if (!pageEntityIds.has(element.entry.entity.id)) {
1445
+ pageEntityIds.add(element.entry.entity.id);
1446
+ pageEntries.push(element.entry);
1447
+ }
1444
1448
  } else {
1445
1449
  pagedAllRels.push(element.rel);
1446
1450
  }
1447
1451
  }
1452
+ if (pagedAllRels.length > 0) {
1453
+ const entityIndex = /* @__PURE__ */ new Map();
1454
+ for (const el of unionElements) {
1455
+ if (el.kind === "entity" && !entityIndex.has(el.entry.entity.id)) {
1456
+ entityIndex.set(el.entry.entity.id, el.entry);
1457
+ }
1458
+ }
1459
+ for (const rel of pagedAllRels) {
1460
+ for (const endpointId of [rel.sourceEntityId, rel.targetEntityId]) {
1461
+ if (!pageEntityIds.has(endpointId)) {
1462
+ const entry = entityIndex.get(endpointId);
1463
+ if (entry) {
1464
+ pageEntityIds.add(endpointId);
1465
+ pageEntries.push(entry);
1466
+ }
1467
+ }
1468
+ }
1469
+ }
1470
+ }
1448
1471
  paged = pageEntries;
1449
1472
  total = pageEntries.length + pagedAllRels.length;
1450
1473
  } else {
@@ -1472,13 +1495,13 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1472
1495
  direction: "both",
1473
1496
  limit: 1e4
1474
1497
  });
1475
- const summary = { outbound: {}, inbound: {} };
1498
+ const summary = { out: {}, in: {} };
1476
1499
  for (const rel of result.items) {
1477
1500
  if (rel.sourceEntityId === entry.entity.id) {
1478
- summary.outbound[rel.relationshipType] = (summary.outbound[rel.relationshipType] ?? 0) + 1;
1501
+ summary.out[rel.relationshipType] = (summary.out[rel.relationshipType] ?? 0) + 1;
1479
1502
  }
1480
1503
  if (rel.targetEntityId === entry.entity.id) {
1481
- summary.inbound[rel.relationshipType] = (summary.inbound[rel.relationshipType] ?? 0) + 1;
1504
+ summary.in[rel.relationshipType] = (summary.in[rel.relationshipType] ?? 0) + 1;
1482
1505
  }
1483
1506
  }
1484
1507
  return summary;
@@ -1541,7 +1564,7 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1541
1564
  type: rel.relationshipType,
1542
1565
  sourceEntityId: rel.sourceEntityId,
1543
1566
  targetEntityId: rel.targetEntityId,
1544
- direction: "outbound",
1567
+ direction: "out",
1545
1568
  properties: rel.properties
1546
1569
  }));
1547
1570
  } else if (!suppressEntities && spec.returnMode === "path") {
@@ -1556,7 +1579,7 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1556
1579
  type: rel.relationshipType,
1557
1580
  sourceEntityId: rel.sourceEntityId,
1558
1581
  targetEntityId: rel.targetEntityId,
1559
- direction: fromId === rel.sourceEntityId ? "outbound" : "inbound",
1582
+ direction: fromId === rel.sourceEntityId ? "out" : "in",
1560
1583
  properties: rel.properties
1561
1584
  });
1562
1585
  }
@@ -1595,7 +1618,7 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1595
1618
  type: rel.relationshipType,
1596
1619
  sourceEntityId: rel.sourceEntityId,
1597
1620
  targetEntityId: rel.targetEntityId,
1598
- direction: fromId === rel.sourceEntityId ? "outbound" : "inbound",
1621
+ direction: fromId === rel.sourceEntityId ? "out" : "in",
1599
1622
  properties: rel.properties
1600
1623
  };
1601
1624
  })
@@ -1624,7 +1647,7 @@ async function executeFallbackTraversal(repositoryId, storage, spec) {
1624
1647
  };
1625
1648
  }
1626
1649
  async function executeSingleStep(repositoryId, storage, frontier, step, onStorageCall) {
1627
- const direction = mapDirection(step.direction);
1650
+ const direction = step.direction;
1628
1651
  const pendingEdges = [];
1629
1652
  for (const entry of frontier) {
1630
1653
  const rels = await storage.getEntityRelationships(repositoryId, entry.entity.id, {
@@ -1719,16 +1742,6 @@ async function resolveEntity(repositoryId, storage, idOrSlug) {
1719
1742
  if (byId) return byId;
1720
1743
  return storage.getEntityBySlug(repositoryId, idOrSlug);
1721
1744
  }
1722
- function mapDirection(direction) {
1723
- switch (direction) {
1724
- case "out":
1725
- return "outbound";
1726
- case "in":
1727
- return "inbound";
1728
- case "both":
1729
- return "both";
1730
- }
1731
- }
1732
1745
  function getTargetId(rel, currentEntityId) {
1733
1746
  if (rel.sourceEntityId === currentEntityId) {
1734
1747
  return rel.targetEntityId;
@@ -1771,6 +1784,12 @@ function buildAllModeUnion(entries) {
1771
1784
  }
1772
1785
  for (let depth = 1; depth <= maxDepth; depth++) {
1773
1786
  const bucket = byDepth.get(depth) ?? [];
1787
+ for (const entry of bucket) {
1788
+ if (!seenEntities.has(entry.entity.id)) {
1789
+ seenEntities.add(entry.entity.id);
1790
+ result.push({ kind: "entity", entry });
1791
+ }
1792
+ }
1774
1793
  for (const entry of bucket) {
1775
1794
  const newRel = entry.relationshipPath[depth - 1];
1776
1795
  if (newRel && !seenRels.has(newRel.id)) {
@@ -1778,12 +1797,6 @@ function buildAllModeUnion(entries) {
1778
1797
  result.push({ kind: "relationship", rel: newRel });
1779
1798
  }
1780
1799
  }
1781
- for (const entry of bucket) {
1782
- if (!seenEntities.has(entry.entity.id)) {
1783
- seenEntities.add(entry.entity.id);
1784
- result.push({ kind: "entity", entry });
1785
- }
1786
- }
1787
1800
  }
1788
1801
  return result;
1789
1802
  }
@@ -1923,7 +1936,7 @@ var GraphTraversal = class {
1923
1936
  return {
1924
1937
  id: rid,
1925
1938
  type: r?.type ?? "unknown",
1926
- direction: r?.sourceEntityId === currentEntityId ? "outbound" : "inbound",
1939
+ direction: r?.sourceEntityId === currentEntityId ? "out" : "in",
1927
1940
  properties: r?.properties ?? {}
1928
1941
  };
1929
1942
  })
@@ -2550,7 +2563,7 @@ var MemoryRepository = class {
2550
2563
  inbound[rel.relationshipType] = (inbound[rel.relationshipType] ?? 0) + 1;
2551
2564
  }
2552
2565
  }
2553
- return { outbound, inbound };
2566
+ return { out: outbound, in: inbound };
2554
2567
  }
2555
2568
  async getRelationshipsForEntities(entityIds) {
2556
2569
  const seen = /* @__PURE__ */ new Set();
@@ -2758,9 +2771,10 @@ var MemoryRepository = class {
2758
2771
  ### Step 1 \u2014 Discover before you traverse
2759
2772
 
2760
2773
  Before following relationships from an entity, check that it actually has relationships.
2761
- Graph queries include \`relationshipSummary\` on every returned entity by default
2762
- (outbound and inbound counts by type). Inspect this before traversing \u2014 entities with
2763
- zero outbound counts for the relationship type you need have no connections to follow.
2774
+ Graph queries include \`relationshipSummary\` on every returned entity by default \u2014
2775
+ \`out\` (entity is source) and \`in\` (entity is target) counts by relationship type.
2776
+ Inspect this before traversing: entities with zero counts for the relationship type
2777
+ you need have no connections to follow.
2764
2778
  To skip summaries and reduce response size, set \`includeRelationshipSummary: false\`.
2765
2779
 
2766
2780
  ### Step 2 \u2014 Use projection for aggregation
@@ -3643,7 +3657,7 @@ var EventBus = class {
3643
3657
  };
3644
3658
 
3645
3659
  // src/portability/RepositoryExporter.ts
3646
- var LIBRARY_VERSION = true ? "0.17.0" : "0.1.0";
3660
+ var LIBRARY_VERSION = true ? "0.19.0" : "0.1.0";
3647
3661
  var RepositoryExporter = class {
3648
3662
  storage;
3649
3663
  provenance;
@@ -3675,6 +3689,9 @@ var RepositoryExporter = class {
3675
3689
  type: repo.type,
3676
3690
  label: repo.label,
3677
3691
  description: repo.description,
3692
+ legal: repo.legal,
3693
+ owner: repo.owner,
3694
+ metadata: repo.metadata,
3678
3695
  vocabularyVersion: vocabulary.version,
3679
3696
  governanceMode: repo.governanceConfig.mode
3680
3697
  },
@@ -4072,7 +4089,10 @@ var RepositoryImporter = class {
4072
4089
  type: target.config.type,
4073
4090
  label: target.config.label,
4074
4091
  description: target.config.description,
4092
+ legal: target.config.legal,
4093
+ owner: target.config.owner,
4075
4094
  governanceConfig: target.config.governance ?? { mode: "open" },
4095
+ metadata: target.config.metadata,
4076
4096
  createdAt: now,
4077
4097
  createdBy: this.actorId
4078
4098
  });
@@ -4587,6 +4607,7 @@ var DeepMemory = class {
4587
4607
  /** Import a repository from a portable archive */
4588
4608
  async importRepository(archive, options) {
4589
4609
  await this.ensureInitialized();
4610
+ this.validateRepositoryId(options.target.repositoryId);
4590
4611
  await this.globalEventBus.emit("import:started", { repositoryId: options.target.repositoryId });
4591
4612
  const importer = new RepositoryImporter({
4592
4613
  storage: this.storage,
@@ -4687,6 +4708,7 @@ var DeepMemory = class {
4687
4708
  */
4688
4709
  async importRepositoryStream(header, chunks, options) {
4689
4710
  await this.ensureInitialized();
4711
+ this.validateRepositoryId(options.target.repositoryId);
4690
4712
  const importer = new RepositoryImporter({
4691
4713
  storage: this.storage,
4692
4714
  actorId: this.provenance.getContext().actorId,
@@ -4921,8 +4943,8 @@ var GremlinCompiler = class {
4921
4943
  const branches = [`__.identity().${VERTEX_PROJECT_EXPR}`];
4922
4944
  let prefix = "";
4923
4945
  for (const { edge, vertex, entityFilters } of compiledSteps) {
4924
- branches.push(`__${prefix}${edge}.${EDGE_PROJECT_EXPR}`);
4925
4946
  branches.push(`__${prefix}${edge}${vertex}${entityFilters}.${VERTEX_PROJECT_EXPR}`);
4947
+ branches.push(`__${prefix}${edge}.${EDGE_PROJECT_EXPR}`);
4926
4948
  prefix = `${prefix}${edge}${vertex}`;
4927
4949
  estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
4928
4950
  }
@@ -4958,12 +4980,16 @@ var GremlinCompiler = class {
4958
4980
  }
4959
4981
  }
4960
4982
  }
4961
- if (returnMode === "terminal" && spec.dedup !== false) {
4983
+ const emitProjection = spec.projection !== void 0 && returnMode === "terminal";
4984
+ if (returnMode === "terminal" && !emitProjection && spec.dedup !== false) {
4962
4985
  parts.push(".dedup()");
4963
4986
  }
4964
4987
  if (returnMode === "path") {
4965
4988
  parts.push(".simplePath()");
4966
4989
  }
4990
+ if (emitProjection) {
4991
+ parts.push(emitProjectionTerminal(spec.projection));
4992
+ }
4967
4993
  const limit = spec.limit ?? 50;
4968
4994
  const offset = spec.offset ?? 0;
4969
4995
  const pOffset = nextParam(offset);
@@ -4971,7 +4997,8 @@ var GremlinCompiler = class {
4971
4997
  parts.push(`.range(${pOffset}, ${pEnd})`);
4972
4998
  params["_limit"] = limit;
4973
4999
  params["_offset"] = offset;
4974
- if (returnMode === "terminal") {
5000
+ if (emitProjection) {
5001
+ } else if (returnMode === "terminal") {
4975
5002
  parts.push(`.${VERTEX_PROJECT_EXPR}`);
4976
5003
  } else {
4977
5004
  parts.push(`.path().by(${VERTEX_PROJECT_EXPR}).by(${EDGE_PROJECT_EXPR})`);
@@ -5067,6 +5094,31 @@ function compileRepeatStep(step, nextParam, useEdgeEmission) {
5067
5094
  }
5068
5095
  return parts.join("");
5069
5096
  }
5097
+ var SAFE_PROJECTION_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
5098
+ function assertSafeProjectionKey(key) {
5099
+ if (!SAFE_PROJECTION_KEY_RE.test(key)) {
5100
+ throw new Error(
5101
+ `Unsafe projection property name: "${key}". Property names must match ${SAFE_PROJECTION_KEY_RE.source}.`
5102
+ );
5103
+ }
5104
+ }
5105
+ function emitProjectionTerminal(projection) {
5106
+ const properties = projection.properties;
5107
+ const mode = projection.mode ?? "values";
5108
+ const distinct = projection.distinct ?? false;
5109
+ for (const prop of properties) {
5110
+ assertSafeProjectionKey(prop);
5111
+ }
5112
+ const singleProperty = properties.length === 1;
5113
+ const keyExpr = singleProperty ? `values('${properties[0]}')` : `project(${properties.map((p) => `'${p}'`).join(",")})${properties.map((p) => `.by(values('${p}'))`).join("")}`;
5114
+ if (mode === "count") {
5115
+ return `.group().by(${keyExpr}).by(count()).unfold()`;
5116
+ }
5117
+ if (distinct) {
5118
+ return `.${keyExpr}.dedup()`;
5119
+ }
5120
+ return `.${keyExpr}`;
5121
+ }
5070
5122
  function compilePropertyFilter(filter, nextParam) {
5071
5123
  const key = nextParam(filter.key);
5072
5124
  switch (filter.operator) {
@@ -5176,17 +5228,35 @@ var CypherCompiler = class {
5176
5228
  }
5177
5229
  lastNode = targetNode;
5178
5230
  }
5179
- const matchClause = `MATCH ${matchParts[0]}${matchParts.slice(1).join("")}`;
5231
+ const bindPath = spec.returnMode === "path";
5232
+ const pathBinding = bindPath ? "p = " : "";
5233
+ const matchClause = `MATCH ${pathBinding}${matchParts[0]}${matchParts.slice(1).join("")}`;
5180
5234
  const whereClause = whereClauses.length > 0 ? `
5181
5235
  WHERE ${whereClauses.join(" AND ")}` : "";
5182
5236
  let returnClause;
5183
- if (spec.returnMode === "path") {
5184
- const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
5185
- const allRels = steps.map((_, i) => `r${i}`);
5186
- returnClause = `RETURN ${[...allNodes, ...allRels].join(", ")}`;
5237
+ const emitProjection = spec.projection !== void 0 && spec.returnMode !== "path" && spec.returnMode !== "all";
5238
+ if (emitProjection) {
5239
+ const projection = spec.projection;
5240
+ const mode = projection.mode ?? "values";
5241
+ const distinct = projection.distinct ?? false;
5242
+ const projectionColumns = projection.properties.map((prop) => {
5243
+ assertSafeProjectionKey2(prop);
5244
+ return `${lastNode}.${prop} AS ${prop}`;
5245
+ });
5246
+ if (mode === "count") {
5247
+ returnClause = `RETURN ${projectionColumns.join(", ")}, count(*) AS count`;
5248
+ } else if (distinct) {
5249
+ returnClause = `RETURN DISTINCT ${projectionColumns.join(", ")}`;
5250
+ } else {
5251
+ returnClause = `RETURN ${projectionColumns.join(", ")}`;
5252
+ }
5253
+ } else if (spec.returnMode === "path") {
5254
+ returnClause = "RETURN nodes(p) AS pathNodes, relationships(p) AS pathRels, length(p) AS pathLength";
5187
5255
  } else if (spec.returnMode === "all") {
5188
5256
  const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
5189
- returnClause = spec.dedup !== false ? `RETURN DISTINCT ${allNodes.join(", ")}` : `RETURN ${allNodes.join(", ")}`;
5257
+ const allRels = steps.map((_, i) => `r${i}`);
5258
+ const projection = [...allNodes, ...allRels].join(", ");
5259
+ returnClause = spec.dedup !== false ? `RETURN DISTINCT ${projection}` : `RETURN ${projection}`;
5190
5260
  } else {
5191
5261
  returnClause = spec.dedup !== false ? `RETURN DISTINCT ${lastNode}` : `RETURN ${lastNode}`;
5192
5262
  }
@@ -5207,6 +5277,14 @@ ${returnClause}${skipClause}${limitClause}`;
5207
5277
  };
5208
5278
  }
5209
5279
  };
5280
+ var SAFE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
5281
+ function assertSafeProjectionKey2(key) {
5282
+ if (!SAFE_KEY_RE.test(key)) {
5283
+ throw new Error(
5284
+ `Unsafe projection property name: "${key}". Property names must match ${SAFE_KEY_RE.source}.`
5285
+ );
5286
+ }
5287
+ }
5210
5288
  function compilePropertyFilterCypher(nodeAlias, filter, nextParam) {
5211
5289
  const prop = `${nodeAlias}.${filter.key}`;
5212
5290
  switch (filter.operator) {
@@ -5517,9 +5595,9 @@ var InMemoryStorageProvider = class {
5517
5595
  const isTarget = rel.targetEntityId === entityId;
5518
5596
  const isBidirectionalTarget = rel.bidirectional && isTarget;
5519
5597
  switch (direction) {
5520
- case "outbound":
5598
+ case "out":
5521
5599
  return isSource || isBidirectionalTarget;
5522
- case "inbound":
5600
+ case "in":
5523
5601
  return isTarget || rel.bidirectional && isSource;
5524
5602
  case "both":
5525
5603
  default:
@@ -5611,23 +5689,24 @@ var InMemoryStorageProvider = class {
5611
5689
  for (let depth = 0; depth < options.depth; depth++) {
5612
5690
  const layer = {};
5613
5691
  const nextFrontier = /* @__PURE__ */ new Set();
5692
+ const layerBucketSeen = /* @__PURE__ */ new Map();
5614
5693
  for (const frontierEntityId of currentFrontier) {
5615
5694
  for (const rel of store.relationships.values()) {
5616
5695
  const isSource = rel.sourceEntityId === frontierEntityId;
5617
5696
  const isTarget = rel.targetEntityId === frontierEntityId;
5618
5697
  let matchesDirection = false;
5619
5698
  let connectedEntityId;
5620
- if (isSource && (options.direction === "outbound" || options.direction === "both")) {
5699
+ if (isSource && (options.direction === "out" || options.direction === "both")) {
5621
5700
  matchesDirection = true;
5622
5701
  connectedEntityId = rel.targetEntityId;
5623
- } else if (isTarget && (options.direction === "inbound" || options.direction === "both")) {
5702
+ } else if (isTarget && (options.direction === "in" || options.direction === "both")) {
5624
5703
  matchesDirection = true;
5625
5704
  connectedEntityId = rel.sourceEntityId;
5626
5705
  } else if (rel.bidirectional) {
5627
- if (isSource && options.direction === "inbound") {
5706
+ if (isSource && options.direction === "in") {
5628
5707
  matchesDirection = true;
5629
5708
  connectedEntityId = rel.targetEntityId;
5630
- } else if (isTarget && options.direction === "outbound") {
5709
+ } else if (isTarget && options.direction === "out") {
5631
5710
  matchesDirection = true;
5632
5711
  connectedEntityId = rel.sourceEntityId;
5633
5712
  }
@@ -5648,6 +5727,13 @@ var InMemoryStorageProvider = class {
5648
5727
  continue;
5649
5728
  }
5650
5729
  const relType = rel.relationshipType;
5730
+ let bucketSeen = layerBucketSeen.get(relType);
5731
+ if (!bucketSeen) {
5732
+ bucketSeen = /* @__PURE__ */ new Set();
5733
+ layerBucketSeen.set(relType, bucketSeen);
5734
+ }
5735
+ if (bucketSeen.has(connectedEntityId)) continue;
5736
+ bucketSeen.add(connectedEntityId);
5651
5737
  if (!layer[relType]) {
5652
5738
  layer[relType] = { total: 0, entities: [], relationships: [] };
5653
5739
  }