import lbug from '@ladybugdb/core'; import { KnowledgeGraph } from '../graph/types.js'; import type { CachedEmbedding } from '../embeddings/types.js'; import { type ExtensionEnsureOptions } from './extension-loader.js'; /** Factory for creating WriteStreams — injectable for testing. */ export type WriteStreamFactory = (filePath: string) => import('fs').WriteStream; /** Result of splitting the relationship CSV into per-label-pair files. */ export interface RelCsvSplitResult { relHeader: string; relsByPairMeta: Map; pairWriteStreams: Map; skippedRels: number; totalValidRels: number; } /** * Split a relationship CSV into per-label-pair files on disk. * * Streams the CSV line-by-line, routing each relationship to a file named * `rel_{fromLabel}_{toLabel}.csv`. Handles backpressure correctly: only one * drain listener per stream at a time, and readline resumes only when ALL * backpressured streams have drained. * * @param csvPath Path to the combined relationship CSV * @param csvDir Directory to write per-pair CSV files * @param validTables Set of valid node table names * @param getNodeLabel Function to extract the label from a node ID * @param wsFactory Optional WriteStream factory (defaults to fs.createWriteStream) */ export declare const splitRelCsvByLabelPair: (csvPath: string, csvDir: string, validTables: Set, getNodeLabel: (id: string) => string, wsFactory?: WriteStreamFactory) => Promise; /** Expose the current Database for pool adapter reuse in tests. */ export declare const getDatabase: () => lbug.Database | null; /** * Return true when the error message indicates a write was attempted against * a read-only LadybugDB connection. The MCP query pool opens DBs read-only, * so any path that calls a `CREATE_*` procedure there will surface this * (e.g. defensive `ensureFTSIndex` calls). Owners of the writable analyze * path should ignore this error — index creation is owned by `gitnexus * analyze` and either already happened or will happen on the next run. */ export declare const isReadOnlyDbError: (err: unknown) => boolean; /** * Acquire a cross-process init lock for `dbPath`. * Uses `O_CREAT | O_EXCL` for atomic create-or-fail semantics. * * Returns a release function that removes the lock file. The release * function is idempotent and safe to call even if the lock was already * cleaned up externally. * * Throws if the lock cannot be acquired after `INIT_LOCK_MAX_ATTEMPTS`. */ export declare const acquireInitLock: (dbPath: string) => Promise<() => Promise>; /** Exported for testing — returns the lock file path for a given dbPath. */ export declare const _initLockPathForTest: (dbPath: string) => string; export declare const initLbug: (dbPath: string) => Promise<{ db: lbug.Database; conn: lbug.Connection; }>; /** * Execute multiple queries against one repo DB atomically. * While the callback runs, no other request can switch the active DB. * * Automatically retries up to DB_LOCK_RETRY_ATTEMPTS times when the * database is busy (e.g. `gitnexus analyze` holds the write lock). * Each retry waits DB_LOCK_RETRY_DELAY_MS * attempt milliseconds. */ export declare const withLbugDb: (dbPath: string, operation: () => Promise, options?: { readOnly?: boolean; }) => Promise; export type LbugProgressCallback = (message: string) => void; export declare const loadGraphToLbug: (graph: KnowledgeGraph, repoPath: string, storagePath: string, onProgress?: LbugProgressCallback) => Promise<{ success: boolean; insertedRels: number; skippedRels: number; warnings: string[]; }>; /** * Insert a single node to LadybugDB * @param label - Node type (File, Function, Class, etc.) * @param properties - Node properties * @param dbPath - Path to LadybugDB database (optional if already initialized) */ export declare const insertNodeToLbug: (label: string, properties: Record, dbPath?: string) => Promise; /** * Batch insert multiple nodes to LadybugDB using a single connection * @param nodes - Array of {label, properties} to insert * @param dbPath - Path to LadybugDB database * @returns Object with success count and error count */ export declare const batchInsertNodesToLbug: (nodes: Array<{ label: string; properties: Record; }>, dbPath: string) => Promise<{ inserted: number; failed: number; }>; export declare const executeQuery: (cypher: string) => Promise; export declare const streamQuery: (cypher: string, onRow: (row: any) => void | Promise) => Promise; /** * Execute a single parameterized query (prepare/execute pattern). * Prevents Cypher injection by binding values as parameters. */ export declare const executePrepared: (cypher: string, params: Record) => Promise; export declare const executeWithReusedStatement: (cypher: string, paramsList: Array>) => Promise; export declare const getLbugStats: () => Promise<{ nodes: number; edges: number; }>; /** * Load cached embeddings from LadybugDB before a rebuild. * Returns all embedding vectors so they can be re-inserted after the graph is reloaded, * avoiding expensive re-embedding of unchanged nodes. * * Detects old schema (no chunkIndex column) and returns empty cache to trigger rebuild. */ export declare const loadCachedEmbeddings: () => Promise<{ embeddingNodeIds: Set; embeddings: CachedEmbedding[]; }>; /** * Fetch existing embedding hashes from CodeEmbedding table for incremental embedding. * Returns a Map suitable for passing to `runEmbeddingPipeline`. * Handles legacy DBs without the `contentHash` column (all rows treated as stale with empty hash). * Returns undefined if the CodeEmbedding table does not exist. * * @param execQuery - Cypher query executor (typically pool-adapter's `executeQuery`) */ export declare const fetchExistingEmbeddingHashes: (execQuery: (cypher: string) => Promise) => Promise | undefined>; /** * Flush the WAL so all pending writes are visible to subsequent readers. * * Best-effort: swallows errors from older LadybugDB versions or schemaless * databases that do not support the CHECKPOINT command. A no-op when there * is nothing pending, so safe (and cheap) to call unconditionally after any * write path. * * Use this instead of safeClose when the connection must stay open * (e.g. the /api/embed handler that keeps serving queries after flushing). * * @see safeClose — CHECKPOINT + connection/database close */ export declare const flushWAL: () => Promise; /** * Issue a manual `CHECKPOINT` against the current connection and surface * any engine error to the caller. Unlike {@link flushWAL}, this variant * does NOT swallow Ladybug rename/remove IO failures — the manual * checkpoint driver (`wal-checkpoint-driver.ts`) relies on the rejection * to drive its bounded retry loop. Returns `false` when no connection is * open (the caller treats this as a no-op success — there is no WAL to * flush). Returns `true` after a successful CHECKPOINT + drain. * * The split from `flushWAL` is deliberate: every other CHECKPOINT site * (server flush, safeClose) is best-effort and prefers a silent skip; * the manual driver, by contrast, must observe failures to decide * whether to retry. */ export declare const tryFlushWAL: () => Promise; /** * Flush the WAL and close the connection and database handles. * * Consolidates the CHECKPOINT + close pattern into a single function so * callers never call conn.close() or db.close() directly (#1376). * An ESLint no-restricted-syntax rule enforces this — see eslint.config.mjs. * * @see flushWAL — CHECKPOINT-only (connection stays open) * @see closeLbug — safeClose + module state reset (full teardown) */ export declare const safeClose: () => Promise; export declare const closeLbug: () => Promise; export declare const isLbugReady: () => boolean; /** * Delete all nodes (and their relationships) for a specific file from LadybugDB * @param filePath - The file path to delete nodes for * @param dbPath - Optional path to LadybugDB for per-query connection * @returns Object with counts of deleted nodes */ export declare const deleteNodesForFile: (filePath: string, dbPath?: string) => Promise<{ deletedNodes: number; }>; export declare const getEmbeddingTableName: () => string; /** * Return the distinct repo-relative paths of files that import * `targetFilePath` according to the IMPORTS edges currently in the * DB. Used by the incremental writeback path to expand the * "files-to-rewrite" set so that files importing a changed file get * their edges (which may have been refined by cross-file resolution) * re-emitted, rather than left stale in the DB. * * The DB query reads the *previous* run's state — pre-pipeline, before * any nodes are deleted — so the returned importers are "files that * USED TO import the target". That's the right set to invalidate: * those are the files whose edges in the DB might no longer match * what cross-file resolution produces given the changed file's new * exports. */ export declare const queryImporters: (targetFilePath: string) => Promise; /** * Drop every Community and Process node (and their MEMBER_OF / * STEP_IN_PROCESS edges via DETACH DELETE). Used at the start of an * incremental run so the communities and processes phases regenerate * them from scratch on the merged graph — required for the * "Leiden runs on the FULL graph" correctness invariant. */ export declare const deleteAllCommunitiesAndProcesses: () => Promise<{ nodesDeleted: number; }>; /** * Load the FTS extension on the supplied connection (or the singleton * writable connection when none is given). * * Delegates to the shared `ExtensionManager` so install policy (auto / * load-only / never), out-of-process bounded INSTALL, and capability * caching are owned in one place. The module-level `ftsLoaded` flag is * kept purely as a per-call short-circuit on the singleton writable * connection so repeated callers (e.g. createFTSIndex) avoid an extra * `LOAD` round-trip per invocation. Pool adapter callers pass * `{ policy: 'load-only' }` so query paths never block on a network install. */ export declare const loadFTSExtension: (targetConn?: lbug.Connection, opts?: ExtensionEnsureOptions) => Promise; /** * Load the VECTOR extension on the supplied connection (or the singleton * writable connection when none is given). Returns false when VECTOR is * unavailable so semantic search can fall back to exact scan. */ export declare const loadVectorExtension: (targetConn?: lbug.Connection, opts?: ExtensionEnsureOptions) => Promise; /** * Create a full-text search index on a table * @param tableName - The node table name (e.g., 'File', 'CodeSymbol') * @param indexName - Name for the FTS index * @param properties - List of properties to index (e.g., ['name', 'code']) * @param stemmer - Stemming algorithm (default: 'porter') */ export declare const createFTSIndex: (tableName: string, indexName: string, properties: string[], stemmer?: string) => Promise; /** * Lazy-create an FTS index, caching the fact in-process. * * Kept for writable maintenance paths that need to lazily materialize an * index. Read-only query paths must not call this; production analysis owns * creating the configured search indexes before the database is served. * * Safe to call repeatedly — the in-process Set guarantees only the first * call hits LadybugDB. `closeLbug` clears the cache so re-init starts fresh. * * Defense in depth: if the active connection is read-only (e.g. the MCP * pool adapter), `CREATE_FTS_INDEX` will fail with "Cannot execute write * operations in a read-only database". Treat that as a no-op and cache * the key so callers don't loop on a path that can never succeed here — * the index is owned by `gitnexus analyze` (writable) and either already * exists or will be created on the next analyze. */ export declare const ensureFTSIndex: (tableName: string, indexName: string, properties: string[], stemmer?: string) => Promise; /** * Query a full-text search index * @param tableName - The node table name * @param indexName - FTS index name * @param query - Search query string * @param limit - Maximum results * @param conjunctive - If true, all terms must match (AND); if false, any term matches (OR) * @returns Array of { node properties, score } */ export declare const queryFTS: (tableName: string, indexName: string, query: string, limit?: number, conjunctive?: boolean) => Promise>; /** * Drop an FTS index */ export declare const dropFTSIndex: (tableName: string, indexName: string) => Promise;