| 484 | // ---- Store implementation ---- |
| 485 | |
| 486 | export class LadybugGraphStore implements GraphStore { |
| 487 | private engine: LbugEngine; |
| 488 | private ready: Promise<void> | null = null; |
| 489 | private embedder: Embedder | null = null; |
| 490 | private sourceCache = new Map< |
| 491 | string, |
| 492 | { compressed: Uint8Array; path: string; binary?: boolean } |
| 493 | >(); |
| 494 | |
| 495 | // --- Write buffer --- |
| 496 | private pendingNodes: ImportBatchRequest['nodes'] = []; |
| 497 | private pendingRels: ImportBatchRequest['relationships'] = []; |
| 498 | private totalNodesBuffered = 0; |
| 499 | private totalRelsBuffered = 0; |
| 500 | |
| 501 | // --- Source text for FTS indexing (populated by storeSource, consumed by flush) --- |
| 502 | private sourceSnippets = new Map<string, string>(); |
| 503 | |
| 504 | // --- JS-side indexes --- |
| 505 | private bm25Index = new BM25Index(1.5, 0.75, { name: 2.0 }, 1); |
| 506 | /** Whether the LadybugDB vector index has been created for the current session. */ |
| 507 | private hasVectorIndex = false; |
| 508 | |
| 509 | /** Maps node ID → typed table name. Populated eagerly during importBatch. */ |
| 510 | private nodeTypeMap = new Map<string, string>(); |
| 511 | |
| 512 | /** LRU node cache: id → {type, name, properties}. Serves getNode and |
| 513 | * fetchNodesByIds from JS memory without WASM round-trips. Capped at |
| 514 | * NODE_CACHE_MAX entries — Map insertion order acts as the eviction queue. */ |
| 515 | private nodeCache = new Map< |
| 516 | string, |
| 517 | { type: string; name: string; properties: Record<string, unknown> } |
| 518 | >(); |
| 519 | private static readonly NODE_CACHE_MAX = 10_000; |
| 520 | |
| 521 | /** Package node IDs already written to LadybugDB. Packages are shared across |
| 522 | * repos so the same ID can arrive from multiple pipeline runs — skip |
| 523 | * duplicates to avoid LadybugDB COPY FROM primary-key violations. */ |
| 524 | private flushedPackageIds = new Set<string>(); |
| 525 | private flushedSourceIds = new Set<string>(); |
| 526 | |
| 527 | // --- Visualization limits --- |
| 528 | private maxVisNodes = 20000; |
| 529 | private maxVisEdges = 20000; |
| 530 | |
| 531 | // --- Serialization queue (lbug-wasm wraps single-threaded C++ engine) --- |
| 532 | private queue: Promise<void> = Promise.resolve(); |
| 533 | |
| 534 | /** Monotonic counter for COPY-FROM temp filenames. The engine's virtual |
| 535 | * filesystem is process-wide, so a fixed path (e.g. /nodes_Function.csv) |
| 536 | * would collide if two import/flush operations ran in close succession — |
| 537 | * one COPY could read the other's bytes, or an unlink could remove a file |
| 538 | * before the other's COPY runs. Unique names make the paths collision-proof |
| 539 | * regardless of caller serialization. */ |
| 540 | private copySeq = 0; |
| 541 | |
| 542 | /** A unique temp path for a CSV about to be COPY'd in. */ |
| 543 | private tmpCsvPath(label: string): string { |
nothing calls this directly
no outgoing calls
no test coverage detected