@utaba/deep-memory 0.1.0 → 0.2.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/{StorageProvider-CkFjdboX.d.cts → StorageProvider-C3tKhfuw.d.cts} +14 -3
- package/dist/{StorageProvider-CJjz8uBY.d.ts → StorageProvider-DR9HLYHJ.d.ts} +14 -3
- package/dist/index.cjs +960 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -13
- package/dist/index.d.ts +65 -13
- package/dist/index.js +954 -143
- package/dist/index.js.map +1 -1
- package/dist/{portability-DdlNYXGX.d.cts → portability-DdjsCB07.d.cts} +44 -1
- package/dist/{portability-DdlNYXGX.d.ts → portability-DdjsCB07.d.ts} +44 -1
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.d.cts +56 -3
- package/dist/providers/index.d.ts +56 -3
- package/dist/testing/conformance.d.cts +2 -2
- package/dist/testing/conformance.d.ts +2 -2
- package/dist/traversal-9JdiqjZ9.d.ts +177 -0
- package/dist/traversal-BB_yERbf.d.cts +177 -0
- package/dist/types/index.cjs.map +1 -1
- package/dist/types/index.d.cts +3 -2
- package/dist/types/index.d.ts +3 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
CypherCompiler: () => CypherCompiler,
|
|
23
24
|
DeepMemory: () => DeepMemory,
|
|
24
25
|
DeepMemoryError: () => DeepMemoryError,
|
|
25
26
|
DuplicateEntityError: () => DuplicateEntityError,
|
|
@@ -29,6 +30,8 @@ __export(src_exports, {
|
|
|
29
30
|
EntityNotFoundError: () => EntityNotFoundError,
|
|
30
31
|
ExportError: () => ExportError,
|
|
31
32
|
GovernanceDeniedError: () => GovernanceDeniedError,
|
|
33
|
+
GraphTraversalProviderRequiredError: () => GraphTraversalProviderRequiredError,
|
|
34
|
+
GremlinCompiler: () => GremlinCompiler,
|
|
32
35
|
ImportError: () => ImportError,
|
|
33
36
|
InMemorySearchProvider: () => InMemorySearchProvider,
|
|
34
37
|
InMemoryStorageProvider: () => InMemoryStorageProvider,
|
|
@@ -40,6 +43,9 @@ __export(src_exports, {
|
|
|
40
43
|
RelationshipConstraintError: () => RelationshipConstraintError,
|
|
41
44
|
RelationshipNotFoundError: () => RelationshipNotFoundError,
|
|
42
45
|
RepositoryNotFoundError: () => RepositoryNotFoundError,
|
|
46
|
+
TraversalTimeoutError: () => TraversalTimeoutError,
|
|
47
|
+
TraversalValidationError: () => TraversalValidationError,
|
|
48
|
+
TraversalVocabularyError: () => TraversalVocabularyError,
|
|
43
49
|
VocabularyValidationError: () => VocabularyValidationError,
|
|
44
50
|
generateId: () => generateId,
|
|
45
51
|
isValidUuid: () => isValidUuid,
|
|
@@ -240,6 +246,52 @@ var ProviderError = class extends DeepMemoryError {
|
|
|
240
246
|
this.name = "ProviderError";
|
|
241
247
|
}
|
|
242
248
|
};
|
|
249
|
+
var GraphTraversalProviderRequiredError = class extends DeepMemoryError {
|
|
250
|
+
constructor() {
|
|
251
|
+
super(
|
|
252
|
+
"GRAPH_TRAVERSAL_PROVIDER_REQUIRED",
|
|
253
|
+
"GraphTraversalProvider required: no graph traversal provider is configured",
|
|
254
|
+
`Provide a GraphTraversalProvider in the DeepMemory config to use native graph queries. Structured traversals via traverse() work without a provider using fallback BFS.`
|
|
255
|
+
);
|
|
256
|
+
this.name = "GraphTraversalProviderRequiredError";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
var TraversalValidationError = class extends DeepMemoryError {
|
|
260
|
+
errors;
|
|
261
|
+
constructor(errors) {
|
|
262
|
+
super(
|
|
263
|
+
"TRAVERSAL_VALIDATION_FAILED",
|
|
264
|
+
`Traversal validation failed: ${errors.join("; ")}`,
|
|
265
|
+
`Check the TraversalSpec structure. Each spec needs a start (entityId, entityType, or filter), at least one step, and a returnMode.`
|
|
266
|
+
);
|
|
267
|
+
this.name = "TraversalValidationError";
|
|
268
|
+
this.errors = errors;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
var TraversalVocabularyError = class extends DeepMemoryError {
|
|
272
|
+
unknownTypes;
|
|
273
|
+
constructor(unknownTypes) {
|
|
274
|
+
super(
|
|
275
|
+
"TRAVERSAL_VOCABULARY_ERROR",
|
|
276
|
+
`Traversal references unknown types: ${unknownTypes.join(", ")}`,
|
|
277
|
+
`Use getVocabulary() to see valid entity and relationship types for this repository.`
|
|
278
|
+
);
|
|
279
|
+
this.name = "TraversalVocabularyError";
|
|
280
|
+
this.unknownTypes = unknownTypes;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
var TraversalTimeoutError = class extends DeepMemoryError {
|
|
284
|
+
timeoutMs;
|
|
285
|
+
constructor(timeoutMs) {
|
|
286
|
+
super(
|
|
287
|
+
"TRAVERSAL_TIMEOUT",
|
|
288
|
+
`Traversal timed out after ${timeoutMs}ms`,
|
|
289
|
+
`Try reducing the traversal depth, adding more specific filters, or increasing the timeout on your GraphTraversalProvider.`
|
|
290
|
+
);
|
|
291
|
+
this.name = "TraversalTimeoutError";
|
|
292
|
+
this.timeoutMs = timeoutMs;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
243
295
|
|
|
244
296
|
// src/entities/IdGenerator.ts
|
|
245
297
|
function slugify(text) {
|
|
@@ -280,50 +332,54 @@ var EntityManager = class {
|
|
|
280
332
|
this.storage = storage;
|
|
281
333
|
this.embedding = embedding;
|
|
282
334
|
}
|
|
283
|
-
/** Create
|
|
284
|
-
async create(
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (hookResult.cancelled) {
|
|
298
|
-
throw new OperationCancelledError("Entity creation", hookResult.reason ?? "cancelled by hook");
|
|
299
|
-
}
|
|
300
|
-
const id = input.id ?? generateEntityId();
|
|
301
|
-
const slug = await generateUniqueSlug(
|
|
302
|
-
input.entityType,
|
|
303
|
-
input.label,
|
|
304
|
-
async (candidateSlug) => {
|
|
305
|
-
const existing = await this.storage.getEntityBySlug(this.repositoryId, candidateSlug);
|
|
306
|
-
return existing !== null;
|
|
335
|
+
/** Create one or more entities with vocabulary validation, ID generation, provenance, and events */
|
|
336
|
+
async create(inputs) {
|
|
337
|
+
const results = [];
|
|
338
|
+
for (const input of inputs) {
|
|
339
|
+
const validation = await this.vocabularyEngine.validateEntity(input);
|
|
340
|
+
if (!validation.valid) {
|
|
341
|
+
const errorMsg = validation.errors.map((e) => e.message).join("; ");
|
|
342
|
+
const suggestions = validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion);
|
|
343
|
+
await this.eventBus.emit("validation:failed", {
|
|
344
|
+
operation: "createEntity",
|
|
345
|
+
error: errorMsg,
|
|
346
|
+
suggestions
|
|
347
|
+
});
|
|
348
|
+
throw new VocabularyValidationError(validation.errors);
|
|
307
349
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
id
|
|
313
|
-
slug
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
350
|
+
const hookResult = await this.eventBus.emitHook("entity:creating", { input });
|
|
351
|
+
if (hookResult.cancelled) {
|
|
352
|
+
throw new OperationCancelledError("Entity creation", hookResult.reason ?? "cancelled by hook");
|
|
353
|
+
}
|
|
354
|
+
const id = input.id ?? generateEntityId();
|
|
355
|
+
const slug = await generateUniqueSlug(
|
|
356
|
+
input.entityType,
|
|
357
|
+
input.label,
|
|
358
|
+
async (candidateSlug) => {
|
|
359
|
+
const existing = await this.storage.getEntityBySlug(this.repositoryId, candidateSlug);
|
|
360
|
+
return existing !== null;
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
const provenance = this.provenanceTracker.stampCreate();
|
|
364
|
+
const entityEmbedding = await this.generateEmbedding(input.label, input.summary);
|
|
365
|
+
const storedEntity = {
|
|
366
|
+
id,
|
|
367
|
+
slug,
|
|
368
|
+
entityType: input.entityType,
|
|
369
|
+
label: input.label,
|
|
370
|
+
summary: input.summary,
|
|
371
|
+
properties: input.properties ?? {},
|
|
372
|
+
data: input.data,
|
|
373
|
+
dataFormat: input.dataFormat,
|
|
374
|
+
provenance,
|
|
375
|
+
embedding: entityEmbedding
|
|
376
|
+
};
|
|
377
|
+
const created = await this.storage.createEntity(this.repositoryId, storedEntity);
|
|
378
|
+
const entity = storedToEntity(created);
|
|
379
|
+
await this.eventBus.emit("entity:created", { entity });
|
|
380
|
+
results.push(entity);
|
|
381
|
+
}
|
|
382
|
+
return results;
|
|
327
383
|
}
|
|
328
384
|
/** Update an existing entity */
|
|
329
385
|
async update(entityId, updates) {
|
|
@@ -655,55 +711,59 @@ var RelationshipManager = class {
|
|
|
655
711
|
this.eventBus = eventBus;
|
|
656
712
|
this.storage = storage;
|
|
657
713
|
}
|
|
658
|
-
/** Create
|
|
659
|
-
async create(
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
await this.eventBus.emit("validation:failed", {
|
|
675
|
-
operation: "createRelationship",
|
|
676
|
-
error: validation.errors.map((e) => e.message).join("; "),
|
|
677
|
-
suggestions: validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion)
|
|
678
|
-
});
|
|
679
|
-
throw new VocabularyValidationError(validation.errors);
|
|
680
|
-
}
|
|
681
|
-
const hookResult = await this.eventBus.emitHook("relationship:creating", { input });
|
|
682
|
-
if (hookResult.cancelled) {
|
|
683
|
-
throw new OperationCancelledError(
|
|
684
|
-
"Relationship creation",
|
|
685
|
-
hookResult.reason ?? "cancelled by hook"
|
|
714
|
+
/** Create one or more relationships with vocabulary validation, provenance, and events */
|
|
715
|
+
async create(inputs) {
|
|
716
|
+
const results = [];
|
|
717
|
+
for (const input of inputs) {
|
|
718
|
+
const sourceEntity = await this.storage.getEntity(this.repositoryId, input.sourceEntityId);
|
|
719
|
+
if (!sourceEntity) {
|
|
720
|
+
throw new EntityNotFoundError(input.sourceEntityId);
|
|
721
|
+
}
|
|
722
|
+
const targetEntity = await this.storage.getEntity(this.repositoryId, input.targetEntityId);
|
|
723
|
+
if (!targetEntity) {
|
|
724
|
+
throw new EntityNotFoundError(input.targetEntityId);
|
|
725
|
+
}
|
|
726
|
+
const validation = await this.vocabularyEngine.validateRelationship(
|
|
727
|
+
input,
|
|
728
|
+
sourceEntity.entityType,
|
|
729
|
+
targetEntity.entityType
|
|
686
730
|
);
|
|
731
|
+
if (!validation.valid) {
|
|
732
|
+
await this.eventBus.emit("validation:failed", {
|
|
733
|
+
operation: "createRelationship",
|
|
734
|
+
error: validation.errors.map((e) => e.message).join("; "),
|
|
735
|
+
suggestions: validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion)
|
|
736
|
+
});
|
|
737
|
+
throw new VocabularyValidationError(validation.errors);
|
|
738
|
+
}
|
|
739
|
+
const hookResult = await this.eventBus.emitHook("relationship:creating", { input });
|
|
740
|
+
if (hookResult.cancelled) {
|
|
741
|
+
throw new OperationCancelledError(
|
|
742
|
+
"Relationship creation",
|
|
743
|
+
hookResult.reason ?? "cancelled by hook"
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
const normalizedType = toScreamingSnakeCase(input.relationshipType);
|
|
747
|
+
const vocabulary = await this.vocabularyEngine.getVocabulary();
|
|
748
|
+
const relType = vocabulary.relationshipTypes.find((rt) => rt.type === normalizedType);
|
|
749
|
+
const bidirectional = relType?.bidirectional ?? false;
|
|
750
|
+
const id = input.id ?? generateRelationshipId();
|
|
751
|
+
const provenance = this.provenanceTracker.stampCreate();
|
|
752
|
+
const storedRelationship = {
|
|
753
|
+
id,
|
|
754
|
+
relationshipType: normalizedType,
|
|
755
|
+
sourceEntityId: input.sourceEntityId,
|
|
756
|
+
targetEntityId: input.targetEntityId,
|
|
757
|
+
properties: input.properties ?? {},
|
|
758
|
+
bidirectional,
|
|
759
|
+
provenance
|
|
760
|
+
};
|
|
761
|
+
const created = await this.storage.createRelationship(this.repositoryId, storedRelationship);
|
|
762
|
+
const relationship = storedToRelationship(created);
|
|
763
|
+
await this.eventBus.emit("relationship:created", { relationship });
|
|
764
|
+
results.push(relationship);
|
|
687
765
|
}
|
|
688
|
-
|
|
689
|
-
const vocabulary = await this.vocabularyEngine.getVocabulary();
|
|
690
|
-
const relType = vocabulary.relationshipTypes.find((rt) => rt.type === normalizedType);
|
|
691
|
-
const bidirectional = relType?.bidirectional ?? false;
|
|
692
|
-
const id = input.id ?? generateRelationshipId();
|
|
693
|
-
const provenance = this.provenanceTracker.stampCreate();
|
|
694
|
-
const storedRelationship = {
|
|
695
|
-
id,
|
|
696
|
-
relationshipType: normalizedType,
|
|
697
|
-
sourceEntityId: input.sourceEntityId,
|
|
698
|
-
targetEntityId: input.targetEntityId,
|
|
699
|
-
properties: input.properties ?? {},
|
|
700
|
-
bidirectional,
|
|
701
|
-
provenance
|
|
702
|
-
};
|
|
703
|
-
const created = await this.storage.createRelationship(this.repositoryId, storedRelationship);
|
|
704
|
-
const relationship = storedToRelationship(created);
|
|
705
|
-
await this.eventBus.emit("relationship:created", { relationship });
|
|
706
|
-
return relationship;
|
|
766
|
+
return results;
|
|
707
767
|
}
|
|
708
768
|
/** Remove a relationship by ID */
|
|
709
769
|
async remove(relationshipId) {
|
|
@@ -784,11 +844,706 @@ function projectEntity(stored, level) {
|
|
|
784
844
|
}
|
|
785
845
|
}
|
|
786
846
|
|
|
847
|
+
// src/relationships/TraversalValidator.ts
|
|
848
|
+
var DEFAULT_MAX_STEPS = 6;
|
|
849
|
+
var DEFAULT_MAX_LIMIT = 200;
|
|
850
|
+
var DEFAULT_FALLBACK_MAX_DEPTH = 10;
|
|
851
|
+
function validateTraversalSpec(spec, vocabulary, capabilities) {
|
|
852
|
+
const errors = [];
|
|
853
|
+
if (!spec.start) {
|
|
854
|
+
errors.push("start is required");
|
|
855
|
+
} else {
|
|
856
|
+
const hasEntityId = spec.start.entityId !== void 0 && spec.start.entityId !== "";
|
|
857
|
+
const hasEntityType = spec.start.entityType !== void 0 && spec.start.entityType !== "";
|
|
858
|
+
const hasFilter = spec.start.filter !== void 0 && spec.start.filter.length > 0;
|
|
859
|
+
if (!hasEntityId && !hasEntityType && !hasFilter) {
|
|
860
|
+
errors.push("start must have at least one of entityId, entityType, or filter");
|
|
861
|
+
}
|
|
862
|
+
if (hasEntityType && !hasEntityId && (spec.limit === void 0 || spec.limit === 0)) {
|
|
863
|
+
errors.push("start.entityType without entityId requires limit on the spec to prevent full type scans");
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
if (!spec.steps || spec.steps.length === 0) {
|
|
867
|
+
errors.push("steps must contain at least one step");
|
|
868
|
+
}
|
|
869
|
+
const maxSteps = capabilities?.maxTraversalDepth ?? DEFAULT_MAX_STEPS;
|
|
870
|
+
if (spec.steps && spec.steps.length > maxSteps) {
|
|
871
|
+
errors.push(`steps length ${spec.steps.length} exceeds maximum ${maxSteps}`);
|
|
872
|
+
}
|
|
873
|
+
if (spec.steps) {
|
|
874
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
875
|
+
const step = spec.steps[i];
|
|
876
|
+
if (!step.direction || !["out", "in", "both"].includes(step.direction)) {
|
|
877
|
+
errors.push(`steps[${i}].direction must be 'out', 'in', or 'both'`);
|
|
878
|
+
}
|
|
879
|
+
if (step.repeat) {
|
|
880
|
+
if (step.repeat.maxDepth === void 0 || step.repeat.maxDepth <= 0) {
|
|
881
|
+
errors.push(`steps[${i}].repeat.maxDepth must be a positive number`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const maxProviderDepth = capabilities?.maxTraversalDepth ?? DEFAULT_FALLBACK_MAX_DEPTH;
|
|
886
|
+
let totalDepth = 0;
|
|
887
|
+
for (const step of spec.steps) {
|
|
888
|
+
totalDepth += step.repeat ? step.repeat.maxDepth : 1;
|
|
889
|
+
}
|
|
890
|
+
if (totalDepth > maxProviderDepth) {
|
|
891
|
+
errors.push(`total potential depth ${totalDepth} exceeds provider maximum ${maxProviderDepth}`);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (spec.returnMode && !["terminal", "path", "all"].includes(spec.returnMode)) {
|
|
895
|
+
errors.push(`returnMode must be 'terminal', 'path', or 'all'`);
|
|
896
|
+
}
|
|
897
|
+
if (spec.limit !== void 0) {
|
|
898
|
+
if (spec.limit < 1 || spec.limit > DEFAULT_MAX_LIMIT) {
|
|
899
|
+
errors.push(`limit must be between 1 and ${DEFAULT_MAX_LIMIT}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (spec.offset !== void 0 && spec.offset < 0) {
|
|
903
|
+
errors.push("offset must be >= 0");
|
|
904
|
+
}
|
|
905
|
+
if (vocabulary && spec.steps) {
|
|
906
|
+
const entityTypeSet = new Set(vocabulary.entityTypes.map((et) => et.type));
|
|
907
|
+
const relationshipTypeSet = new Set(vocabulary.relationshipTypes.map((rt) => rt.type));
|
|
908
|
+
const unknownTypes = [];
|
|
909
|
+
if (spec.start?.entityType && !entityTypeSet.has(spec.start.entityType)) {
|
|
910
|
+
unknownTypes.push(`entity type "${spec.start.entityType}"`);
|
|
911
|
+
}
|
|
912
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
913
|
+
const step = spec.steps[i];
|
|
914
|
+
if (step.relationshipTypes) {
|
|
915
|
+
for (const rt of step.relationshipTypes) {
|
|
916
|
+
if (!relationshipTypeSet.has(rt)) {
|
|
917
|
+
unknownTypes.push(`relationship type "${rt}" in steps[${i}]`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
if (step.entityTypes) {
|
|
922
|
+
for (const et of step.entityTypes) {
|
|
923
|
+
if (!entityTypeSet.has(et)) {
|
|
924
|
+
unknownTypes.push(`entity type "${et}" in steps[${i}]`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (unknownTypes.length > 0) {
|
|
930
|
+
errors.push(`Unknown vocabulary types: ${unknownTypes.join(", ")}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return {
|
|
934
|
+
valid: errors.length === 0,
|
|
935
|
+
errors
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// src/relationships/compilers/GremlinCompiler.ts
|
|
940
|
+
var DEFAULT_ESTIMATED_FANOUT_PER_HOP = 10;
|
|
941
|
+
var GremlinCompiler = class {
|
|
942
|
+
language = "gremlin";
|
|
943
|
+
compile(spec, _vocabulary) {
|
|
944
|
+
const parts = [];
|
|
945
|
+
const params = {};
|
|
946
|
+
let paramIndex = 0;
|
|
947
|
+
let estimatedFanOut = 1;
|
|
948
|
+
const nextParam = (value) => {
|
|
949
|
+
const name = `p${paramIndex++}`;
|
|
950
|
+
params[name] = value;
|
|
951
|
+
return name;
|
|
952
|
+
};
|
|
953
|
+
parts.push("g.V()");
|
|
954
|
+
if (spec.start.entityId) {
|
|
955
|
+
const p = nextParam(spec.start.entityId);
|
|
956
|
+
parts.push(`.has('id', ${p})`);
|
|
957
|
+
} else if (spec.start.entityType) {
|
|
958
|
+
const p = nextParam(spec.start.entityType);
|
|
959
|
+
parts.push(`.has('entityType', ${p})`);
|
|
960
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
961
|
+
}
|
|
962
|
+
if (spec.start.filter) {
|
|
963
|
+
for (const f of spec.start.filter) {
|
|
964
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
for (const step of spec.steps) {
|
|
968
|
+
if (step.repeat) {
|
|
969
|
+
parts.push(compileRepeatStep(step, nextParam));
|
|
970
|
+
estimatedFanOut *= step.repeat.maxDepth * DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
971
|
+
} else if (step.relationshipFilter && step.relationshipFilter.length > 0) {
|
|
972
|
+
parts.push(compileEdgeStep(step, nextParam));
|
|
973
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
974
|
+
} else {
|
|
975
|
+
parts.push(compileSimpleStep(step, nextParam));
|
|
976
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
977
|
+
}
|
|
978
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
979
|
+
const typeParams = step.entityTypes.map((t) => nextParam(t));
|
|
980
|
+
parts.push(`.has('entityType', within(${typeParams.join(", ")}))`);
|
|
981
|
+
}
|
|
982
|
+
if (step.entityFilter) {
|
|
983
|
+
for (const f of step.entityFilter) {
|
|
984
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (spec.returnMode === "all") {
|
|
989
|
+
}
|
|
990
|
+
if (spec.dedup !== false) {
|
|
991
|
+
parts.push(".dedup()");
|
|
992
|
+
}
|
|
993
|
+
const limit = spec.limit ?? 50;
|
|
994
|
+
const offset = spec.offset ?? 0;
|
|
995
|
+
const pOffset = nextParam(offset);
|
|
996
|
+
const pEnd = nextParam(offset + limit);
|
|
997
|
+
parts.push(`.range(${pOffset}, ${pEnd})`);
|
|
998
|
+
params["_limit"] = limit;
|
|
999
|
+
params["_offset"] = offset;
|
|
1000
|
+
if (spec.returnMode === "path") {
|
|
1001
|
+
parts.push(".path()");
|
|
1002
|
+
}
|
|
1003
|
+
if (spec.returnMode !== "path") {
|
|
1004
|
+
parts.push(".valueMap(true)");
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
query: parts.join(""),
|
|
1008
|
+
params,
|
|
1009
|
+
estimatedFanOut: Math.min(estimatedFanOut, 1e4)
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
function compileSimpleStep(step, nextParam) {
|
|
1014
|
+
const types = step.relationshipTypes;
|
|
1015
|
+
const typeArgs = types ? types.map((t) => nextParam(t)).join(", ") : "";
|
|
1016
|
+
switch (step.direction) {
|
|
1017
|
+
case "out":
|
|
1018
|
+
return types ? `.out(${typeArgs})` : ".out()";
|
|
1019
|
+
case "in":
|
|
1020
|
+
return types ? `.in(${typeArgs})` : ".in()";
|
|
1021
|
+
case "both":
|
|
1022
|
+
return types ? `.both(${typeArgs})` : ".both()";
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function compileEdgeStep(step, nextParam) {
|
|
1026
|
+
const parts = [];
|
|
1027
|
+
const types = step.relationshipTypes;
|
|
1028
|
+
const typeArgs = types ? types.map((t) => nextParam(t)).join(", ") : "";
|
|
1029
|
+
switch (step.direction) {
|
|
1030
|
+
case "out":
|
|
1031
|
+
parts.push(types ? `.outE(${typeArgs})` : ".outE()");
|
|
1032
|
+
break;
|
|
1033
|
+
case "in":
|
|
1034
|
+
parts.push(types ? `.inE(${typeArgs})` : ".inE()");
|
|
1035
|
+
break;
|
|
1036
|
+
case "both":
|
|
1037
|
+
parts.push(types ? `.bothE(${typeArgs})` : ".bothE()");
|
|
1038
|
+
break;
|
|
1039
|
+
}
|
|
1040
|
+
if (step.relationshipFilter) {
|
|
1041
|
+
for (const f of step.relationshipFilter) {
|
|
1042
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
switch (step.direction) {
|
|
1046
|
+
case "out":
|
|
1047
|
+
parts.push(".inV()");
|
|
1048
|
+
break;
|
|
1049
|
+
case "in":
|
|
1050
|
+
parts.push(".outV()");
|
|
1051
|
+
break;
|
|
1052
|
+
case "both":
|
|
1053
|
+
parts.push(".otherV()");
|
|
1054
|
+
break;
|
|
1055
|
+
}
|
|
1056
|
+
return parts.join("");
|
|
1057
|
+
}
|
|
1058
|
+
function compileRepeatStep(step, nextParam) {
|
|
1059
|
+
const parts = [];
|
|
1060
|
+
const innerStep = step.relationshipFilter?.length ? compileEdgeStep({ ...step, repeat: void 0 }, nextParam) : compileSimpleStep(step, nextParam);
|
|
1061
|
+
if (step.repeat?.emitIntermediates !== false) {
|
|
1062
|
+
parts.push(".emit()");
|
|
1063
|
+
}
|
|
1064
|
+
parts.push(`.repeat(${innerStep.startsWith(".") ? `__${innerStep}` : innerStep})`);
|
|
1065
|
+
if (step.repeat?.until && step.repeat.until.length > 0) {
|
|
1066
|
+
const untilParts = step.repeat.until.map((f) => compilePropertyFilter(f, nextParam));
|
|
1067
|
+
parts.push(`.until(${untilParts.join("")})`);
|
|
1068
|
+
}
|
|
1069
|
+
if (step.repeat?.maxDepth) {
|
|
1070
|
+
const p = nextParam(step.repeat.maxDepth);
|
|
1071
|
+
parts.push(`.times(${p})`);
|
|
1072
|
+
}
|
|
1073
|
+
if (step.repeat?.emitIntermediates === false) {
|
|
1074
|
+
parts.push(".emit()");
|
|
1075
|
+
}
|
|
1076
|
+
return parts.join("");
|
|
1077
|
+
}
|
|
1078
|
+
function compilePropertyFilter(filter, nextParam) {
|
|
1079
|
+
const key = nextParam(filter.key);
|
|
1080
|
+
switch (filter.operator) {
|
|
1081
|
+
case "eq": {
|
|
1082
|
+
const val = nextParam(filter.value);
|
|
1083
|
+
return `.has(${key}, ${val})`;
|
|
1084
|
+
}
|
|
1085
|
+
case "neq": {
|
|
1086
|
+
const val = nextParam(filter.value);
|
|
1087
|
+
return `.has(${key}, neq(${val}))`;
|
|
1088
|
+
}
|
|
1089
|
+
case "gt": {
|
|
1090
|
+
const val = nextParam(filter.value);
|
|
1091
|
+
return `.has(${key}, gt(${val}))`;
|
|
1092
|
+
}
|
|
1093
|
+
case "gte": {
|
|
1094
|
+
const val = nextParam(filter.value);
|
|
1095
|
+
return `.has(${key}, gte(${val}))`;
|
|
1096
|
+
}
|
|
1097
|
+
case "lt": {
|
|
1098
|
+
const val = nextParam(filter.value);
|
|
1099
|
+
return `.has(${key}, lt(${val}))`;
|
|
1100
|
+
}
|
|
1101
|
+
case "lte": {
|
|
1102
|
+
const val = nextParam(filter.value);
|
|
1103
|
+
return `.has(${key}, lte(${val}))`;
|
|
1104
|
+
}
|
|
1105
|
+
case "contains": {
|
|
1106
|
+
const val = nextParam(filter.value);
|
|
1107
|
+
return `.has(${key}, containing(${val}))`;
|
|
1108
|
+
}
|
|
1109
|
+
case "isNull":
|
|
1110
|
+
return `.hasNot(${key})`;
|
|
1111
|
+
case "isNotNull":
|
|
1112
|
+
return `.has(${key})`;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/relationships/compilers/CypherCompiler.ts
|
|
1117
|
+
var DEFAULT_ESTIMATED_FANOUT_PER_HOP2 = 10;
|
|
1118
|
+
var CypherCompiler = class {
|
|
1119
|
+
language = "cypher";
|
|
1120
|
+
compile(spec, _vocabulary) {
|
|
1121
|
+
const params = {};
|
|
1122
|
+
let paramIndex = 0;
|
|
1123
|
+
let estimatedFanOut = 1;
|
|
1124
|
+
const whereClauses = [];
|
|
1125
|
+
let nodeIndex = 0;
|
|
1126
|
+
const nextParam = (value) => {
|
|
1127
|
+
const name = `p${paramIndex++}`;
|
|
1128
|
+
params[name] = value;
|
|
1129
|
+
return `$${name}`;
|
|
1130
|
+
};
|
|
1131
|
+
const startNode = `n${nodeIndex++}`;
|
|
1132
|
+
let matchParts = [];
|
|
1133
|
+
if (spec.start.entityId) {
|
|
1134
|
+
matchParts.push(`(${startNode})`);
|
|
1135
|
+
whereClauses.push(`${startNode}.id = ${nextParam(spec.start.entityId)}`);
|
|
1136
|
+
} else if (spec.start.entityType) {
|
|
1137
|
+
matchParts.push(`(${startNode})`);
|
|
1138
|
+
whereClauses.push(`${startNode}.entityType = ${nextParam(spec.start.entityType)}`);
|
|
1139
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP2;
|
|
1140
|
+
} else {
|
|
1141
|
+
matchParts.push(`(${startNode})`);
|
|
1142
|
+
}
|
|
1143
|
+
if (spec.start.filter) {
|
|
1144
|
+
for (const f of spec.start.filter) {
|
|
1145
|
+
whereClauses.push(compilePropertyFilterCypher(startNode, f, nextParam));
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
let lastNode = startNode;
|
|
1149
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
1150
|
+
const step = spec.steps[i];
|
|
1151
|
+
const targetNode = `n${nodeIndex++}`;
|
|
1152
|
+
const relAlias = `r${i}`;
|
|
1153
|
+
const relTypes = step.relationshipTypes?.length ? step.relationshipTypes.join("|") : "";
|
|
1154
|
+
const relTypePattern = relTypes ? `:${relTypes}` : "";
|
|
1155
|
+
const depthPattern = step.repeat ? `*1..${step.repeat.maxDepth}` : "";
|
|
1156
|
+
let pattern;
|
|
1157
|
+
switch (step.direction) {
|
|
1158
|
+
case "out":
|
|
1159
|
+
pattern = `-[${relAlias}${relTypePattern}${depthPattern}]->(${targetNode})`;
|
|
1160
|
+
break;
|
|
1161
|
+
case "in":
|
|
1162
|
+
pattern = `<-[${relAlias}${relTypePattern}${depthPattern}]-(${targetNode})`;
|
|
1163
|
+
break;
|
|
1164
|
+
case "both":
|
|
1165
|
+
pattern = `-[${relAlias}${relTypePattern}${depthPattern}]-(${targetNode})`;
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
matchParts.push(pattern);
|
|
1169
|
+
estimatedFanOut *= step.repeat ? step.repeat.maxDepth * DEFAULT_ESTIMATED_FANOUT_PER_HOP2 : DEFAULT_ESTIMATED_FANOUT_PER_HOP2;
|
|
1170
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
1171
|
+
const typeParam = nextParam(step.entityTypes);
|
|
1172
|
+
whereClauses.push(`${targetNode}.entityType IN ${typeParam}`);
|
|
1173
|
+
}
|
|
1174
|
+
if (step.entityFilter) {
|
|
1175
|
+
for (const f of step.entityFilter) {
|
|
1176
|
+
whereClauses.push(compilePropertyFilterCypher(targetNode, f, nextParam));
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (step.relationshipFilter) {
|
|
1180
|
+
for (const f of step.relationshipFilter) {
|
|
1181
|
+
whereClauses.push(compilePropertyFilterCypher(relAlias, f, nextParam));
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
lastNode = targetNode;
|
|
1185
|
+
}
|
|
1186
|
+
const matchClause = `MATCH ${matchParts[0]}${matchParts.slice(1).join("")}`;
|
|
1187
|
+
const whereClause = whereClauses.length > 0 ? `
|
|
1188
|
+
WHERE ${whereClauses.join(" AND ")}` : "";
|
|
1189
|
+
let returnClause;
|
|
1190
|
+
if (spec.returnMode === "path") {
|
|
1191
|
+
const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
|
|
1192
|
+
const allRels = spec.steps.map((_, i) => `r${i}`);
|
|
1193
|
+
returnClause = `RETURN ${[...allNodes, ...allRels].join(", ")}`;
|
|
1194
|
+
} else if (spec.returnMode === "all") {
|
|
1195
|
+
const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
|
|
1196
|
+
returnClause = spec.dedup !== false ? `RETURN DISTINCT ${allNodes.join(", ")}` : `RETURN ${allNodes.join(", ")}`;
|
|
1197
|
+
} else {
|
|
1198
|
+
returnClause = spec.dedup !== false ? `RETURN DISTINCT ${lastNode}` : `RETURN ${lastNode}`;
|
|
1199
|
+
}
|
|
1200
|
+
const offset = spec.offset ?? 0;
|
|
1201
|
+
const limit = spec.limit ?? 50;
|
|
1202
|
+
const skipClause = offset > 0 ? `
|
|
1203
|
+
SKIP ${nextParam(offset)}` : "";
|
|
1204
|
+
const limitClause = `
|
|
1205
|
+
LIMIT ${nextParam(limit)}`;
|
|
1206
|
+
params["_limit"] = limit;
|
|
1207
|
+
params["_offset"] = offset;
|
|
1208
|
+
const query = `${matchClause}${whereClause}
|
|
1209
|
+
${returnClause}${skipClause}${limitClause}`;
|
|
1210
|
+
return {
|
|
1211
|
+
query,
|
|
1212
|
+
params,
|
|
1213
|
+
estimatedFanOut: Math.min(estimatedFanOut, 1e4)
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
function compilePropertyFilterCypher(nodeAlias, filter, nextParam) {
|
|
1218
|
+
const prop = `${nodeAlias}.${filter.key}`;
|
|
1219
|
+
switch (filter.operator) {
|
|
1220
|
+
case "eq":
|
|
1221
|
+
return `${prop} = ${nextParam(filter.value)}`;
|
|
1222
|
+
case "neq":
|
|
1223
|
+
return `${prop} <> ${nextParam(filter.value)}`;
|
|
1224
|
+
case "gt":
|
|
1225
|
+
return `${prop} > ${nextParam(filter.value)}`;
|
|
1226
|
+
case "gte":
|
|
1227
|
+
return `${prop} >= ${nextParam(filter.value)}`;
|
|
1228
|
+
case "lt":
|
|
1229
|
+
return `${prop} < ${nextParam(filter.value)}`;
|
|
1230
|
+
case "lte":
|
|
1231
|
+
return `${prop} <= ${nextParam(filter.value)}`;
|
|
1232
|
+
case "contains":
|
|
1233
|
+
return `${prop} CONTAINS ${nextParam(filter.value)}`;
|
|
1234
|
+
case "isNull":
|
|
1235
|
+
return `${prop} IS NULL`;
|
|
1236
|
+
case "isNotNull":
|
|
1237
|
+
return `${prop} IS NOT NULL`;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/relationships/PropertyFilterMatcher.ts
|
|
1242
|
+
function matchesPropertyFilters(properties, filters) {
|
|
1243
|
+
return filters.every((filter) => matchesSingleFilter(properties, filter));
|
|
1244
|
+
}
|
|
1245
|
+
function matchesSingleFilter(properties, filter) {
|
|
1246
|
+
const value = properties[filter.key];
|
|
1247
|
+
switch (filter.operator) {
|
|
1248
|
+
case "isNull":
|
|
1249
|
+
return value === null || value === void 0;
|
|
1250
|
+
case "isNotNull":
|
|
1251
|
+
return value !== null && value !== void 0;
|
|
1252
|
+
case "eq":
|
|
1253
|
+
return value === filter.value;
|
|
1254
|
+
case "neq":
|
|
1255
|
+
return value !== filter.value;
|
|
1256
|
+
case "gt":
|
|
1257
|
+
return typeof value === "number" && typeof filter.value === "number" ? value > filter.value : typeof value === "string" && typeof filter.value === "string" ? value > filter.value : false;
|
|
1258
|
+
case "lt":
|
|
1259
|
+
return typeof value === "number" && typeof filter.value === "number" ? value < filter.value : typeof value === "string" && typeof filter.value === "string" ? value < filter.value : false;
|
|
1260
|
+
case "gte":
|
|
1261
|
+
return typeof value === "number" && typeof filter.value === "number" ? value >= filter.value : typeof value === "string" && typeof filter.value === "string" ? value >= filter.value : false;
|
|
1262
|
+
case "lte":
|
|
1263
|
+
return typeof value === "number" && typeof filter.value === "number" ? value <= filter.value : typeof value === "string" && typeof filter.value === "string" ? value <= filter.value : false;
|
|
1264
|
+
case "contains":
|
|
1265
|
+
return typeof value === "string" && typeof filter.value === "string" ? value.toLowerCase().includes(filter.value.toLowerCase()) : false;
|
|
1266
|
+
default:
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// src/relationships/FallbackTraversalExecutor.ts
|
|
1272
|
+
var MAX_STORAGE_CALLS = 500;
|
|
1273
|
+
var REL_FETCH_LIMIT = 1e3;
|
|
1274
|
+
async function executeFallbackTraversal(repositoryId, storage, spec) {
|
|
1275
|
+
const startTime = Date.now();
|
|
1276
|
+
let storageCalls = 0;
|
|
1277
|
+
const checkCallLimit = () => {
|
|
1278
|
+
if (storageCalls >= MAX_STORAGE_CALLS) {
|
|
1279
|
+
throw new Error(`Fallback traversal exceeded ${MAX_STORAGE_CALLS} storage calls. Consider using a GraphTraversalProvider for large traversals.`);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
let frontier = [];
|
|
1283
|
+
if (spec.start.entityId) {
|
|
1284
|
+
const entity = await resolveEntity(repositoryId, storage, spec.start.entityId);
|
|
1285
|
+
storageCalls++;
|
|
1286
|
+
if (entity) {
|
|
1287
|
+
frontier.push({ entity, entityPath: [entity.id], relationshipPath: [] });
|
|
1288
|
+
} else {
|
|
1289
|
+
throw new EntityNotFoundError(spec.start.entityId);
|
|
1290
|
+
}
|
|
1291
|
+
} else if (spec.start.entityType) {
|
|
1292
|
+
const result = await storage.findEntities(repositoryId, {
|
|
1293
|
+
entityTypes: [spec.start.entityType],
|
|
1294
|
+
limit: spec.limit ?? 50,
|
|
1295
|
+
offset: 0
|
|
1296
|
+
});
|
|
1297
|
+
storageCalls++;
|
|
1298
|
+
frontier = result.items.map((e) => ({
|
|
1299
|
+
entity: e,
|
|
1300
|
+
entityPath: [e.id],
|
|
1301
|
+
relationshipPath: []
|
|
1302
|
+
}));
|
|
1303
|
+
} else if (spec.start.filter && spec.start.filter.length > 0) {
|
|
1304
|
+
const result = await storage.findEntities(repositoryId, {
|
|
1305
|
+
limit: spec.limit ?? 50,
|
|
1306
|
+
offset: 0
|
|
1307
|
+
});
|
|
1308
|
+
storageCalls++;
|
|
1309
|
+
frontier = result.items.filter((e) => matchesPropertyFilters(e.properties, spec.start.filter)).map((e) => ({
|
|
1310
|
+
entity: e,
|
|
1311
|
+
entityPath: [e.id],
|
|
1312
|
+
relationshipPath: []
|
|
1313
|
+
}));
|
|
1314
|
+
}
|
|
1315
|
+
if (spec.start.filter && spec.start.filter.length > 0 && (spec.start.entityId || spec.start.entityType)) {
|
|
1316
|
+
frontier = frontier.filter(
|
|
1317
|
+
(entry) => matchesPropertyFilters(entry.entity.properties, spec.start.filter)
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
const allCollected = spec.returnMode === "all" ? [...frontier] : [];
|
|
1321
|
+
for (const step of spec.steps) {
|
|
1322
|
+
checkCallLimit();
|
|
1323
|
+
if (step.repeat) {
|
|
1324
|
+
const result = await executeRepeatStep(
|
|
1325
|
+
repositoryId,
|
|
1326
|
+
storage,
|
|
1327
|
+
frontier,
|
|
1328
|
+
step,
|
|
1329
|
+
() => {
|
|
1330
|
+
storageCalls++;
|
|
1331
|
+
checkCallLimit();
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
frontier = result.terminal;
|
|
1335
|
+
if (spec.returnMode === "all") {
|
|
1336
|
+
allCollected.push(...result.intermediates);
|
|
1337
|
+
}
|
|
1338
|
+
} else {
|
|
1339
|
+
frontier = await executeSingleStep(
|
|
1340
|
+
repositoryId,
|
|
1341
|
+
storage,
|
|
1342
|
+
frontier,
|
|
1343
|
+
step,
|
|
1344
|
+
() => {
|
|
1345
|
+
storageCalls++;
|
|
1346
|
+
checkCallLimit();
|
|
1347
|
+
}
|
|
1348
|
+
);
|
|
1349
|
+
if (spec.returnMode === "all") {
|
|
1350
|
+
allCollected.push(...frontier);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
const detailLevel = spec.detailLevel ?? "summary";
|
|
1355
|
+
const dedup = spec.dedup !== false;
|
|
1356
|
+
const limit = spec.limit ?? 50;
|
|
1357
|
+
const offset = spec.offset ?? 0;
|
|
1358
|
+
let resultEntries;
|
|
1359
|
+
if (spec.returnMode === "all") {
|
|
1360
|
+
resultEntries = allCollected;
|
|
1361
|
+
} else {
|
|
1362
|
+
resultEntries = frontier;
|
|
1363
|
+
}
|
|
1364
|
+
if (dedup) {
|
|
1365
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1366
|
+
resultEntries = resultEntries.filter((entry) => {
|
|
1367
|
+
if (seen.has(entry.entity.id)) return false;
|
|
1368
|
+
seen.add(entry.entity.id);
|
|
1369
|
+
return true;
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
const total = resultEntries.length;
|
|
1373
|
+
const paged = resultEntries.slice(offset, offset + limit);
|
|
1374
|
+
const entities = paged.map((entry) => projectEntity(entry.entity, detailLevel));
|
|
1375
|
+
let relationships;
|
|
1376
|
+
let paths;
|
|
1377
|
+
if (spec.returnMode === "path" || spec.returnMode === "all") {
|
|
1378
|
+
const relMap = /* @__PURE__ */ new Map();
|
|
1379
|
+
for (const entry of paged) {
|
|
1380
|
+
for (const rel of entry.relationshipPath) {
|
|
1381
|
+
if (!relMap.has(rel.id)) {
|
|
1382
|
+
relMap.set(rel.id, {
|
|
1383
|
+
id: rel.id,
|
|
1384
|
+
type: rel.relationshipType,
|
|
1385
|
+
sourceEntityId: rel.sourceEntityId,
|
|
1386
|
+
targetEntityId: rel.targetEntityId,
|
|
1387
|
+
direction: "outbound",
|
|
1388
|
+
properties: rel.properties
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
relationships = Array.from(relMap.values());
|
|
1394
|
+
}
|
|
1395
|
+
if (spec.returnMode === "path") {
|
|
1396
|
+
paths = paged.map((entry) => ({
|
|
1397
|
+
length: entry.entityPath.length - 1,
|
|
1398
|
+
entities: [projectEntity(entry.entity, detailLevel)],
|
|
1399
|
+
relationships: entry.relationshipPath.map((rel) => ({
|
|
1400
|
+
id: rel.id,
|
|
1401
|
+
type: rel.relationshipType,
|
|
1402
|
+
sourceEntityId: rel.sourceEntityId,
|
|
1403
|
+
targetEntityId: rel.targetEntityId,
|
|
1404
|
+
direction: "outbound",
|
|
1405
|
+
properties: rel.properties
|
|
1406
|
+
}))
|
|
1407
|
+
}));
|
|
1408
|
+
}
|
|
1409
|
+
const executionTimeMs = Date.now() - startTime;
|
|
1410
|
+
return {
|
|
1411
|
+
entities,
|
|
1412
|
+
relationships,
|
|
1413
|
+
paths,
|
|
1414
|
+
total,
|
|
1415
|
+
returned: paged.length,
|
|
1416
|
+
hasMore: offset + limit < total,
|
|
1417
|
+
queryMetadata: {
|
|
1418
|
+
executionTimeMs,
|
|
1419
|
+
appliedLimits: {
|
|
1420
|
+
maxResults: limit
|
|
1421
|
+
},
|
|
1422
|
+
truncated: total > limit + offset,
|
|
1423
|
+
truncationReason: total > limit + offset ? "result_limit" : void 0
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
async function executeSingleStep(repositoryId, storage, frontier, step, onStorageCall) {
|
|
1428
|
+
const direction = mapDirection(step.direction);
|
|
1429
|
+
const pendingEdges = [];
|
|
1430
|
+
for (const entry of frontier) {
|
|
1431
|
+
const rels = await storage.getEntityRelationships(repositoryId, entry.entity.id, {
|
|
1432
|
+
direction,
|
|
1433
|
+
relationshipTypes: step.relationshipTypes,
|
|
1434
|
+
limit: REL_FETCH_LIMIT
|
|
1435
|
+
});
|
|
1436
|
+
onStorageCall();
|
|
1437
|
+
for (const rel of rels.items) {
|
|
1438
|
+
if (step.relationshipFilter && step.relationshipFilter.length > 0) {
|
|
1439
|
+
if (!matchesPropertyFilters(rel.properties, step.relationshipFilter)) {
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
const targetId = getTargetId(rel, entry.entity.id);
|
|
1444
|
+
pendingEdges.push({ entry, rel, targetId });
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
if (pendingEdges.length === 0) return [];
|
|
1448
|
+
const uniqueTargetIds = [...new Set(pendingEdges.map((e) => e.targetId))];
|
|
1449
|
+
const entityMap = await storage.getEntities(repositoryId, uniqueTargetIds);
|
|
1450
|
+
onStorageCall();
|
|
1451
|
+
const nextFrontier = [];
|
|
1452
|
+
for (const { entry, rel, targetId } of pendingEdges) {
|
|
1453
|
+
const targetEntity = entityMap.get(targetId);
|
|
1454
|
+
if (!targetEntity) continue;
|
|
1455
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
1456
|
+
if (!step.entityTypes.includes(targetEntity.entityType)) continue;
|
|
1457
|
+
}
|
|
1458
|
+
if (step.entityFilter && step.entityFilter.length > 0) {
|
|
1459
|
+
if (!matchesPropertyFilters(targetEntity.properties, step.entityFilter)) continue;
|
|
1460
|
+
}
|
|
1461
|
+
nextFrontier.push({
|
|
1462
|
+
entity: targetEntity,
|
|
1463
|
+
entityPath: [...entry.entityPath, targetEntity.id],
|
|
1464
|
+
relationshipPath: [...entry.relationshipPath, rel]
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
return nextFrontier;
|
|
1468
|
+
}
|
|
1469
|
+
async function executeRepeatStep(repositoryId, storage, frontier, step, onStorageCall) {
|
|
1470
|
+
const maxDepth = step.repeat?.maxDepth ?? 1;
|
|
1471
|
+
const emitIntermediates = step.repeat?.emitIntermediates !== false;
|
|
1472
|
+
const untilFilters = step.repeat?.until;
|
|
1473
|
+
let current = frontier;
|
|
1474
|
+
const allIntermediates = [];
|
|
1475
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1476
|
+
for (const entry of frontier) {
|
|
1477
|
+
visited.add(entry.entity.id);
|
|
1478
|
+
}
|
|
1479
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1480
|
+
const next = await executeSingleStep(repositoryId, storage, current, step, onStorageCall);
|
|
1481
|
+
const unvisited = next.filter((entry) => {
|
|
1482
|
+
if (visited.has(entry.entity.id)) return false;
|
|
1483
|
+
visited.add(entry.entity.id);
|
|
1484
|
+
return true;
|
|
1485
|
+
});
|
|
1486
|
+
if (unvisited.length === 0) break;
|
|
1487
|
+
if (untilFilters && untilFilters.length > 0) {
|
|
1488
|
+
const matching = [];
|
|
1489
|
+
const continuing = [];
|
|
1490
|
+
for (const entry of unvisited) {
|
|
1491
|
+
if (matchesPropertyFilters(entry.entity.properties, untilFilters)) {
|
|
1492
|
+
matching.push(entry);
|
|
1493
|
+
} else {
|
|
1494
|
+
continuing.push(entry);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (emitIntermediates) {
|
|
1498
|
+
allIntermediates.push(...continuing);
|
|
1499
|
+
}
|
|
1500
|
+
allIntermediates.push(...matching);
|
|
1501
|
+
if (matching.length > 0 && continuing.length === 0) {
|
|
1502
|
+
current = matching;
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
current = continuing;
|
|
1506
|
+
} else {
|
|
1507
|
+
if (emitIntermediates) {
|
|
1508
|
+
allIntermediates.push(...unvisited);
|
|
1509
|
+
}
|
|
1510
|
+
current = unvisited;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return {
|
|
1514
|
+
terminal: current,
|
|
1515
|
+
intermediates: allIntermediates
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
async function resolveEntity(repositoryId, storage, idOrSlug) {
|
|
1519
|
+
const byId = await storage.getEntity(repositoryId, idOrSlug);
|
|
1520
|
+
if (byId) return byId;
|
|
1521
|
+
return storage.getEntityBySlug(repositoryId, idOrSlug);
|
|
1522
|
+
}
|
|
1523
|
+
function mapDirection(direction) {
|
|
1524
|
+
switch (direction) {
|
|
1525
|
+
case "out":
|
|
1526
|
+
return "outbound";
|
|
1527
|
+
case "in":
|
|
1528
|
+
return "inbound";
|
|
1529
|
+
case "both":
|
|
1530
|
+
return "both";
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
function getTargetId(rel, currentEntityId) {
|
|
1534
|
+
if (rel.sourceEntityId === currentEntityId) {
|
|
1535
|
+
return rel.targetEntityId;
|
|
1536
|
+
}
|
|
1537
|
+
return rel.sourceEntityId;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
787
1540
|
// src/relationships/GraphTraversal.ts
|
|
788
1541
|
var GraphTraversal = class {
|
|
789
|
-
constructor(repositoryId, storage) {
|
|
1542
|
+
constructor(repositoryId, storage, graphTraversalProvider, vocabularyEngine) {
|
|
790
1543
|
this.repositoryId = repositoryId;
|
|
791
1544
|
this.storage = storage;
|
|
1545
|
+
this.graphTraversalProvider = graphTraversalProvider;
|
|
1546
|
+
this.vocabularyEngine = vocabularyEngine;
|
|
792
1547
|
}
|
|
793
1548
|
/** BFS neighbourhood exploration from a centre entity */
|
|
794
1549
|
async exploreNeighbourhood(entityId, options) {
|
|
@@ -920,6 +1675,51 @@ var GraphTraversal = class {
|
|
|
920
1675
|
}))
|
|
921
1676
|
};
|
|
922
1677
|
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Execute a structured traversal.
|
|
1680
|
+
* Validates the spec, compiles to native query if a provider exists,
|
|
1681
|
+
* falls back to application-level BFS otherwise.
|
|
1682
|
+
*/
|
|
1683
|
+
async traverse(spec) {
|
|
1684
|
+
const capabilities = this.graphTraversalProvider?.getCapabilities();
|
|
1685
|
+
const vocabulary = this.vocabularyEngine ? await this.vocabularyEngine.getVocabulary() : void 0;
|
|
1686
|
+
const validation = validateTraversalSpec(spec, vocabulary, capabilities);
|
|
1687
|
+
if (!validation.valid) {
|
|
1688
|
+
const vocabErrors = validation.errors.filter((e) => e.startsWith("Unknown vocabulary types:"));
|
|
1689
|
+
const structErrors = validation.errors.filter((e) => !e.startsWith("Unknown vocabulary types:"));
|
|
1690
|
+
if (vocabErrors.length > 0) {
|
|
1691
|
+
const unknownTypes = vocabErrors.join("; ").replace(/Unknown vocabulary types: /g, "").split(", ").map((t) => t.trim());
|
|
1692
|
+
throw new TraversalVocabularyError(unknownTypes);
|
|
1693
|
+
}
|
|
1694
|
+
throw new TraversalValidationError(structErrors.length > 0 ? structErrors : validation.errors);
|
|
1695
|
+
}
|
|
1696
|
+
if (this.graphTraversalProvider && vocabulary) {
|
|
1697
|
+
const language = capabilities.nativeQueryLanguage;
|
|
1698
|
+
let compiledQuery;
|
|
1699
|
+
if (language === "gremlin") {
|
|
1700
|
+
const compiler = new GremlinCompiler();
|
|
1701
|
+
const compiled = compiler.compile(spec, vocabulary);
|
|
1702
|
+
compiledQuery = compiled.query;
|
|
1703
|
+
} else if (language === "cypher") {
|
|
1704
|
+
const compiler = new CypherCompiler();
|
|
1705
|
+
const compiled = compiler.compile(spec, vocabulary);
|
|
1706
|
+
compiledQuery = compiled.query;
|
|
1707
|
+
}
|
|
1708
|
+
return this.graphTraversalProvider.traverse(this.repositoryId, spec, compiledQuery);
|
|
1709
|
+
}
|
|
1710
|
+
return executeFallbackTraversal(this.repositoryId, this.storage, spec);
|
|
1711
|
+
}
|
|
1712
|
+
/**
|
|
1713
|
+
* Execute a native graph query.
|
|
1714
|
+
* Pass-through to GraphTraversalProvider.executeNativeQuery().
|
|
1715
|
+
* Throws GraphTraversalProviderRequiredError if no provider registered.
|
|
1716
|
+
*/
|
|
1717
|
+
async executeNativeQuery(query, params) {
|
|
1718
|
+
if (!this.graphTraversalProvider) {
|
|
1719
|
+
throw new GraphTraversalProviderRequiredError();
|
|
1720
|
+
}
|
|
1721
|
+
return this.graphTraversalProvider.executeNativeQuery(this.repositoryId, query, params);
|
|
1722
|
+
}
|
|
923
1723
|
};
|
|
924
1724
|
|
|
925
1725
|
// src/search/SearchOrchestrator.ts
|
|
@@ -1128,7 +1928,9 @@ var MemoryRepository = class {
|
|
|
1128
1928
|
);
|
|
1129
1929
|
this.graphTraversal = new GraphTraversal(
|
|
1130
1930
|
config.repositoryId,
|
|
1131
|
-
config.storage
|
|
1931
|
+
config.storage,
|
|
1932
|
+
config.graphTraversal,
|
|
1933
|
+
config.vocabularyEngine
|
|
1132
1934
|
);
|
|
1133
1935
|
this.searchOrchestrator = new SearchOrchestrator({
|
|
1134
1936
|
repositoryId: config.repositoryId,
|
|
@@ -1152,19 +1954,21 @@ var MemoryRepository = class {
|
|
|
1152
1954
|
return this.proposeVocabularyChange(proposal);
|
|
1153
1955
|
}
|
|
1154
1956
|
// ─── Entities ──────────────────────────────────────────────────────
|
|
1155
|
-
async
|
|
1156
|
-
const
|
|
1957
|
+
async createEntities(inputs) {
|
|
1958
|
+
const entities = await this.entityManager.create(inputs);
|
|
1157
1959
|
if (this.search) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1960
|
+
for (const entity of entities) {
|
|
1961
|
+
await this.search.indexEntity(this.repositoryId, {
|
|
1962
|
+
entityId: entity.id,
|
|
1963
|
+
entityType: entity.entityType,
|
|
1964
|
+
label: entity.label,
|
|
1965
|
+
summary: entity.summary,
|
|
1966
|
+
properties: entity.properties,
|
|
1967
|
+
data: entity.data
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1166
1970
|
}
|
|
1167
|
-
return
|
|
1971
|
+
return entities;
|
|
1168
1972
|
}
|
|
1169
1973
|
async updateEntity(entityId, updates) {
|
|
1170
1974
|
const entity = await this.entityManager.update(entityId, updates);
|
|
@@ -1251,8 +2055,8 @@ var MemoryRepository = class {
|
|
|
1251
2055
|
}
|
|
1252
2056
|
}
|
|
1253
2057
|
// ─── Relationships ─────────────────────────────────────────────────
|
|
1254
|
-
async
|
|
1255
|
-
return this.relationshipManager.create(
|
|
2058
|
+
async createRelationships(inputs) {
|
|
2059
|
+
return this.relationshipManager.create(inputs);
|
|
1256
2060
|
}
|
|
1257
2061
|
async removeRelationship(relationshipId) {
|
|
1258
2062
|
return this.relationshipManager.remove(relationshipId);
|
|
@@ -1346,6 +2150,12 @@ var MemoryRepository = class {
|
|
|
1346
2150
|
async findPaths(sourceId, targetId, options) {
|
|
1347
2151
|
return this.graphTraversal.findPaths(sourceId, targetId, options);
|
|
1348
2152
|
}
|
|
2153
|
+
async traverse(spec) {
|
|
2154
|
+
return this.graphTraversal.traverse(spec);
|
|
2155
|
+
}
|
|
2156
|
+
async executeNativeQuery(query, params) {
|
|
2157
|
+
return this.graphTraversal.executeNativeQuery(query, params);
|
|
2158
|
+
}
|
|
1349
2159
|
// ─── Search ────────────────────────────────────────────────────────
|
|
1350
2160
|
async searchByConcept(query, options) {
|
|
1351
2161
|
return this.searchOrchestrator.searchByConcept(query, options);
|
|
@@ -2430,13 +3240,15 @@ var EventBus = class {
|
|
|
2430
3240
|
};
|
|
2431
3241
|
|
|
2432
3242
|
// src/portability/RepositoryExporter.ts
|
|
2433
|
-
var LIBRARY_VERSION = "0.1.0";
|
|
3243
|
+
var LIBRARY_VERSION = true ? "0.2.0" : "0.1.0";
|
|
2434
3244
|
var RepositoryExporter = class {
|
|
2435
3245
|
storage;
|
|
2436
3246
|
provenance;
|
|
3247
|
+
legal;
|
|
2437
3248
|
constructor(config) {
|
|
2438
3249
|
this.storage = config.storage;
|
|
2439
3250
|
this.provenance = config.provenance;
|
|
3251
|
+
this.legal = config.legal;
|
|
2440
3252
|
}
|
|
2441
3253
|
/**
|
|
2442
3254
|
* Stream a repository export as an async generator.
|
|
@@ -2470,6 +3282,9 @@ var RepositoryExporter = class {
|
|
|
2470
3282
|
relationshipTypeBreakdown: stats.relationshipTypeBreakdown
|
|
2471
3283
|
}
|
|
2472
3284
|
};
|
|
3285
|
+
if (this.legal) {
|
|
3286
|
+
manifest.legal = this.legal;
|
|
3287
|
+
}
|
|
2473
3288
|
if (repo.metadata?.embeddingModelId) {
|
|
2474
3289
|
manifest.embedding = {
|
|
2475
3290
|
modelId: repo.metadata.embeddingModelId,
|
|
@@ -3038,6 +3853,7 @@ var DeepMemory = class {
|
|
|
3038
3853
|
embedding;
|
|
3039
3854
|
/** Reserved for future distributed locking support */
|
|
3040
3855
|
lock;
|
|
3856
|
+
graphTraversalProvider;
|
|
3041
3857
|
provenance;
|
|
3042
3858
|
globalEventBus;
|
|
3043
3859
|
initialised = false;
|
|
@@ -3046,24 +3862,42 @@ var DeepMemory = class {
|
|
|
3046
3862
|
this.search = config.search;
|
|
3047
3863
|
this.embedding = config.embedding;
|
|
3048
3864
|
this.lock = config.lock;
|
|
3865
|
+
this.graphTraversalProvider = config.graphTraversal;
|
|
3049
3866
|
this.provenance = new ProvenanceTracker(config.provenance);
|
|
3050
3867
|
this.globalEventBus = new EventBus(config.provenance);
|
|
3051
3868
|
}
|
|
3052
|
-
/** Initialise the storage provider (call once before use) */
|
|
3869
|
+
/** Initialise the storage provider and optional providers (call once before use) */
|
|
3053
3870
|
async ensureInitialised() {
|
|
3054
3871
|
if (!this.initialised) {
|
|
3055
3872
|
if (this.storage.initialise) {
|
|
3056
3873
|
await this.storage.initialise();
|
|
3057
3874
|
}
|
|
3875
|
+
if (this.graphTraversalProvider?.initialise) {
|
|
3876
|
+
await this.graphTraversalProvider.initialise();
|
|
3877
|
+
}
|
|
3058
3878
|
this.initialised = true;
|
|
3059
3879
|
}
|
|
3060
3880
|
}
|
|
3061
3881
|
/** Create or migrate the storage schema. Call once at deployment / startup, not per-request. */
|
|
3062
3882
|
async ensureSchema() {
|
|
3063
|
-
|
|
3883
|
+
const noOpResult = {
|
|
3884
|
+
databaseCreated: false,
|
|
3885
|
+
schemaCreated: false,
|
|
3886
|
+
alreadyUpToDate: true,
|
|
3887
|
+
schemaVersion: 0
|
|
3888
|
+
};
|
|
3889
|
+
try {
|
|
3890
|
+
await this.ensureInitialised();
|
|
3891
|
+
} catch {
|
|
3892
|
+
}
|
|
3893
|
+
let result = noOpResult;
|
|
3064
3894
|
if (this.storage.ensureSchema) {
|
|
3065
|
-
await this.storage.ensureSchema();
|
|
3895
|
+
result = await this.storage.ensureSchema();
|
|
3896
|
+
}
|
|
3897
|
+
if (!this.initialised) {
|
|
3898
|
+
await this.ensureInitialised();
|
|
3066
3899
|
}
|
|
3900
|
+
return result;
|
|
3067
3901
|
}
|
|
3068
3902
|
validateRepositoryId(repositoryId) {
|
|
3069
3903
|
if (!isValidUuid(repositoryId)) {
|
|
@@ -3120,6 +3954,7 @@ var DeepMemory = class {
|
|
|
3120
3954
|
storage: this.storage,
|
|
3121
3955
|
search: this.search,
|
|
3122
3956
|
embedding: this.embedding,
|
|
3957
|
+
graphTraversal: this.graphTraversalProvider,
|
|
3123
3958
|
vocabularyEngine,
|
|
3124
3959
|
provenanceTracker: this.provenance,
|
|
3125
3960
|
eventBus
|
|
@@ -3151,6 +3986,7 @@ var DeepMemory = class {
|
|
|
3151
3986
|
storage: this.storage,
|
|
3152
3987
|
search: this.search,
|
|
3153
3988
|
embedding: this.embedding,
|
|
3989
|
+
graphTraversal: this.graphTraversalProvider,
|
|
3154
3990
|
vocabularyEngine,
|
|
3155
3991
|
provenanceTracker: this.provenance,
|
|
3156
3992
|
eventBus
|
|
@@ -3192,11 +4028,12 @@ var DeepMemory = class {
|
|
|
3192
4028
|
await this.globalEventBus.emit("repository:deleted", { repositoryId });
|
|
3193
4029
|
}
|
|
3194
4030
|
/** Export a repository to a portable archive */
|
|
3195
|
-
async exportRepository(repositoryId) {
|
|
4031
|
+
async exportRepository(repositoryId, options) {
|
|
3196
4032
|
await this.ensureInitialised();
|
|
3197
4033
|
const exporter = new RepositoryExporter({
|
|
3198
4034
|
storage: this.storage,
|
|
3199
|
-
provenance: this.provenance.getContext()
|
|
4035
|
+
provenance: this.provenance.getContext(),
|
|
4036
|
+
legal: options?.legal
|
|
3200
4037
|
});
|
|
3201
4038
|
const archive = await exporter.export(repositoryId);
|
|
3202
4039
|
await this.globalEventBus.emit("export:completed", {
|
|
@@ -3233,11 +4070,12 @@ var DeepMemory = class {
|
|
|
3233
4070
|
* Yields: manifest → vocabulary → entity chunks → relationship chunks.
|
|
3234
4071
|
* Use for large repositories to avoid loading everything into memory.
|
|
3235
4072
|
*/
|
|
3236
|
-
async *exportRepositoryStream(repositoryId) {
|
|
4073
|
+
async *exportRepositoryStream(repositoryId, options) {
|
|
3237
4074
|
await this.ensureInitialised();
|
|
3238
4075
|
const exporter = new RepositoryExporter({
|
|
3239
4076
|
storage: this.storage,
|
|
3240
|
-
provenance: this.provenance.getContext()
|
|
4077
|
+
provenance: this.provenance.getContext(),
|
|
4078
|
+
legal: options?.legal
|
|
3241
4079
|
});
|
|
3242
4080
|
await this.globalEventBus.emit("export:started", { repositoryId });
|
|
3243
4081
|
let entityCount = 0;
|
|
@@ -3294,6 +4132,9 @@ var DeepMemory = class {
|
|
|
3294
4132
|
/** Dispose of all resources */
|
|
3295
4133
|
async dispose() {
|
|
3296
4134
|
this.globalEventBus.removeAllListeners();
|
|
4135
|
+
if (this.graphTraversalProvider?.dispose) {
|
|
4136
|
+
await this.graphTraversalProvider.dispose();
|
|
4137
|
+
}
|
|
3297
4138
|
if (this.storage.dispose) {
|
|
3298
4139
|
await this.storage.dispose();
|
|
3299
4140
|
}
|
|
@@ -3301,36 +4142,6 @@ var DeepMemory = class {
|
|
|
3301
4142
|
}
|
|
3302
4143
|
};
|
|
3303
4144
|
|
|
3304
|
-
// src/relationships/PropertyFilterMatcher.ts
|
|
3305
|
-
function matchesPropertyFilters(properties, filters) {
|
|
3306
|
-
return filters.every((filter) => matchesSingleFilter(properties, filter));
|
|
3307
|
-
}
|
|
3308
|
-
function matchesSingleFilter(properties, filter) {
|
|
3309
|
-
const value = properties[filter.key];
|
|
3310
|
-
switch (filter.operator) {
|
|
3311
|
-
case "isNull":
|
|
3312
|
-
return value === null || value === void 0;
|
|
3313
|
-
case "isNotNull":
|
|
3314
|
-
return value !== null && value !== void 0;
|
|
3315
|
-
case "eq":
|
|
3316
|
-
return value === filter.value;
|
|
3317
|
-
case "neq":
|
|
3318
|
-
return value !== filter.value;
|
|
3319
|
-
case "gt":
|
|
3320
|
-
return typeof value === "number" && typeof filter.value === "number" ? value > filter.value : typeof value === "string" && typeof filter.value === "string" ? value > filter.value : false;
|
|
3321
|
-
case "lt":
|
|
3322
|
-
return typeof value === "number" && typeof filter.value === "number" ? value < filter.value : typeof value === "string" && typeof filter.value === "string" ? value < filter.value : false;
|
|
3323
|
-
case "gte":
|
|
3324
|
-
return typeof value === "number" && typeof filter.value === "number" ? value >= filter.value : typeof value === "string" && typeof filter.value === "string" ? value >= filter.value : false;
|
|
3325
|
-
case "lte":
|
|
3326
|
-
return typeof value === "number" && typeof filter.value === "number" ? value <= filter.value : typeof value === "string" && typeof filter.value === "string" ? value <= filter.value : false;
|
|
3327
|
-
case "contains":
|
|
3328
|
-
return typeof value === "string" && typeof filter.value === "string" ? value.toLowerCase().includes(filter.value.toLowerCase()) : false;
|
|
3329
|
-
default:
|
|
3330
|
-
return false;
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
|
-
|
|
3334
4145
|
// src/providers-builtin/InMemoryStorageProvider.ts
|
|
3335
4146
|
var InMemoryStorageProvider = class {
|
|
3336
4147
|
stores = /* @__PURE__ */ new Map();
|
|
@@ -4026,6 +4837,7 @@ var NoOpEmbeddingProvider = class {
|
|
|
4026
4837
|
};
|
|
4027
4838
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4028
4839
|
0 && (module.exports = {
|
|
4840
|
+
CypherCompiler,
|
|
4029
4841
|
DeepMemory,
|
|
4030
4842
|
DeepMemoryError,
|
|
4031
4843
|
DuplicateEntityError,
|
|
@@ -4035,6 +4847,8 @@ var NoOpEmbeddingProvider = class {
|
|
|
4035
4847
|
EntityNotFoundError,
|
|
4036
4848
|
ExportError,
|
|
4037
4849
|
GovernanceDeniedError,
|
|
4850
|
+
GraphTraversalProviderRequiredError,
|
|
4851
|
+
GremlinCompiler,
|
|
4038
4852
|
ImportError,
|
|
4039
4853
|
InMemorySearchProvider,
|
|
4040
4854
|
InMemoryStorageProvider,
|
|
@@ -4046,6 +4860,9 @@ var NoOpEmbeddingProvider = class {
|
|
|
4046
4860
|
RelationshipConstraintError,
|
|
4047
4861
|
RelationshipNotFoundError,
|
|
4048
4862
|
RepositoryNotFoundError,
|
|
4863
|
+
TraversalTimeoutError,
|
|
4864
|
+
TraversalValidationError,
|
|
4865
|
+
TraversalVocabularyError,
|
|
4049
4866
|
VocabularyValidationError,
|
|
4050
4867
|
generateId,
|
|
4051
4868
|
isValidUuid,
|