@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.js
CHANGED
|
@@ -191,6 +191,52 @@ var ProviderError = class extends DeepMemoryError {
|
|
|
191
191
|
this.name = "ProviderError";
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
|
+
var GraphTraversalProviderRequiredError = class extends DeepMemoryError {
|
|
195
|
+
constructor() {
|
|
196
|
+
super(
|
|
197
|
+
"GRAPH_TRAVERSAL_PROVIDER_REQUIRED",
|
|
198
|
+
"GraphTraversalProvider required: no graph traversal provider is configured",
|
|
199
|
+
`Provide a GraphTraversalProvider in the DeepMemory config to use native graph queries. Structured traversals via traverse() work without a provider using fallback BFS.`
|
|
200
|
+
);
|
|
201
|
+
this.name = "GraphTraversalProviderRequiredError";
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
var TraversalValidationError = class extends DeepMemoryError {
|
|
205
|
+
errors;
|
|
206
|
+
constructor(errors) {
|
|
207
|
+
super(
|
|
208
|
+
"TRAVERSAL_VALIDATION_FAILED",
|
|
209
|
+
`Traversal validation failed: ${errors.join("; ")}`,
|
|
210
|
+
`Check the TraversalSpec structure. Each spec needs a start (entityId, entityType, or filter), at least one step, and a returnMode.`
|
|
211
|
+
);
|
|
212
|
+
this.name = "TraversalValidationError";
|
|
213
|
+
this.errors = errors;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var TraversalVocabularyError = class extends DeepMemoryError {
|
|
217
|
+
unknownTypes;
|
|
218
|
+
constructor(unknownTypes) {
|
|
219
|
+
super(
|
|
220
|
+
"TRAVERSAL_VOCABULARY_ERROR",
|
|
221
|
+
`Traversal references unknown types: ${unknownTypes.join(", ")}`,
|
|
222
|
+
`Use getVocabulary() to see valid entity and relationship types for this repository.`
|
|
223
|
+
);
|
|
224
|
+
this.name = "TraversalVocabularyError";
|
|
225
|
+
this.unknownTypes = unknownTypes;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var TraversalTimeoutError = class extends DeepMemoryError {
|
|
229
|
+
timeoutMs;
|
|
230
|
+
constructor(timeoutMs) {
|
|
231
|
+
super(
|
|
232
|
+
"TRAVERSAL_TIMEOUT",
|
|
233
|
+
`Traversal timed out after ${timeoutMs}ms`,
|
|
234
|
+
`Try reducing the traversal depth, adding more specific filters, or increasing the timeout on your GraphTraversalProvider.`
|
|
235
|
+
);
|
|
236
|
+
this.name = "TraversalTimeoutError";
|
|
237
|
+
this.timeoutMs = timeoutMs;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
194
240
|
|
|
195
241
|
// src/entities/IdGenerator.ts
|
|
196
242
|
function slugify(text) {
|
|
@@ -231,50 +277,54 @@ var EntityManager = class {
|
|
|
231
277
|
this.storage = storage;
|
|
232
278
|
this.embedding = embedding;
|
|
233
279
|
}
|
|
234
|
-
/** Create
|
|
235
|
-
async create(
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (hookResult.cancelled) {
|
|
249
|
-
throw new OperationCancelledError("Entity creation", hookResult.reason ?? "cancelled by hook");
|
|
250
|
-
}
|
|
251
|
-
const id = input.id ?? generateEntityId();
|
|
252
|
-
const slug = await generateUniqueSlug(
|
|
253
|
-
input.entityType,
|
|
254
|
-
input.label,
|
|
255
|
-
async (candidateSlug) => {
|
|
256
|
-
const existing = await this.storage.getEntityBySlug(this.repositoryId, candidateSlug);
|
|
257
|
-
return existing !== null;
|
|
280
|
+
/** Create one or more entities with vocabulary validation, ID generation, provenance, and events */
|
|
281
|
+
async create(inputs) {
|
|
282
|
+
const results = [];
|
|
283
|
+
for (const input of inputs) {
|
|
284
|
+
const validation = await this.vocabularyEngine.validateEntity(input);
|
|
285
|
+
if (!validation.valid) {
|
|
286
|
+
const errorMsg = validation.errors.map((e) => e.message).join("; ");
|
|
287
|
+
const suggestions = validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion);
|
|
288
|
+
await this.eventBus.emit("validation:failed", {
|
|
289
|
+
operation: "createEntity",
|
|
290
|
+
error: errorMsg,
|
|
291
|
+
suggestions
|
|
292
|
+
});
|
|
293
|
+
throw new VocabularyValidationError(validation.errors);
|
|
258
294
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
id
|
|
264
|
-
slug
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
295
|
+
const hookResult = await this.eventBus.emitHook("entity:creating", { input });
|
|
296
|
+
if (hookResult.cancelled) {
|
|
297
|
+
throw new OperationCancelledError("Entity creation", hookResult.reason ?? "cancelled by hook");
|
|
298
|
+
}
|
|
299
|
+
const id = input.id ?? generateEntityId();
|
|
300
|
+
const slug = await generateUniqueSlug(
|
|
301
|
+
input.entityType,
|
|
302
|
+
input.label,
|
|
303
|
+
async (candidateSlug) => {
|
|
304
|
+
const existing = await this.storage.getEntityBySlug(this.repositoryId, candidateSlug);
|
|
305
|
+
return existing !== null;
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
const provenance = this.provenanceTracker.stampCreate();
|
|
309
|
+
const entityEmbedding = await this.generateEmbedding(input.label, input.summary);
|
|
310
|
+
const storedEntity = {
|
|
311
|
+
id,
|
|
312
|
+
slug,
|
|
313
|
+
entityType: input.entityType,
|
|
314
|
+
label: input.label,
|
|
315
|
+
summary: input.summary,
|
|
316
|
+
properties: input.properties ?? {},
|
|
317
|
+
data: input.data,
|
|
318
|
+
dataFormat: input.dataFormat,
|
|
319
|
+
provenance,
|
|
320
|
+
embedding: entityEmbedding
|
|
321
|
+
};
|
|
322
|
+
const created = await this.storage.createEntity(this.repositoryId, storedEntity);
|
|
323
|
+
const entity = storedToEntity(created);
|
|
324
|
+
await this.eventBus.emit("entity:created", { entity });
|
|
325
|
+
results.push(entity);
|
|
326
|
+
}
|
|
327
|
+
return results;
|
|
278
328
|
}
|
|
279
329
|
/** Update an existing entity */
|
|
280
330
|
async update(entityId, updates) {
|
|
@@ -606,55 +656,59 @@ var RelationshipManager = class {
|
|
|
606
656
|
this.eventBus = eventBus;
|
|
607
657
|
this.storage = storage;
|
|
608
658
|
}
|
|
609
|
-
/** Create
|
|
610
|
-
async create(
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
await this.eventBus.emit("validation:failed", {
|
|
626
|
-
operation: "createRelationship",
|
|
627
|
-
error: validation.errors.map((e) => e.message).join("; "),
|
|
628
|
-
suggestions: validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion)
|
|
629
|
-
});
|
|
630
|
-
throw new VocabularyValidationError(validation.errors);
|
|
631
|
-
}
|
|
632
|
-
const hookResult = await this.eventBus.emitHook("relationship:creating", { input });
|
|
633
|
-
if (hookResult.cancelled) {
|
|
634
|
-
throw new OperationCancelledError(
|
|
635
|
-
"Relationship creation",
|
|
636
|
-
hookResult.reason ?? "cancelled by hook"
|
|
659
|
+
/** Create one or more relationships with vocabulary validation, provenance, and events */
|
|
660
|
+
async create(inputs) {
|
|
661
|
+
const results = [];
|
|
662
|
+
for (const input of inputs) {
|
|
663
|
+
const sourceEntity = await this.storage.getEntity(this.repositoryId, input.sourceEntityId);
|
|
664
|
+
if (!sourceEntity) {
|
|
665
|
+
throw new EntityNotFoundError(input.sourceEntityId);
|
|
666
|
+
}
|
|
667
|
+
const targetEntity = await this.storage.getEntity(this.repositoryId, input.targetEntityId);
|
|
668
|
+
if (!targetEntity) {
|
|
669
|
+
throw new EntityNotFoundError(input.targetEntityId);
|
|
670
|
+
}
|
|
671
|
+
const validation = await this.vocabularyEngine.validateRelationship(
|
|
672
|
+
input,
|
|
673
|
+
sourceEntity.entityType,
|
|
674
|
+
targetEntity.entityType
|
|
637
675
|
);
|
|
676
|
+
if (!validation.valid) {
|
|
677
|
+
await this.eventBus.emit("validation:failed", {
|
|
678
|
+
operation: "createRelationship",
|
|
679
|
+
error: validation.errors.map((e) => e.message).join("; "),
|
|
680
|
+
suggestions: validation.errors.filter((e) => e.suggestion).map((e) => e.suggestion)
|
|
681
|
+
});
|
|
682
|
+
throw new VocabularyValidationError(validation.errors);
|
|
683
|
+
}
|
|
684
|
+
const hookResult = await this.eventBus.emitHook("relationship:creating", { input });
|
|
685
|
+
if (hookResult.cancelled) {
|
|
686
|
+
throw new OperationCancelledError(
|
|
687
|
+
"Relationship creation",
|
|
688
|
+
hookResult.reason ?? "cancelled by hook"
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
const normalizedType = toScreamingSnakeCase(input.relationshipType);
|
|
692
|
+
const vocabulary = await this.vocabularyEngine.getVocabulary();
|
|
693
|
+
const relType = vocabulary.relationshipTypes.find((rt) => rt.type === normalizedType);
|
|
694
|
+
const bidirectional = relType?.bidirectional ?? false;
|
|
695
|
+
const id = input.id ?? generateRelationshipId();
|
|
696
|
+
const provenance = this.provenanceTracker.stampCreate();
|
|
697
|
+
const storedRelationship = {
|
|
698
|
+
id,
|
|
699
|
+
relationshipType: normalizedType,
|
|
700
|
+
sourceEntityId: input.sourceEntityId,
|
|
701
|
+
targetEntityId: input.targetEntityId,
|
|
702
|
+
properties: input.properties ?? {},
|
|
703
|
+
bidirectional,
|
|
704
|
+
provenance
|
|
705
|
+
};
|
|
706
|
+
const created = await this.storage.createRelationship(this.repositoryId, storedRelationship);
|
|
707
|
+
const relationship = storedToRelationship(created);
|
|
708
|
+
await this.eventBus.emit("relationship:created", { relationship });
|
|
709
|
+
results.push(relationship);
|
|
638
710
|
}
|
|
639
|
-
|
|
640
|
-
const vocabulary = await this.vocabularyEngine.getVocabulary();
|
|
641
|
-
const relType = vocabulary.relationshipTypes.find((rt) => rt.type === normalizedType);
|
|
642
|
-
const bidirectional = relType?.bidirectional ?? false;
|
|
643
|
-
const id = input.id ?? generateRelationshipId();
|
|
644
|
-
const provenance = this.provenanceTracker.stampCreate();
|
|
645
|
-
const storedRelationship = {
|
|
646
|
-
id,
|
|
647
|
-
relationshipType: normalizedType,
|
|
648
|
-
sourceEntityId: input.sourceEntityId,
|
|
649
|
-
targetEntityId: input.targetEntityId,
|
|
650
|
-
properties: input.properties ?? {},
|
|
651
|
-
bidirectional,
|
|
652
|
-
provenance
|
|
653
|
-
};
|
|
654
|
-
const created = await this.storage.createRelationship(this.repositoryId, storedRelationship);
|
|
655
|
-
const relationship = storedToRelationship(created);
|
|
656
|
-
await this.eventBus.emit("relationship:created", { relationship });
|
|
657
|
-
return relationship;
|
|
711
|
+
return results;
|
|
658
712
|
}
|
|
659
713
|
/** Remove a relationship by ID */
|
|
660
714
|
async remove(relationshipId) {
|
|
@@ -735,11 +789,706 @@ function projectEntity(stored, level) {
|
|
|
735
789
|
}
|
|
736
790
|
}
|
|
737
791
|
|
|
792
|
+
// src/relationships/TraversalValidator.ts
|
|
793
|
+
var DEFAULT_MAX_STEPS = 6;
|
|
794
|
+
var DEFAULT_MAX_LIMIT = 200;
|
|
795
|
+
var DEFAULT_FALLBACK_MAX_DEPTH = 10;
|
|
796
|
+
function validateTraversalSpec(spec, vocabulary, capabilities) {
|
|
797
|
+
const errors = [];
|
|
798
|
+
if (!spec.start) {
|
|
799
|
+
errors.push("start is required");
|
|
800
|
+
} else {
|
|
801
|
+
const hasEntityId = spec.start.entityId !== void 0 && spec.start.entityId !== "";
|
|
802
|
+
const hasEntityType = spec.start.entityType !== void 0 && spec.start.entityType !== "";
|
|
803
|
+
const hasFilter = spec.start.filter !== void 0 && spec.start.filter.length > 0;
|
|
804
|
+
if (!hasEntityId && !hasEntityType && !hasFilter) {
|
|
805
|
+
errors.push("start must have at least one of entityId, entityType, or filter");
|
|
806
|
+
}
|
|
807
|
+
if (hasEntityType && !hasEntityId && (spec.limit === void 0 || spec.limit === 0)) {
|
|
808
|
+
errors.push("start.entityType without entityId requires limit on the spec to prevent full type scans");
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (!spec.steps || spec.steps.length === 0) {
|
|
812
|
+
errors.push("steps must contain at least one step");
|
|
813
|
+
}
|
|
814
|
+
const maxSteps = capabilities?.maxTraversalDepth ?? DEFAULT_MAX_STEPS;
|
|
815
|
+
if (spec.steps && spec.steps.length > maxSteps) {
|
|
816
|
+
errors.push(`steps length ${spec.steps.length} exceeds maximum ${maxSteps}`);
|
|
817
|
+
}
|
|
818
|
+
if (spec.steps) {
|
|
819
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
820
|
+
const step = spec.steps[i];
|
|
821
|
+
if (!step.direction || !["out", "in", "both"].includes(step.direction)) {
|
|
822
|
+
errors.push(`steps[${i}].direction must be 'out', 'in', or 'both'`);
|
|
823
|
+
}
|
|
824
|
+
if (step.repeat) {
|
|
825
|
+
if (step.repeat.maxDepth === void 0 || step.repeat.maxDepth <= 0) {
|
|
826
|
+
errors.push(`steps[${i}].repeat.maxDepth must be a positive number`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const maxProviderDepth = capabilities?.maxTraversalDepth ?? DEFAULT_FALLBACK_MAX_DEPTH;
|
|
831
|
+
let totalDepth = 0;
|
|
832
|
+
for (const step of spec.steps) {
|
|
833
|
+
totalDepth += step.repeat ? step.repeat.maxDepth : 1;
|
|
834
|
+
}
|
|
835
|
+
if (totalDepth > maxProviderDepth) {
|
|
836
|
+
errors.push(`total potential depth ${totalDepth} exceeds provider maximum ${maxProviderDepth}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (spec.returnMode && !["terminal", "path", "all"].includes(spec.returnMode)) {
|
|
840
|
+
errors.push(`returnMode must be 'terminal', 'path', or 'all'`);
|
|
841
|
+
}
|
|
842
|
+
if (spec.limit !== void 0) {
|
|
843
|
+
if (spec.limit < 1 || spec.limit > DEFAULT_MAX_LIMIT) {
|
|
844
|
+
errors.push(`limit must be between 1 and ${DEFAULT_MAX_LIMIT}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (spec.offset !== void 0 && spec.offset < 0) {
|
|
848
|
+
errors.push("offset must be >= 0");
|
|
849
|
+
}
|
|
850
|
+
if (vocabulary && spec.steps) {
|
|
851
|
+
const entityTypeSet = new Set(vocabulary.entityTypes.map((et) => et.type));
|
|
852
|
+
const relationshipTypeSet = new Set(vocabulary.relationshipTypes.map((rt) => rt.type));
|
|
853
|
+
const unknownTypes = [];
|
|
854
|
+
if (spec.start?.entityType && !entityTypeSet.has(spec.start.entityType)) {
|
|
855
|
+
unknownTypes.push(`entity type "${spec.start.entityType}"`);
|
|
856
|
+
}
|
|
857
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
858
|
+
const step = spec.steps[i];
|
|
859
|
+
if (step.relationshipTypes) {
|
|
860
|
+
for (const rt of step.relationshipTypes) {
|
|
861
|
+
if (!relationshipTypeSet.has(rt)) {
|
|
862
|
+
unknownTypes.push(`relationship type "${rt}" in steps[${i}]`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
if (step.entityTypes) {
|
|
867
|
+
for (const et of step.entityTypes) {
|
|
868
|
+
if (!entityTypeSet.has(et)) {
|
|
869
|
+
unknownTypes.push(`entity type "${et}" in steps[${i}]`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (unknownTypes.length > 0) {
|
|
875
|
+
errors.push(`Unknown vocabulary types: ${unknownTypes.join(", ")}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return {
|
|
879
|
+
valid: errors.length === 0,
|
|
880
|
+
errors
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// src/relationships/compilers/GremlinCompiler.ts
|
|
885
|
+
var DEFAULT_ESTIMATED_FANOUT_PER_HOP = 10;
|
|
886
|
+
var GremlinCompiler = class {
|
|
887
|
+
language = "gremlin";
|
|
888
|
+
compile(spec, _vocabulary) {
|
|
889
|
+
const parts = [];
|
|
890
|
+
const params = {};
|
|
891
|
+
let paramIndex = 0;
|
|
892
|
+
let estimatedFanOut = 1;
|
|
893
|
+
const nextParam = (value) => {
|
|
894
|
+
const name = `p${paramIndex++}`;
|
|
895
|
+
params[name] = value;
|
|
896
|
+
return name;
|
|
897
|
+
};
|
|
898
|
+
parts.push("g.V()");
|
|
899
|
+
if (spec.start.entityId) {
|
|
900
|
+
const p = nextParam(spec.start.entityId);
|
|
901
|
+
parts.push(`.has('id', ${p})`);
|
|
902
|
+
} else if (spec.start.entityType) {
|
|
903
|
+
const p = nextParam(spec.start.entityType);
|
|
904
|
+
parts.push(`.has('entityType', ${p})`);
|
|
905
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
906
|
+
}
|
|
907
|
+
if (spec.start.filter) {
|
|
908
|
+
for (const f of spec.start.filter) {
|
|
909
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
for (const step of spec.steps) {
|
|
913
|
+
if (step.repeat) {
|
|
914
|
+
parts.push(compileRepeatStep(step, nextParam));
|
|
915
|
+
estimatedFanOut *= step.repeat.maxDepth * DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
916
|
+
} else if (step.relationshipFilter && step.relationshipFilter.length > 0) {
|
|
917
|
+
parts.push(compileEdgeStep(step, nextParam));
|
|
918
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
919
|
+
} else {
|
|
920
|
+
parts.push(compileSimpleStep(step, nextParam));
|
|
921
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP;
|
|
922
|
+
}
|
|
923
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
924
|
+
const typeParams = step.entityTypes.map((t) => nextParam(t));
|
|
925
|
+
parts.push(`.has('entityType', within(${typeParams.join(", ")}))`);
|
|
926
|
+
}
|
|
927
|
+
if (step.entityFilter) {
|
|
928
|
+
for (const f of step.entityFilter) {
|
|
929
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (spec.returnMode === "all") {
|
|
934
|
+
}
|
|
935
|
+
if (spec.dedup !== false) {
|
|
936
|
+
parts.push(".dedup()");
|
|
937
|
+
}
|
|
938
|
+
const limit = spec.limit ?? 50;
|
|
939
|
+
const offset = spec.offset ?? 0;
|
|
940
|
+
const pOffset = nextParam(offset);
|
|
941
|
+
const pEnd = nextParam(offset + limit);
|
|
942
|
+
parts.push(`.range(${pOffset}, ${pEnd})`);
|
|
943
|
+
params["_limit"] = limit;
|
|
944
|
+
params["_offset"] = offset;
|
|
945
|
+
if (spec.returnMode === "path") {
|
|
946
|
+
parts.push(".path()");
|
|
947
|
+
}
|
|
948
|
+
if (spec.returnMode !== "path") {
|
|
949
|
+
parts.push(".valueMap(true)");
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
query: parts.join(""),
|
|
953
|
+
params,
|
|
954
|
+
estimatedFanOut: Math.min(estimatedFanOut, 1e4)
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
function compileSimpleStep(step, nextParam) {
|
|
959
|
+
const types = step.relationshipTypes;
|
|
960
|
+
const typeArgs = types ? types.map((t) => nextParam(t)).join(", ") : "";
|
|
961
|
+
switch (step.direction) {
|
|
962
|
+
case "out":
|
|
963
|
+
return types ? `.out(${typeArgs})` : ".out()";
|
|
964
|
+
case "in":
|
|
965
|
+
return types ? `.in(${typeArgs})` : ".in()";
|
|
966
|
+
case "both":
|
|
967
|
+
return types ? `.both(${typeArgs})` : ".both()";
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
function compileEdgeStep(step, nextParam) {
|
|
971
|
+
const parts = [];
|
|
972
|
+
const types = step.relationshipTypes;
|
|
973
|
+
const typeArgs = types ? types.map((t) => nextParam(t)).join(", ") : "";
|
|
974
|
+
switch (step.direction) {
|
|
975
|
+
case "out":
|
|
976
|
+
parts.push(types ? `.outE(${typeArgs})` : ".outE()");
|
|
977
|
+
break;
|
|
978
|
+
case "in":
|
|
979
|
+
parts.push(types ? `.inE(${typeArgs})` : ".inE()");
|
|
980
|
+
break;
|
|
981
|
+
case "both":
|
|
982
|
+
parts.push(types ? `.bothE(${typeArgs})` : ".bothE()");
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
if (step.relationshipFilter) {
|
|
986
|
+
for (const f of step.relationshipFilter) {
|
|
987
|
+
parts.push(compilePropertyFilter(f, nextParam));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
switch (step.direction) {
|
|
991
|
+
case "out":
|
|
992
|
+
parts.push(".inV()");
|
|
993
|
+
break;
|
|
994
|
+
case "in":
|
|
995
|
+
parts.push(".outV()");
|
|
996
|
+
break;
|
|
997
|
+
case "both":
|
|
998
|
+
parts.push(".otherV()");
|
|
999
|
+
break;
|
|
1000
|
+
}
|
|
1001
|
+
return parts.join("");
|
|
1002
|
+
}
|
|
1003
|
+
function compileRepeatStep(step, nextParam) {
|
|
1004
|
+
const parts = [];
|
|
1005
|
+
const innerStep = step.relationshipFilter?.length ? compileEdgeStep({ ...step, repeat: void 0 }, nextParam) : compileSimpleStep(step, nextParam);
|
|
1006
|
+
if (step.repeat?.emitIntermediates !== false) {
|
|
1007
|
+
parts.push(".emit()");
|
|
1008
|
+
}
|
|
1009
|
+
parts.push(`.repeat(${innerStep.startsWith(".") ? `__${innerStep}` : innerStep})`);
|
|
1010
|
+
if (step.repeat?.until && step.repeat.until.length > 0) {
|
|
1011
|
+
const untilParts = step.repeat.until.map((f) => compilePropertyFilter(f, nextParam));
|
|
1012
|
+
parts.push(`.until(${untilParts.join("")})`);
|
|
1013
|
+
}
|
|
1014
|
+
if (step.repeat?.maxDepth) {
|
|
1015
|
+
const p = nextParam(step.repeat.maxDepth);
|
|
1016
|
+
parts.push(`.times(${p})`);
|
|
1017
|
+
}
|
|
1018
|
+
if (step.repeat?.emitIntermediates === false) {
|
|
1019
|
+
parts.push(".emit()");
|
|
1020
|
+
}
|
|
1021
|
+
return parts.join("");
|
|
1022
|
+
}
|
|
1023
|
+
function compilePropertyFilter(filter, nextParam) {
|
|
1024
|
+
const key = nextParam(filter.key);
|
|
1025
|
+
switch (filter.operator) {
|
|
1026
|
+
case "eq": {
|
|
1027
|
+
const val = nextParam(filter.value);
|
|
1028
|
+
return `.has(${key}, ${val})`;
|
|
1029
|
+
}
|
|
1030
|
+
case "neq": {
|
|
1031
|
+
const val = nextParam(filter.value);
|
|
1032
|
+
return `.has(${key}, neq(${val}))`;
|
|
1033
|
+
}
|
|
1034
|
+
case "gt": {
|
|
1035
|
+
const val = nextParam(filter.value);
|
|
1036
|
+
return `.has(${key}, gt(${val}))`;
|
|
1037
|
+
}
|
|
1038
|
+
case "gte": {
|
|
1039
|
+
const val = nextParam(filter.value);
|
|
1040
|
+
return `.has(${key}, gte(${val}))`;
|
|
1041
|
+
}
|
|
1042
|
+
case "lt": {
|
|
1043
|
+
const val = nextParam(filter.value);
|
|
1044
|
+
return `.has(${key}, lt(${val}))`;
|
|
1045
|
+
}
|
|
1046
|
+
case "lte": {
|
|
1047
|
+
const val = nextParam(filter.value);
|
|
1048
|
+
return `.has(${key}, lte(${val}))`;
|
|
1049
|
+
}
|
|
1050
|
+
case "contains": {
|
|
1051
|
+
const val = nextParam(filter.value);
|
|
1052
|
+
return `.has(${key}, containing(${val}))`;
|
|
1053
|
+
}
|
|
1054
|
+
case "isNull":
|
|
1055
|
+
return `.hasNot(${key})`;
|
|
1056
|
+
case "isNotNull":
|
|
1057
|
+
return `.has(${key})`;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// src/relationships/compilers/CypherCompiler.ts
|
|
1062
|
+
var DEFAULT_ESTIMATED_FANOUT_PER_HOP2 = 10;
|
|
1063
|
+
var CypherCompiler = class {
|
|
1064
|
+
language = "cypher";
|
|
1065
|
+
compile(spec, _vocabulary) {
|
|
1066
|
+
const params = {};
|
|
1067
|
+
let paramIndex = 0;
|
|
1068
|
+
let estimatedFanOut = 1;
|
|
1069
|
+
const whereClauses = [];
|
|
1070
|
+
let nodeIndex = 0;
|
|
1071
|
+
const nextParam = (value) => {
|
|
1072
|
+
const name = `p${paramIndex++}`;
|
|
1073
|
+
params[name] = value;
|
|
1074
|
+
return `$${name}`;
|
|
1075
|
+
};
|
|
1076
|
+
const startNode = `n${nodeIndex++}`;
|
|
1077
|
+
let matchParts = [];
|
|
1078
|
+
if (spec.start.entityId) {
|
|
1079
|
+
matchParts.push(`(${startNode})`);
|
|
1080
|
+
whereClauses.push(`${startNode}.id = ${nextParam(spec.start.entityId)}`);
|
|
1081
|
+
} else if (spec.start.entityType) {
|
|
1082
|
+
matchParts.push(`(${startNode})`);
|
|
1083
|
+
whereClauses.push(`${startNode}.entityType = ${nextParam(spec.start.entityType)}`);
|
|
1084
|
+
estimatedFanOut *= DEFAULT_ESTIMATED_FANOUT_PER_HOP2;
|
|
1085
|
+
} else {
|
|
1086
|
+
matchParts.push(`(${startNode})`);
|
|
1087
|
+
}
|
|
1088
|
+
if (spec.start.filter) {
|
|
1089
|
+
for (const f of spec.start.filter) {
|
|
1090
|
+
whereClauses.push(compilePropertyFilterCypher(startNode, f, nextParam));
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
let lastNode = startNode;
|
|
1094
|
+
for (let i = 0; i < spec.steps.length; i++) {
|
|
1095
|
+
const step = spec.steps[i];
|
|
1096
|
+
const targetNode = `n${nodeIndex++}`;
|
|
1097
|
+
const relAlias = `r${i}`;
|
|
1098
|
+
const relTypes = step.relationshipTypes?.length ? step.relationshipTypes.join("|") : "";
|
|
1099
|
+
const relTypePattern = relTypes ? `:${relTypes}` : "";
|
|
1100
|
+
const depthPattern = step.repeat ? `*1..${step.repeat.maxDepth}` : "";
|
|
1101
|
+
let pattern;
|
|
1102
|
+
switch (step.direction) {
|
|
1103
|
+
case "out":
|
|
1104
|
+
pattern = `-[${relAlias}${relTypePattern}${depthPattern}]->(${targetNode})`;
|
|
1105
|
+
break;
|
|
1106
|
+
case "in":
|
|
1107
|
+
pattern = `<-[${relAlias}${relTypePattern}${depthPattern}]-(${targetNode})`;
|
|
1108
|
+
break;
|
|
1109
|
+
case "both":
|
|
1110
|
+
pattern = `-[${relAlias}${relTypePattern}${depthPattern}]-(${targetNode})`;
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
matchParts.push(pattern);
|
|
1114
|
+
estimatedFanOut *= step.repeat ? step.repeat.maxDepth * DEFAULT_ESTIMATED_FANOUT_PER_HOP2 : DEFAULT_ESTIMATED_FANOUT_PER_HOP2;
|
|
1115
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
1116
|
+
const typeParam = nextParam(step.entityTypes);
|
|
1117
|
+
whereClauses.push(`${targetNode}.entityType IN ${typeParam}`);
|
|
1118
|
+
}
|
|
1119
|
+
if (step.entityFilter) {
|
|
1120
|
+
for (const f of step.entityFilter) {
|
|
1121
|
+
whereClauses.push(compilePropertyFilterCypher(targetNode, f, nextParam));
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (step.relationshipFilter) {
|
|
1125
|
+
for (const f of step.relationshipFilter) {
|
|
1126
|
+
whereClauses.push(compilePropertyFilterCypher(relAlias, f, nextParam));
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
lastNode = targetNode;
|
|
1130
|
+
}
|
|
1131
|
+
const matchClause = `MATCH ${matchParts[0]}${matchParts.slice(1).join("")}`;
|
|
1132
|
+
const whereClause = whereClauses.length > 0 ? `
|
|
1133
|
+
WHERE ${whereClauses.join(" AND ")}` : "";
|
|
1134
|
+
let returnClause;
|
|
1135
|
+
if (spec.returnMode === "path") {
|
|
1136
|
+
const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
|
|
1137
|
+
const allRels = spec.steps.map((_, i) => `r${i}`);
|
|
1138
|
+
returnClause = `RETURN ${[...allNodes, ...allRels].join(", ")}`;
|
|
1139
|
+
} else if (spec.returnMode === "all") {
|
|
1140
|
+
const allNodes = Array.from({ length: nodeIndex }, (_, i) => `n${i}`);
|
|
1141
|
+
returnClause = spec.dedup !== false ? `RETURN DISTINCT ${allNodes.join(", ")}` : `RETURN ${allNodes.join(", ")}`;
|
|
1142
|
+
} else {
|
|
1143
|
+
returnClause = spec.dedup !== false ? `RETURN DISTINCT ${lastNode}` : `RETURN ${lastNode}`;
|
|
1144
|
+
}
|
|
1145
|
+
const offset = spec.offset ?? 0;
|
|
1146
|
+
const limit = spec.limit ?? 50;
|
|
1147
|
+
const skipClause = offset > 0 ? `
|
|
1148
|
+
SKIP ${nextParam(offset)}` : "";
|
|
1149
|
+
const limitClause = `
|
|
1150
|
+
LIMIT ${nextParam(limit)}`;
|
|
1151
|
+
params["_limit"] = limit;
|
|
1152
|
+
params["_offset"] = offset;
|
|
1153
|
+
const query = `${matchClause}${whereClause}
|
|
1154
|
+
${returnClause}${skipClause}${limitClause}`;
|
|
1155
|
+
return {
|
|
1156
|
+
query,
|
|
1157
|
+
params,
|
|
1158
|
+
estimatedFanOut: Math.min(estimatedFanOut, 1e4)
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
};
|
|
1162
|
+
function compilePropertyFilterCypher(nodeAlias, filter, nextParam) {
|
|
1163
|
+
const prop = `${nodeAlias}.${filter.key}`;
|
|
1164
|
+
switch (filter.operator) {
|
|
1165
|
+
case "eq":
|
|
1166
|
+
return `${prop} = ${nextParam(filter.value)}`;
|
|
1167
|
+
case "neq":
|
|
1168
|
+
return `${prop} <> ${nextParam(filter.value)}`;
|
|
1169
|
+
case "gt":
|
|
1170
|
+
return `${prop} > ${nextParam(filter.value)}`;
|
|
1171
|
+
case "gte":
|
|
1172
|
+
return `${prop} >= ${nextParam(filter.value)}`;
|
|
1173
|
+
case "lt":
|
|
1174
|
+
return `${prop} < ${nextParam(filter.value)}`;
|
|
1175
|
+
case "lte":
|
|
1176
|
+
return `${prop} <= ${nextParam(filter.value)}`;
|
|
1177
|
+
case "contains":
|
|
1178
|
+
return `${prop} CONTAINS ${nextParam(filter.value)}`;
|
|
1179
|
+
case "isNull":
|
|
1180
|
+
return `${prop} IS NULL`;
|
|
1181
|
+
case "isNotNull":
|
|
1182
|
+
return `${prop} IS NOT NULL`;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/relationships/PropertyFilterMatcher.ts
|
|
1187
|
+
function matchesPropertyFilters(properties, filters) {
|
|
1188
|
+
return filters.every((filter) => matchesSingleFilter(properties, filter));
|
|
1189
|
+
}
|
|
1190
|
+
function matchesSingleFilter(properties, filter) {
|
|
1191
|
+
const value = properties[filter.key];
|
|
1192
|
+
switch (filter.operator) {
|
|
1193
|
+
case "isNull":
|
|
1194
|
+
return value === null || value === void 0;
|
|
1195
|
+
case "isNotNull":
|
|
1196
|
+
return value !== null && value !== void 0;
|
|
1197
|
+
case "eq":
|
|
1198
|
+
return value === filter.value;
|
|
1199
|
+
case "neq":
|
|
1200
|
+
return value !== filter.value;
|
|
1201
|
+
case "gt":
|
|
1202
|
+
return typeof value === "number" && typeof filter.value === "number" ? value > filter.value : typeof value === "string" && typeof filter.value === "string" ? value > filter.value : false;
|
|
1203
|
+
case "lt":
|
|
1204
|
+
return typeof value === "number" && typeof filter.value === "number" ? value < filter.value : typeof value === "string" && typeof filter.value === "string" ? value < filter.value : false;
|
|
1205
|
+
case "gte":
|
|
1206
|
+
return typeof value === "number" && typeof filter.value === "number" ? value >= filter.value : typeof value === "string" && typeof filter.value === "string" ? value >= filter.value : false;
|
|
1207
|
+
case "lte":
|
|
1208
|
+
return typeof value === "number" && typeof filter.value === "number" ? value <= filter.value : typeof value === "string" && typeof filter.value === "string" ? value <= filter.value : false;
|
|
1209
|
+
case "contains":
|
|
1210
|
+
return typeof value === "string" && typeof filter.value === "string" ? value.toLowerCase().includes(filter.value.toLowerCase()) : false;
|
|
1211
|
+
default:
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/relationships/FallbackTraversalExecutor.ts
|
|
1217
|
+
var MAX_STORAGE_CALLS = 500;
|
|
1218
|
+
var REL_FETCH_LIMIT = 1e3;
|
|
1219
|
+
async function executeFallbackTraversal(repositoryId, storage, spec) {
|
|
1220
|
+
const startTime = Date.now();
|
|
1221
|
+
let storageCalls = 0;
|
|
1222
|
+
const checkCallLimit = () => {
|
|
1223
|
+
if (storageCalls >= MAX_STORAGE_CALLS) {
|
|
1224
|
+
throw new Error(`Fallback traversal exceeded ${MAX_STORAGE_CALLS} storage calls. Consider using a GraphTraversalProvider for large traversals.`);
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
let frontier = [];
|
|
1228
|
+
if (spec.start.entityId) {
|
|
1229
|
+
const entity = await resolveEntity(repositoryId, storage, spec.start.entityId);
|
|
1230
|
+
storageCalls++;
|
|
1231
|
+
if (entity) {
|
|
1232
|
+
frontier.push({ entity, entityPath: [entity.id], relationshipPath: [] });
|
|
1233
|
+
} else {
|
|
1234
|
+
throw new EntityNotFoundError(spec.start.entityId);
|
|
1235
|
+
}
|
|
1236
|
+
} else if (spec.start.entityType) {
|
|
1237
|
+
const result = await storage.findEntities(repositoryId, {
|
|
1238
|
+
entityTypes: [spec.start.entityType],
|
|
1239
|
+
limit: spec.limit ?? 50,
|
|
1240
|
+
offset: 0
|
|
1241
|
+
});
|
|
1242
|
+
storageCalls++;
|
|
1243
|
+
frontier = result.items.map((e) => ({
|
|
1244
|
+
entity: e,
|
|
1245
|
+
entityPath: [e.id],
|
|
1246
|
+
relationshipPath: []
|
|
1247
|
+
}));
|
|
1248
|
+
} else if (spec.start.filter && spec.start.filter.length > 0) {
|
|
1249
|
+
const result = await storage.findEntities(repositoryId, {
|
|
1250
|
+
limit: spec.limit ?? 50,
|
|
1251
|
+
offset: 0
|
|
1252
|
+
});
|
|
1253
|
+
storageCalls++;
|
|
1254
|
+
frontier = result.items.filter((e) => matchesPropertyFilters(e.properties, spec.start.filter)).map((e) => ({
|
|
1255
|
+
entity: e,
|
|
1256
|
+
entityPath: [e.id],
|
|
1257
|
+
relationshipPath: []
|
|
1258
|
+
}));
|
|
1259
|
+
}
|
|
1260
|
+
if (spec.start.filter && spec.start.filter.length > 0 && (spec.start.entityId || spec.start.entityType)) {
|
|
1261
|
+
frontier = frontier.filter(
|
|
1262
|
+
(entry) => matchesPropertyFilters(entry.entity.properties, spec.start.filter)
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
const allCollected = spec.returnMode === "all" ? [...frontier] : [];
|
|
1266
|
+
for (const step of spec.steps) {
|
|
1267
|
+
checkCallLimit();
|
|
1268
|
+
if (step.repeat) {
|
|
1269
|
+
const result = await executeRepeatStep(
|
|
1270
|
+
repositoryId,
|
|
1271
|
+
storage,
|
|
1272
|
+
frontier,
|
|
1273
|
+
step,
|
|
1274
|
+
() => {
|
|
1275
|
+
storageCalls++;
|
|
1276
|
+
checkCallLimit();
|
|
1277
|
+
}
|
|
1278
|
+
);
|
|
1279
|
+
frontier = result.terminal;
|
|
1280
|
+
if (spec.returnMode === "all") {
|
|
1281
|
+
allCollected.push(...result.intermediates);
|
|
1282
|
+
}
|
|
1283
|
+
} else {
|
|
1284
|
+
frontier = await executeSingleStep(
|
|
1285
|
+
repositoryId,
|
|
1286
|
+
storage,
|
|
1287
|
+
frontier,
|
|
1288
|
+
step,
|
|
1289
|
+
() => {
|
|
1290
|
+
storageCalls++;
|
|
1291
|
+
checkCallLimit();
|
|
1292
|
+
}
|
|
1293
|
+
);
|
|
1294
|
+
if (spec.returnMode === "all") {
|
|
1295
|
+
allCollected.push(...frontier);
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const detailLevel = spec.detailLevel ?? "summary";
|
|
1300
|
+
const dedup = spec.dedup !== false;
|
|
1301
|
+
const limit = spec.limit ?? 50;
|
|
1302
|
+
const offset = spec.offset ?? 0;
|
|
1303
|
+
let resultEntries;
|
|
1304
|
+
if (spec.returnMode === "all") {
|
|
1305
|
+
resultEntries = allCollected;
|
|
1306
|
+
} else {
|
|
1307
|
+
resultEntries = frontier;
|
|
1308
|
+
}
|
|
1309
|
+
if (dedup) {
|
|
1310
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1311
|
+
resultEntries = resultEntries.filter((entry) => {
|
|
1312
|
+
if (seen.has(entry.entity.id)) return false;
|
|
1313
|
+
seen.add(entry.entity.id);
|
|
1314
|
+
return true;
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
const total = resultEntries.length;
|
|
1318
|
+
const paged = resultEntries.slice(offset, offset + limit);
|
|
1319
|
+
const entities = paged.map((entry) => projectEntity(entry.entity, detailLevel));
|
|
1320
|
+
let relationships;
|
|
1321
|
+
let paths;
|
|
1322
|
+
if (spec.returnMode === "path" || spec.returnMode === "all") {
|
|
1323
|
+
const relMap = /* @__PURE__ */ new Map();
|
|
1324
|
+
for (const entry of paged) {
|
|
1325
|
+
for (const rel of entry.relationshipPath) {
|
|
1326
|
+
if (!relMap.has(rel.id)) {
|
|
1327
|
+
relMap.set(rel.id, {
|
|
1328
|
+
id: rel.id,
|
|
1329
|
+
type: rel.relationshipType,
|
|
1330
|
+
sourceEntityId: rel.sourceEntityId,
|
|
1331
|
+
targetEntityId: rel.targetEntityId,
|
|
1332
|
+
direction: "outbound",
|
|
1333
|
+
properties: rel.properties
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
relationships = Array.from(relMap.values());
|
|
1339
|
+
}
|
|
1340
|
+
if (spec.returnMode === "path") {
|
|
1341
|
+
paths = paged.map((entry) => ({
|
|
1342
|
+
length: entry.entityPath.length - 1,
|
|
1343
|
+
entities: [projectEntity(entry.entity, detailLevel)],
|
|
1344
|
+
relationships: entry.relationshipPath.map((rel) => ({
|
|
1345
|
+
id: rel.id,
|
|
1346
|
+
type: rel.relationshipType,
|
|
1347
|
+
sourceEntityId: rel.sourceEntityId,
|
|
1348
|
+
targetEntityId: rel.targetEntityId,
|
|
1349
|
+
direction: "outbound",
|
|
1350
|
+
properties: rel.properties
|
|
1351
|
+
}))
|
|
1352
|
+
}));
|
|
1353
|
+
}
|
|
1354
|
+
const executionTimeMs = Date.now() - startTime;
|
|
1355
|
+
return {
|
|
1356
|
+
entities,
|
|
1357
|
+
relationships,
|
|
1358
|
+
paths,
|
|
1359
|
+
total,
|
|
1360
|
+
returned: paged.length,
|
|
1361
|
+
hasMore: offset + limit < total,
|
|
1362
|
+
queryMetadata: {
|
|
1363
|
+
executionTimeMs,
|
|
1364
|
+
appliedLimits: {
|
|
1365
|
+
maxResults: limit
|
|
1366
|
+
},
|
|
1367
|
+
truncated: total > limit + offset,
|
|
1368
|
+
truncationReason: total > limit + offset ? "result_limit" : void 0
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
async function executeSingleStep(repositoryId, storage, frontier, step, onStorageCall) {
|
|
1373
|
+
const direction = mapDirection(step.direction);
|
|
1374
|
+
const pendingEdges = [];
|
|
1375
|
+
for (const entry of frontier) {
|
|
1376
|
+
const rels = await storage.getEntityRelationships(repositoryId, entry.entity.id, {
|
|
1377
|
+
direction,
|
|
1378
|
+
relationshipTypes: step.relationshipTypes,
|
|
1379
|
+
limit: REL_FETCH_LIMIT
|
|
1380
|
+
});
|
|
1381
|
+
onStorageCall();
|
|
1382
|
+
for (const rel of rels.items) {
|
|
1383
|
+
if (step.relationshipFilter && step.relationshipFilter.length > 0) {
|
|
1384
|
+
if (!matchesPropertyFilters(rel.properties, step.relationshipFilter)) {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
const targetId = getTargetId(rel, entry.entity.id);
|
|
1389
|
+
pendingEdges.push({ entry, rel, targetId });
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
if (pendingEdges.length === 0) return [];
|
|
1393
|
+
const uniqueTargetIds = [...new Set(pendingEdges.map((e) => e.targetId))];
|
|
1394
|
+
const entityMap = await storage.getEntities(repositoryId, uniqueTargetIds);
|
|
1395
|
+
onStorageCall();
|
|
1396
|
+
const nextFrontier = [];
|
|
1397
|
+
for (const { entry, rel, targetId } of pendingEdges) {
|
|
1398
|
+
const targetEntity = entityMap.get(targetId);
|
|
1399
|
+
if (!targetEntity) continue;
|
|
1400
|
+
if (step.entityTypes && step.entityTypes.length > 0) {
|
|
1401
|
+
if (!step.entityTypes.includes(targetEntity.entityType)) continue;
|
|
1402
|
+
}
|
|
1403
|
+
if (step.entityFilter && step.entityFilter.length > 0) {
|
|
1404
|
+
if (!matchesPropertyFilters(targetEntity.properties, step.entityFilter)) continue;
|
|
1405
|
+
}
|
|
1406
|
+
nextFrontier.push({
|
|
1407
|
+
entity: targetEntity,
|
|
1408
|
+
entityPath: [...entry.entityPath, targetEntity.id],
|
|
1409
|
+
relationshipPath: [...entry.relationshipPath, rel]
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
return nextFrontier;
|
|
1413
|
+
}
|
|
1414
|
+
async function executeRepeatStep(repositoryId, storage, frontier, step, onStorageCall) {
|
|
1415
|
+
const maxDepth = step.repeat?.maxDepth ?? 1;
|
|
1416
|
+
const emitIntermediates = step.repeat?.emitIntermediates !== false;
|
|
1417
|
+
const untilFilters = step.repeat?.until;
|
|
1418
|
+
let current = frontier;
|
|
1419
|
+
const allIntermediates = [];
|
|
1420
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1421
|
+
for (const entry of frontier) {
|
|
1422
|
+
visited.add(entry.entity.id);
|
|
1423
|
+
}
|
|
1424
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1425
|
+
const next = await executeSingleStep(repositoryId, storage, current, step, onStorageCall);
|
|
1426
|
+
const unvisited = next.filter((entry) => {
|
|
1427
|
+
if (visited.has(entry.entity.id)) return false;
|
|
1428
|
+
visited.add(entry.entity.id);
|
|
1429
|
+
return true;
|
|
1430
|
+
});
|
|
1431
|
+
if (unvisited.length === 0) break;
|
|
1432
|
+
if (untilFilters && untilFilters.length > 0) {
|
|
1433
|
+
const matching = [];
|
|
1434
|
+
const continuing = [];
|
|
1435
|
+
for (const entry of unvisited) {
|
|
1436
|
+
if (matchesPropertyFilters(entry.entity.properties, untilFilters)) {
|
|
1437
|
+
matching.push(entry);
|
|
1438
|
+
} else {
|
|
1439
|
+
continuing.push(entry);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (emitIntermediates) {
|
|
1443
|
+
allIntermediates.push(...continuing);
|
|
1444
|
+
}
|
|
1445
|
+
allIntermediates.push(...matching);
|
|
1446
|
+
if (matching.length > 0 && continuing.length === 0) {
|
|
1447
|
+
current = matching;
|
|
1448
|
+
break;
|
|
1449
|
+
}
|
|
1450
|
+
current = continuing;
|
|
1451
|
+
} else {
|
|
1452
|
+
if (emitIntermediates) {
|
|
1453
|
+
allIntermediates.push(...unvisited);
|
|
1454
|
+
}
|
|
1455
|
+
current = unvisited;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
return {
|
|
1459
|
+
terminal: current,
|
|
1460
|
+
intermediates: allIntermediates
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
async function resolveEntity(repositoryId, storage, idOrSlug) {
|
|
1464
|
+
const byId = await storage.getEntity(repositoryId, idOrSlug);
|
|
1465
|
+
if (byId) return byId;
|
|
1466
|
+
return storage.getEntityBySlug(repositoryId, idOrSlug);
|
|
1467
|
+
}
|
|
1468
|
+
function mapDirection(direction) {
|
|
1469
|
+
switch (direction) {
|
|
1470
|
+
case "out":
|
|
1471
|
+
return "outbound";
|
|
1472
|
+
case "in":
|
|
1473
|
+
return "inbound";
|
|
1474
|
+
case "both":
|
|
1475
|
+
return "both";
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function getTargetId(rel, currentEntityId) {
|
|
1479
|
+
if (rel.sourceEntityId === currentEntityId) {
|
|
1480
|
+
return rel.targetEntityId;
|
|
1481
|
+
}
|
|
1482
|
+
return rel.sourceEntityId;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
738
1485
|
// src/relationships/GraphTraversal.ts
|
|
739
1486
|
var GraphTraversal = class {
|
|
740
|
-
constructor(repositoryId, storage) {
|
|
1487
|
+
constructor(repositoryId, storage, graphTraversalProvider, vocabularyEngine) {
|
|
741
1488
|
this.repositoryId = repositoryId;
|
|
742
1489
|
this.storage = storage;
|
|
1490
|
+
this.graphTraversalProvider = graphTraversalProvider;
|
|
1491
|
+
this.vocabularyEngine = vocabularyEngine;
|
|
743
1492
|
}
|
|
744
1493
|
/** BFS neighbourhood exploration from a centre entity */
|
|
745
1494
|
async exploreNeighbourhood(entityId, options) {
|
|
@@ -871,6 +1620,51 @@ var GraphTraversal = class {
|
|
|
871
1620
|
}))
|
|
872
1621
|
};
|
|
873
1622
|
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Execute a structured traversal.
|
|
1625
|
+
* Validates the spec, compiles to native query if a provider exists,
|
|
1626
|
+
* falls back to application-level BFS otherwise.
|
|
1627
|
+
*/
|
|
1628
|
+
async traverse(spec) {
|
|
1629
|
+
const capabilities = this.graphTraversalProvider?.getCapabilities();
|
|
1630
|
+
const vocabulary = this.vocabularyEngine ? await this.vocabularyEngine.getVocabulary() : void 0;
|
|
1631
|
+
const validation = validateTraversalSpec(spec, vocabulary, capabilities);
|
|
1632
|
+
if (!validation.valid) {
|
|
1633
|
+
const vocabErrors = validation.errors.filter((e) => e.startsWith("Unknown vocabulary types:"));
|
|
1634
|
+
const structErrors = validation.errors.filter((e) => !e.startsWith("Unknown vocabulary types:"));
|
|
1635
|
+
if (vocabErrors.length > 0) {
|
|
1636
|
+
const unknownTypes = vocabErrors.join("; ").replace(/Unknown vocabulary types: /g, "").split(", ").map((t) => t.trim());
|
|
1637
|
+
throw new TraversalVocabularyError(unknownTypes);
|
|
1638
|
+
}
|
|
1639
|
+
throw new TraversalValidationError(structErrors.length > 0 ? structErrors : validation.errors);
|
|
1640
|
+
}
|
|
1641
|
+
if (this.graphTraversalProvider && vocabulary) {
|
|
1642
|
+
const language = capabilities.nativeQueryLanguage;
|
|
1643
|
+
let compiledQuery;
|
|
1644
|
+
if (language === "gremlin") {
|
|
1645
|
+
const compiler = new GremlinCompiler();
|
|
1646
|
+
const compiled = compiler.compile(spec, vocabulary);
|
|
1647
|
+
compiledQuery = compiled.query;
|
|
1648
|
+
} else if (language === "cypher") {
|
|
1649
|
+
const compiler = new CypherCompiler();
|
|
1650
|
+
const compiled = compiler.compile(spec, vocabulary);
|
|
1651
|
+
compiledQuery = compiled.query;
|
|
1652
|
+
}
|
|
1653
|
+
return this.graphTraversalProvider.traverse(this.repositoryId, spec, compiledQuery);
|
|
1654
|
+
}
|
|
1655
|
+
return executeFallbackTraversal(this.repositoryId, this.storage, spec);
|
|
1656
|
+
}
|
|
1657
|
+
/**
|
|
1658
|
+
* Execute a native graph query.
|
|
1659
|
+
* Pass-through to GraphTraversalProvider.executeNativeQuery().
|
|
1660
|
+
* Throws GraphTraversalProviderRequiredError if no provider registered.
|
|
1661
|
+
*/
|
|
1662
|
+
async executeNativeQuery(query, params) {
|
|
1663
|
+
if (!this.graphTraversalProvider) {
|
|
1664
|
+
throw new GraphTraversalProviderRequiredError();
|
|
1665
|
+
}
|
|
1666
|
+
return this.graphTraversalProvider.executeNativeQuery(this.repositoryId, query, params);
|
|
1667
|
+
}
|
|
874
1668
|
};
|
|
875
1669
|
|
|
876
1670
|
// src/search/SearchOrchestrator.ts
|
|
@@ -1079,7 +1873,9 @@ var MemoryRepository = class {
|
|
|
1079
1873
|
);
|
|
1080
1874
|
this.graphTraversal = new GraphTraversal(
|
|
1081
1875
|
config.repositoryId,
|
|
1082
|
-
config.storage
|
|
1876
|
+
config.storage,
|
|
1877
|
+
config.graphTraversal,
|
|
1878
|
+
config.vocabularyEngine
|
|
1083
1879
|
);
|
|
1084
1880
|
this.searchOrchestrator = new SearchOrchestrator({
|
|
1085
1881
|
repositoryId: config.repositoryId,
|
|
@@ -1103,19 +1899,21 @@ var MemoryRepository = class {
|
|
|
1103
1899
|
return this.proposeVocabularyChange(proposal);
|
|
1104
1900
|
}
|
|
1105
1901
|
// ─── Entities ──────────────────────────────────────────────────────
|
|
1106
|
-
async
|
|
1107
|
-
const
|
|
1902
|
+
async createEntities(inputs) {
|
|
1903
|
+
const entities = await this.entityManager.create(inputs);
|
|
1108
1904
|
if (this.search) {
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1905
|
+
for (const entity of entities) {
|
|
1906
|
+
await this.search.indexEntity(this.repositoryId, {
|
|
1907
|
+
entityId: entity.id,
|
|
1908
|
+
entityType: entity.entityType,
|
|
1909
|
+
label: entity.label,
|
|
1910
|
+
summary: entity.summary,
|
|
1911
|
+
properties: entity.properties,
|
|
1912
|
+
data: entity.data
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1117
1915
|
}
|
|
1118
|
-
return
|
|
1916
|
+
return entities;
|
|
1119
1917
|
}
|
|
1120
1918
|
async updateEntity(entityId, updates) {
|
|
1121
1919
|
const entity = await this.entityManager.update(entityId, updates);
|
|
@@ -1202,8 +2000,8 @@ var MemoryRepository = class {
|
|
|
1202
2000
|
}
|
|
1203
2001
|
}
|
|
1204
2002
|
// ─── Relationships ─────────────────────────────────────────────────
|
|
1205
|
-
async
|
|
1206
|
-
return this.relationshipManager.create(
|
|
2003
|
+
async createRelationships(inputs) {
|
|
2004
|
+
return this.relationshipManager.create(inputs);
|
|
1207
2005
|
}
|
|
1208
2006
|
async removeRelationship(relationshipId) {
|
|
1209
2007
|
return this.relationshipManager.remove(relationshipId);
|
|
@@ -1297,6 +2095,12 @@ var MemoryRepository = class {
|
|
|
1297
2095
|
async findPaths(sourceId, targetId, options) {
|
|
1298
2096
|
return this.graphTraversal.findPaths(sourceId, targetId, options);
|
|
1299
2097
|
}
|
|
2098
|
+
async traverse(spec) {
|
|
2099
|
+
return this.graphTraversal.traverse(spec);
|
|
2100
|
+
}
|
|
2101
|
+
async executeNativeQuery(query, params) {
|
|
2102
|
+
return this.graphTraversal.executeNativeQuery(query, params);
|
|
2103
|
+
}
|
|
1300
2104
|
// ─── Search ────────────────────────────────────────────────────────
|
|
1301
2105
|
async searchByConcept(query, options) {
|
|
1302
2106
|
return this.searchOrchestrator.searchByConcept(query, options);
|
|
@@ -2381,13 +3185,15 @@ var EventBus = class {
|
|
|
2381
3185
|
};
|
|
2382
3186
|
|
|
2383
3187
|
// src/portability/RepositoryExporter.ts
|
|
2384
|
-
var LIBRARY_VERSION = "0.1.0";
|
|
3188
|
+
var LIBRARY_VERSION = true ? "0.2.0" : "0.1.0";
|
|
2385
3189
|
var RepositoryExporter = class {
|
|
2386
3190
|
storage;
|
|
2387
3191
|
provenance;
|
|
3192
|
+
legal;
|
|
2388
3193
|
constructor(config) {
|
|
2389
3194
|
this.storage = config.storage;
|
|
2390
3195
|
this.provenance = config.provenance;
|
|
3196
|
+
this.legal = config.legal;
|
|
2391
3197
|
}
|
|
2392
3198
|
/**
|
|
2393
3199
|
* Stream a repository export as an async generator.
|
|
@@ -2421,6 +3227,9 @@ var RepositoryExporter = class {
|
|
|
2421
3227
|
relationshipTypeBreakdown: stats.relationshipTypeBreakdown
|
|
2422
3228
|
}
|
|
2423
3229
|
};
|
|
3230
|
+
if (this.legal) {
|
|
3231
|
+
manifest.legal = this.legal;
|
|
3232
|
+
}
|
|
2424
3233
|
if (repo.metadata?.embeddingModelId) {
|
|
2425
3234
|
manifest.embedding = {
|
|
2426
3235
|
modelId: repo.metadata.embeddingModelId,
|
|
@@ -2989,6 +3798,7 @@ var DeepMemory = class {
|
|
|
2989
3798
|
embedding;
|
|
2990
3799
|
/** Reserved for future distributed locking support */
|
|
2991
3800
|
lock;
|
|
3801
|
+
graphTraversalProvider;
|
|
2992
3802
|
provenance;
|
|
2993
3803
|
globalEventBus;
|
|
2994
3804
|
initialised = false;
|
|
@@ -2997,24 +3807,42 @@ var DeepMemory = class {
|
|
|
2997
3807
|
this.search = config.search;
|
|
2998
3808
|
this.embedding = config.embedding;
|
|
2999
3809
|
this.lock = config.lock;
|
|
3810
|
+
this.graphTraversalProvider = config.graphTraversal;
|
|
3000
3811
|
this.provenance = new ProvenanceTracker(config.provenance);
|
|
3001
3812
|
this.globalEventBus = new EventBus(config.provenance);
|
|
3002
3813
|
}
|
|
3003
|
-
/** Initialise the storage provider (call once before use) */
|
|
3814
|
+
/** Initialise the storage provider and optional providers (call once before use) */
|
|
3004
3815
|
async ensureInitialised() {
|
|
3005
3816
|
if (!this.initialised) {
|
|
3006
3817
|
if (this.storage.initialise) {
|
|
3007
3818
|
await this.storage.initialise();
|
|
3008
3819
|
}
|
|
3820
|
+
if (this.graphTraversalProvider?.initialise) {
|
|
3821
|
+
await this.graphTraversalProvider.initialise();
|
|
3822
|
+
}
|
|
3009
3823
|
this.initialised = true;
|
|
3010
3824
|
}
|
|
3011
3825
|
}
|
|
3012
3826
|
/** Create or migrate the storage schema. Call once at deployment / startup, not per-request. */
|
|
3013
3827
|
async ensureSchema() {
|
|
3014
|
-
|
|
3828
|
+
const noOpResult = {
|
|
3829
|
+
databaseCreated: false,
|
|
3830
|
+
schemaCreated: false,
|
|
3831
|
+
alreadyUpToDate: true,
|
|
3832
|
+
schemaVersion: 0
|
|
3833
|
+
};
|
|
3834
|
+
try {
|
|
3835
|
+
await this.ensureInitialised();
|
|
3836
|
+
} catch {
|
|
3837
|
+
}
|
|
3838
|
+
let result = noOpResult;
|
|
3015
3839
|
if (this.storage.ensureSchema) {
|
|
3016
|
-
await this.storage.ensureSchema();
|
|
3840
|
+
result = await this.storage.ensureSchema();
|
|
3841
|
+
}
|
|
3842
|
+
if (!this.initialised) {
|
|
3843
|
+
await this.ensureInitialised();
|
|
3017
3844
|
}
|
|
3845
|
+
return result;
|
|
3018
3846
|
}
|
|
3019
3847
|
validateRepositoryId(repositoryId) {
|
|
3020
3848
|
if (!isValidUuid(repositoryId)) {
|
|
@@ -3071,6 +3899,7 @@ var DeepMemory = class {
|
|
|
3071
3899
|
storage: this.storage,
|
|
3072
3900
|
search: this.search,
|
|
3073
3901
|
embedding: this.embedding,
|
|
3902
|
+
graphTraversal: this.graphTraversalProvider,
|
|
3074
3903
|
vocabularyEngine,
|
|
3075
3904
|
provenanceTracker: this.provenance,
|
|
3076
3905
|
eventBus
|
|
@@ -3102,6 +3931,7 @@ var DeepMemory = class {
|
|
|
3102
3931
|
storage: this.storage,
|
|
3103
3932
|
search: this.search,
|
|
3104
3933
|
embedding: this.embedding,
|
|
3934
|
+
graphTraversal: this.graphTraversalProvider,
|
|
3105
3935
|
vocabularyEngine,
|
|
3106
3936
|
provenanceTracker: this.provenance,
|
|
3107
3937
|
eventBus
|
|
@@ -3143,11 +3973,12 @@ var DeepMemory = class {
|
|
|
3143
3973
|
await this.globalEventBus.emit("repository:deleted", { repositoryId });
|
|
3144
3974
|
}
|
|
3145
3975
|
/** Export a repository to a portable archive */
|
|
3146
|
-
async exportRepository(repositoryId) {
|
|
3976
|
+
async exportRepository(repositoryId, options) {
|
|
3147
3977
|
await this.ensureInitialised();
|
|
3148
3978
|
const exporter = new RepositoryExporter({
|
|
3149
3979
|
storage: this.storage,
|
|
3150
|
-
provenance: this.provenance.getContext()
|
|
3980
|
+
provenance: this.provenance.getContext(),
|
|
3981
|
+
legal: options?.legal
|
|
3151
3982
|
});
|
|
3152
3983
|
const archive = await exporter.export(repositoryId);
|
|
3153
3984
|
await this.globalEventBus.emit("export:completed", {
|
|
@@ -3184,11 +4015,12 @@ var DeepMemory = class {
|
|
|
3184
4015
|
* Yields: manifest → vocabulary → entity chunks → relationship chunks.
|
|
3185
4016
|
* Use for large repositories to avoid loading everything into memory.
|
|
3186
4017
|
*/
|
|
3187
|
-
async *exportRepositoryStream(repositoryId) {
|
|
4018
|
+
async *exportRepositoryStream(repositoryId, options) {
|
|
3188
4019
|
await this.ensureInitialised();
|
|
3189
4020
|
const exporter = new RepositoryExporter({
|
|
3190
4021
|
storage: this.storage,
|
|
3191
|
-
provenance: this.provenance.getContext()
|
|
4022
|
+
provenance: this.provenance.getContext(),
|
|
4023
|
+
legal: options?.legal
|
|
3192
4024
|
});
|
|
3193
4025
|
await this.globalEventBus.emit("export:started", { repositoryId });
|
|
3194
4026
|
let entityCount = 0;
|
|
@@ -3245,6 +4077,9 @@ var DeepMemory = class {
|
|
|
3245
4077
|
/** Dispose of all resources */
|
|
3246
4078
|
async dispose() {
|
|
3247
4079
|
this.globalEventBus.removeAllListeners();
|
|
4080
|
+
if (this.graphTraversalProvider?.dispose) {
|
|
4081
|
+
await this.graphTraversalProvider.dispose();
|
|
4082
|
+
}
|
|
3248
4083
|
if (this.storage.dispose) {
|
|
3249
4084
|
await this.storage.dispose();
|
|
3250
4085
|
}
|
|
@@ -3252,36 +4087,6 @@ var DeepMemory = class {
|
|
|
3252
4087
|
}
|
|
3253
4088
|
};
|
|
3254
4089
|
|
|
3255
|
-
// src/relationships/PropertyFilterMatcher.ts
|
|
3256
|
-
function matchesPropertyFilters(properties, filters) {
|
|
3257
|
-
return filters.every((filter) => matchesSingleFilter(properties, filter));
|
|
3258
|
-
}
|
|
3259
|
-
function matchesSingleFilter(properties, filter) {
|
|
3260
|
-
const value = properties[filter.key];
|
|
3261
|
-
switch (filter.operator) {
|
|
3262
|
-
case "isNull":
|
|
3263
|
-
return value === null || value === void 0;
|
|
3264
|
-
case "isNotNull":
|
|
3265
|
-
return value !== null && value !== void 0;
|
|
3266
|
-
case "eq":
|
|
3267
|
-
return value === filter.value;
|
|
3268
|
-
case "neq":
|
|
3269
|
-
return value !== filter.value;
|
|
3270
|
-
case "gt":
|
|
3271
|
-
return typeof value === "number" && typeof filter.value === "number" ? value > filter.value : typeof value === "string" && typeof filter.value === "string" ? value > filter.value : false;
|
|
3272
|
-
case "lt":
|
|
3273
|
-
return typeof value === "number" && typeof filter.value === "number" ? value < filter.value : typeof value === "string" && typeof filter.value === "string" ? value < filter.value : false;
|
|
3274
|
-
case "gte":
|
|
3275
|
-
return typeof value === "number" && typeof filter.value === "number" ? value >= filter.value : typeof value === "string" && typeof filter.value === "string" ? value >= filter.value : false;
|
|
3276
|
-
case "lte":
|
|
3277
|
-
return typeof value === "number" && typeof filter.value === "number" ? value <= filter.value : typeof value === "string" && typeof filter.value === "string" ? value <= filter.value : false;
|
|
3278
|
-
case "contains":
|
|
3279
|
-
return typeof value === "string" && typeof filter.value === "string" ? value.toLowerCase().includes(filter.value.toLowerCase()) : false;
|
|
3280
|
-
default:
|
|
3281
|
-
return false;
|
|
3282
|
-
}
|
|
3283
|
-
}
|
|
3284
|
-
|
|
3285
4090
|
// src/providers-builtin/InMemoryStorageProvider.ts
|
|
3286
4091
|
var InMemoryStorageProvider = class {
|
|
3287
4092
|
stores = /* @__PURE__ */ new Map();
|
|
@@ -3976,6 +4781,7 @@ var NoOpEmbeddingProvider = class {
|
|
|
3976
4781
|
}
|
|
3977
4782
|
};
|
|
3978
4783
|
export {
|
|
4784
|
+
CypherCompiler,
|
|
3979
4785
|
DeepMemory,
|
|
3980
4786
|
DeepMemoryError,
|
|
3981
4787
|
DuplicateEntityError,
|
|
@@ -3985,6 +4791,8 @@ export {
|
|
|
3985
4791
|
EntityNotFoundError,
|
|
3986
4792
|
ExportError,
|
|
3987
4793
|
GovernanceDeniedError,
|
|
4794
|
+
GraphTraversalProviderRequiredError,
|
|
4795
|
+
GremlinCompiler,
|
|
3988
4796
|
ImportError,
|
|
3989
4797
|
InMemorySearchProvider,
|
|
3990
4798
|
InMemoryStorageProvider,
|
|
@@ -3996,6 +4804,9 @@ export {
|
|
|
3996
4804
|
RelationshipConstraintError,
|
|
3997
4805
|
RelationshipNotFoundError,
|
|
3998
4806
|
RepositoryNotFoundError,
|
|
4807
|
+
TraversalTimeoutError,
|
|
4808
|
+
TraversalValidationError,
|
|
4809
|
+
TraversalVocabularyError,
|
|
3999
4810
|
VocabularyValidationError,
|
|
4000
4811
|
generateId,
|
|
4001
4812
|
isValidUuid,
|