@semiont/graph 0.2.30 → 0.2.31

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/README.md CHANGED
@@ -1,7 +1,10 @@
1
1
  # @semiont/graph
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@semiont/graph)](https://www.npmjs.com/package/@semiont/graph)
4
3
  [![Tests](https://github.com/The-AI-Alliance/semiont/actions/workflows/package-tests.yml/badge.svg)](https://github.com/The-AI-Alliance/semiont/actions/workflows/package-tests.yml?query=branch%3Amain+is%3Asuccess+job%3A%22Test+graph%22)
4
+ [![codecov](https://codecov.io/gh/The-AI-Alliance/semiont/graph/badge.svg?flag=graph)](https://codecov.io/gh/The-AI-Alliance/semiont?flag=graph)
5
+ [![npm version](https://img.shields.io/npm/v/@semiont/graph.svg)](https://www.npmjs.com/package/@semiont/graph)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@semiont/graph.svg)](https://www.npmjs.com/package/@semiont/graph)
7
+ [![License](https://img.shields.io/npm/l/@semiont/graph.svg)](https://github.com/The-AI-Alliance/semiont/blob/main/LICENSE)
5
8
 
6
9
  Graph database abstraction with Neo4j, Neptune, JanusGraph, and in-memory implementations.
7
10
 
@@ -84,6 +87,7 @@ const annotations = await graph.getAnnotationsForDocument('doc-123');
84
87
 
85
88
  - [API Reference](./docs/API.md) - Complete API documentation
86
89
  - [Architecture](./docs/ARCHITECTURE.md) - System design and principles
90
+ - [Eventual Consistency](./docs/EVENTUAL-CONSISTENCY.md) - Order-independent projections and race condition handling
87
91
  - [Provider Guide](./docs/PROVIDERS.md) - Provider-specific details
88
92
 
89
93
  ## Examples
package/dist/index.js CHANGED
@@ -880,19 +880,19 @@ var Neo4jGraphDatabase = class {
880
880
  throw new Error("Resource must have at least one representation");
881
881
  }
882
882
  const result = await session.run(
883
- `CREATE (d:Resource {
884
- id: $id,
885
- name: $name,
886
- entityTypes: $entityTypes,
887
- format: $format,
888
- archived: $archived,
889
- created: datetime($created),
890
- creator: $creator,
891
- creationMethod: $creationMethod,
892
- contentChecksum: $contentChecksum,
893
- sourceAnnotationId: $sourceAnnotationId,
894
- sourceResourceId: $sourceResourceId
895
- }) RETURN d`,
883
+ `MERGE (d:Resource {id: $id})
884
+ SET d.name = $name,
885
+ d.entityTypes = $entityTypes,
886
+ d.format = $format,
887
+ d.archived = $archived,
888
+ d.created = datetime($created),
889
+ d.creator = $creator,
890
+ d.creationMethod = $creationMethod,
891
+ d.contentChecksum = $contentChecksum,
892
+ d.sourceAnnotationId = $sourceAnnotationId,
893
+ d.sourceResourceId = $sourceResourceId,
894
+ d.stub = false
895
+ RETURN d`,
896
896
  {
897
897
  id,
898
898
  name: resource.name,
@@ -907,6 +907,7 @@ var Neo4jGraphDatabase = class {
907
907
  sourceResourceId: resource.sourceResourceId ?? null
908
908
  }
909
909
  );
910
+ console.log(`[Neo4j] Resource created/enriched: ${id}`);
910
911
  return this.parseResourceNode(result.records[0].get("d"));
911
912
  } finally {
912
913
  await session.close();
@@ -1189,18 +1190,24 @@ var Neo4jGraphDatabase = class {
1189
1190
  console.log(`[Neo4j] \u2705 Creating REFERENCES edge: ${id} -> ${specificResource.source}`);
1190
1191
  const refResult = await session.run(
1191
1192
  `MATCH (a:Annotation {id: $annotationId})
1192
- MATCH (target:Resource {id: $targetResourceId})
1193
+ MERGE (target:Resource {id: $targetResourceId})
1194
+ ON CREATE SET target.stub = true
1193
1195
  MERGE (a)-[:REFERENCES]->(target)
1194
- RETURN a, target`,
1196
+ RETURN a, target, target.stub AS wasStub`,
1195
1197
  {
1196
1198
  annotationId: id,
1197
1199
  targetResourceId: specificResource.source
1198
1200
  }
1199
1201
  );
1200
- console.log(`[Neo4j] \u2705 REFERENCES edge created! Matched ${refResult.records.length} nodes`);
1201
1202
  if (refResult.records.length > 0) {
1202
- console.log(`[Neo4j] Annotation: ${refResult.records[0].get("a").properties.id}`);
1203
- console.log(`[Neo4j] Target Resource: ${refResult.records[0].get("target").properties.id}`);
1203
+ const wasStub = refResult.records[0].get("wasStub");
1204
+ if (wasStub) {
1205
+ console.log(`[Neo4j] \u2705 REFERENCES edge created with stub node for ${specificResource.source} (will be enriched by resource.created event)`);
1206
+ } else {
1207
+ console.log(`[Neo4j] \u2705 REFERENCES edge created to existing resource ${specificResource.source}`);
1208
+ }
1209
+ } else {
1210
+ console.log(`[Neo4j] \u26A0\uFE0F REFERENCES edge creation returned no records`);
1204
1211
  }
1205
1212
  } else {
1206
1213
  console.log(`[Neo4j] No SpecificResource in body - this is a stub reference (not yet resolved)`);
@@ -2258,7 +2265,7 @@ var JanusGraphDatabase = class {
2258
2265
  // src/implementations/memorygraph.ts
2259
2266
  import { getResourceEntityTypes } from "@semiont/api-client";
2260
2267
  import { getEntityTypes as getEntityTypes4 } from "@semiont/ontology";
2261
- import { resourceId as makeResourceId } from "@semiont/core";
2268
+ import { resourceId as makeResourceId, uriToResourceId } from "@semiont/core";
2262
2269
  import { resourceUri as resourceUri2 } from "@semiont/api-client";
2263
2270
  import { v4 as uuidv44 } from "uuid";
2264
2271
  import { getBodySource as getBodySource4, getTargetSource as getTargetSource3 } from "@semiont/api-client";
@@ -2290,19 +2297,28 @@ var MemoryGraphDatabase = class {
2290
2297
  return resource;
2291
2298
  }
2292
2299
  async getResource(id) {
2293
- return this.resources.get(id) || null;
2300
+ try {
2301
+ const resourceId = uriToResourceId(id);
2302
+ return this.resources.get(resourceId) || null;
2303
+ } catch {
2304
+ return null;
2305
+ }
2294
2306
  }
2295
2307
  async updateResource(id, input) {
2296
2308
  if (Object.keys(input).length !== 1 || input.archived === void 0) {
2297
2309
  throw new Error("Resources are immutable. Only archiving is allowed.");
2298
2310
  }
2299
- const doc = this.resources.get(id);
2311
+ const resourceId = uriToResourceId(id);
2312
+ const doc = resourceId ? this.resources.get(resourceId) : null;
2300
2313
  if (!doc) throw new Error("Resource not found");
2301
2314
  doc.archived = input.archived;
2302
2315
  return doc;
2303
2316
  }
2304
2317
  async deleteResource(id) {
2305
- this.resources.delete(id);
2318
+ const resourceId = uriToResourceId(id);
2319
+ if (resourceId) {
2320
+ this.resources.delete(resourceId);
2321
+ }
2306
2322
  for (const [selId, sel] of this.annotations) {
2307
2323
  if (getTargetSource3(sel.target) === id || getBodySource4(sel.body) === id) {
2308
2324
  this.annotations.delete(selId);
@@ -2376,7 +2392,8 @@ var MemoryGraphDatabase = class {
2376
2392
  const resourceIdStr = String(filter.resourceId);
2377
2393
  results = results.filter((a) => {
2378
2394
  const targetSource = getTargetSource3(a.target);
2379
- return targetSource === resourceIdStr || targetSource === resourceUri2(resourceIdStr);
2395
+ const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
2396
+ return targetResourceId === resourceIdStr;
2380
2397
  });
2381
2398
  }
2382
2399
  if (filter.type) {
@@ -2389,7 +2406,8 @@ var MemoryGraphDatabase = class {
2389
2406
  const resourceIdStr = String(resourceId);
2390
2407
  const highlights = Array.from(this.annotations.values()).filter((sel) => {
2391
2408
  const targetSource = getTargetSource3(sel.target);
2392
- return (targetSource === resourceIdStr || targetSource === resourceUri2(resourceIdStr)) && sel.motivation === "highlighting";
2409
+ const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
2410
+ return targetResourceId === resourceIdStr && sel.motivation === "highlighting";
2393
2411
  });
2394
2412
  console.log(`Memory: getHighlights for ${resourceId} found ${highlights.length} highlights`);
2395
2413
  return highlights;
@@ -2397,11 +2415,13 @@ var MemoryGraphDatabase = class {
2397
2415
  async resolveReference(annotationId, source) {
2398
2416
  const annotation = this.annotations.get(annotationId);
2399
2417
  if (!annotation) throw new Error("Annotation not found");
2418
+ const sourceResource = this.resources.get(String(source));
2419
+ if (!sourceResource) throw new Error("Source resource not found");
2400
2420
  const updated = {
2401
2421
  ...annotation,
2402
2422
  body: {
2403
2423
  type: "SpecificResource",
2404
- source,
2424
+ source: sourceResource["@id"],
2405
2425
  purpose: "linking"
2406
2426
  }
2407
2427
  };
@@ -2412,7 +2432,8 @@ var MemoryGraphDatabase = class {
2412
2432
  const resourceIdStr = String(resourceId);
2413
2433
  const references = Array.from(this.annotations.values()).filter((sel) => {
2414
2434
  const targetSource = getTargetSource3(sel.target);
2415
- return (targetSource === resourceIdStr || targetSource === resourceUri2(resourceIdStr)) && sel.motivation === "linking";
2435
+ const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
2436
+ return targetResourceId === resourceIdStr && sel.motivation === "linking";
2416
2437
  });
2417
2438
  console.log(`Memory: getReferences for ${resourceId} found ${references.length} references`);
2418
2439
  references.forEach((ref) => {
@@ -2428,14 +2449,16 @@ var MemoryGraphDatabase = class {
2428
2449
  async getEntityReferences(resourceId, entityTypes) {
2429
2450
  const resourceIdStr = String(resourceId);
2430
2451
  let refs = Array.from(this.annotations.values()).filter((sel) => {
2431
- const selEntityTypes = getEntityTypes4(sel);
2432
2452
  const targetSource = getTargetSource3(sel.target);
2433
- return (targetSource === resourceIdStr || targetSource === resourceUri2(resourceIdStr)) && selEntityTypes.length > 0;
2453
+ const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
2454
+ const bodyEntityTypes = sel.body?.entityTypes;
2455
+ const hasEntityTypes = Array.isArray(bodyEntityTypes) && bodyEntityTypes.length > 0;
2456
+ return targetResourceId === resourceIdStr && hasEntityTypes;
2434
2457
  });
2435
2458
  if (entityTypes && entityTypes.length > 0) {
2436
2459
  refs = refs.filter((sel) => {
2437
- const selEntityTypes = getEntityTypes4(sel);
2438
- return selEntityTypes.some((type) => entityTypes.includes(type));
2460
+ const bodyEntityTypes = sel.body?.entityTypes || [];
2461
+ return bodyEntityTypes.some((type) => entityTypes.includes(type));
2439
2462
  });
2440
2463
  }
2441
2464
  return refs;
@@ -2444,7 +2467,8 @@ var MemoryGraphDatabase = class {
2444
2467
  const resourceIdStr = String(resourceId);
2445
2468
  return Array.from(this.annotations.values()).filter((sel) => {
2446
2469
  const targetSource = getTargetSource3(sel.target);
2447
- return targetSource === resourceIdStr || targetSource === resourceUri2(resourceIdStr);
2470
+ const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
2471
+ return targetResourceId === resourceIdStr;
2448
2472
  });
2449
2473
  }
2450
2474
  async getResourceReferencedBy(resourceUri3, _motivation) {
@@ -2458,34 +2482,41 @@ var MemoryGraphDatabase = class {
2458
2482
  if (bodySource) {
2459
2483
  const targetDoc = await this.getResource(resourceUri2(bodySource));
2460
2484
  if (targetDoc) {
2461
- const reverseRefs = await this.getReferences(makeResourceId(bodySource));
2462
- const resourceIdStr = String(resourceId);
2463
- const bidirectional = reverseRefs.some((r) => {
2464
- const reverseBodySource = getBodySource4(r.body);
2465
- return reverseBodySource === resourceIdStr || reverseBodySource === resourceUri2(resourceIdStr);
2466
- });
2467
- connections.push({
2468
- targetResource: targetDoc,
2469
- annotations: [ref],
2470
- bidirectional
2471
- });
2485
+ const bodySourceId = uriToResourceId(bodySource);
2486
+ if (bodySourceId) {
2487
+ const reverseRefs = await this.getReferences(makeResourceId(bodySourceId));
2488
+ const resourceIdStr = String(resourceId);
2489
+ const bidirectional = reverseRefs.some((r) => {
2490
+ const reverseBodySource = getBodySource4(r.body);
2491
+ const reverseBodyResourceId = reverseBodySource ? uriToResourceId(reverseBodySource) : null;
2492
+ return reverseBodyResourceId === resourceIdStr;
2493
+ });
2494
+ connections.push({
2495
+ targetResource: targetDoc,
2496
+ annotations: [ref],
2497
+ bidirectional
2498
+ });
2499
+ }
2472
2500
  }
2473
2501
  }
2474
2502
  }
2475
2503
  return connections;
2476
2504
  }
2477
2505
  async findPath(fromResourceId, toResourceId, maxDepth = 5) {
2506
+ const fromUuid = uriToResourceId(fromResourceId);
2507
+ const toUuid = uriToResourceId(toResourceId);
2508
+ if (!fromUuid || !toUuid) return [];
2478
2509
  const visited = /* @__PURE__ */ new Set();
2479
2510
  const queue = [];
2480
2511
  const fromDoc = await this.getResource(resourceUri2(fromResourceId));
2481
2512
  if (!fromDoc) return [];
2482
- queue.push({ docId: fromResourceId, path: [fromDoc], sels: [] });
2483
- visited.add(fromResourceId);
2513
+ queue.push({ docId: fromUuid, path: [fromDoc], sels: [] });
2514
+ visited.add(fromUuid);
2484
2515
  const paths = [];
2485
2516
  while (queue.length > 0 && paths.length < 10) {
2486
2517
  const { docId, path, sels } = queue.shift();
2487
2518
  if (path.length > maxDepth) continue;
2488
- if (docId === toResourceId) {
2519
+ if (docId === toUuid) {
2489
2520
  paths.push({ resources: path, annotations: sels });
2490
2521
  continue;
2491
2522
  }
@@ -2532,7 +2563,10 @@ var MemoryGraphDatabase = class {
2532
2563
  const annotations = Array.from(this.annotations.values());
2533
2564
  const highlightCount = annotations.filter((a) => a.motivation === "highlighting").length;
2534
2565
  const referenceCount = annotations.filter((a) => a.motivation === "linking").length;
2535
- const entityReferenceCount = annotations.filter((a) => a.motivation === "linking" && getEntityTypes4(a).length > 0).length;
2566
+ const entityReferenceCount = annotations.filter((a) => {
2567
+ const bodyEntityTypes = a.body?.entityTypes;
2568
+ return a.motivation === "linking" && Array.isArray(bodyEntityTypes) && bodyEntityTypes.length > 0;
2569
+ }).length;
2536
2570
  return {
2537
2571
  resourceCount: this.resources.size,
2538
2572
  annotationCount: this.annotations.size,