{"version":3,"file":"sqlite-By6r2YhZ.mjs","names":[],"sources":["../src/storage/sqlite.ts"],"sourcesContent":["/**\n * SQLiteBackend — wraps the existing better-sqlite3 federation.db\n * behind the StorageBackend interface.\n *\n * This is a thin adapter.  The heavy lifting is all in the existing\n * memory/indexer.ts and memory/search.ts code; we just provide a\n * backend-agnostic surface so the daemon and tools can call either\n * SQLite or Postgres transparently.\n */\n\nimport type { Database } from \"better-sqlite3\";\nimport type { StorageBackend, ChunkRow, FileRow, FederationStats } from \"./interface.js\";\nimport type { SearchResult, SearchOptions } from \"../memory/search.js\";\nimport { searchMemory, searchMemorySemantic } from \"../memory/search.js\";\n\nexport class SQLiteBackend implements StorageBackend {\n  readonly backendType = \"sqlite\" as const;\n\n  private db: Database;\n\n  constructor(db: Database) {\n    this.db = db;\n  }\n\n  /**\n   * Expose the raw better-sqlite3 Database handle.\n   * Used by the daemon to pass to indexAll() which still uses the synchronous API directly.\n   */\n  getRawDb(): Database {\n    return this.db;\n  }\n\n  /**\n   * Alias for getRawDb() — used by the dispatcher for operations that require\n   * direct SQLite access (e.g. memory_feedback, touchChunksLastAccessed).\n   */\n  getSqliteDb(): Database {\n    return this.db;\n  }\n\n  // -------------------------------------------------------------------------\n  // Lifecycle\n  // -------------------------------------------------------------------------\n\n  async close(): Promise<void> {\n    try {\n      this.db.close();\n    } catch {\n      // ignore\n    }\n  }\n\n  async getStats(): Promise<FederationStats> {\n    const files = (\n      this.db.prepare(\"SELECT COUNT(*) AS n FROM memory_files\").get() as { n: number }\n    ).n;\n    const chunks = (\n      this.db.prepare(\"SELECT COUNT(*) AS n FROM memory_chunks\").get() as { n: number }\n    ).n;\n    return { files, chunks };\n  }\n\n  // -------------------------------------------------------------------------\n  // File tracking\n  // -------------------------------------------------------------------------\n\n  async getFileHash(projectId: number, path: string): Promise<string | undefined> {\n    const row = this.db\n      .prepare(\"SELECT hash FROM memory_files WHERE project_id = ? AND path = ?\")\n      .get(projectId, path) as { hash: string } | undefined;\n    return row?.hash;\n  }\n\n  async upsertFile(file: FileRow): Promise<void> {\n    this.db\n      .prepare(\n        `INSERT INTO memory_files (project_id, path, source, tier, hash, mtime, size)\n         VALUES (?, ?, ?, ?, ?, ?, ?)\n         ON CONFLICT(project_id, path) DO UPDATE SET\n           source = excluded.source,\n           tier   = excluded.tier,\n           hash   = excluded.hash,\n           mtime  = excluded.mtime,\n           size   = excluded.size`\n      )\n      .run(file.projectId, file.path, file.source, file.tier, file.hash, file.mtime, file.size);\n  }\n\n  // -------------------------------------------------------------------------\n  // Chunk management\n  // -------------------------------------------------------------------------\n\n  async getChunkIds(projectId: number, path: string): Promise<string[]> {\n    const rows = this.db\n      .prepare(\"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\")\n      .all(projectId, path) as Array<{ id: string }>;\n    return rows.map((r) => r.id);\n  }\n\n  async deleteChunksForFile(projectId: number, path: string): Promise<void> {\n    const ids = await this.getChunkIds(projectId, path);\n    const deleteFts = this.db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n    const deleteChunks = this.db.prepare(\n      \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\"\n    );\n    this.db.transaction(() => {\n      for (const id of ids) {\n        deleteFts.run(id);\n      }\n      deleteChunks.run(projectId, path);\n    })();\n  }\n\n  async insertChunks(chunks: ChunkRow[]): Promise<void> {\n    if (chunks.length === 0) return;\n\n    const insertChunk = this.db.prepare(\n      `INSERT INTO memory_chunks (id, project_id, source, tier, path, start_line, end_line, hash, text, updated_at)\n       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n    );\n    const insertFts = this.db.prepare(\n      `INSERT INTO memory_fts (text, id, project_id, path, source, tier, start_line, end_line)\n       VALUES (?, ?, ?, ?, ?, ?, ?, ?)`\n    );\n\n    this.db.transaction(() => {\n      for (const c of chunks) {\n        insertChunk.run(\n          c.id,\n          c.projectId,\n          c.source,\n          c.tier,\n          c.path,\n          c.startLine,\n          c.endLine,\n          c.hash,\n          c.text,\n          c.updatedAt\n        );\n        insertFts.run(\n          c.text,\n          c.id,\n          c.projectId,\n          c.path,\n          c.source,\n          c.tier,\n          c.startLine,\n          c.endLine\n        );\n      }\n    })();\n  }\n\n  async getDistinctChunkPaths(projectId: number): Promise<string[]> {\n    const rows = this.db\n      .prepare(\"SELECT DISTINCT path FROM memory_chunks WHERE project_id = ?\")\n      .all(projectId) as Array<{ path: string }>;\n    return rows.map((r) => r.path);\n  }\n\n  async deletePaths(projectId: number, paths: string[]): Promise<void> {\n    if (paths.length === 0) return;\n    const deleteFts = this.db.prepare(\"DELETE FROM memory_fts WHERE id = ?\");\n    const deleteChunks = this.db.prepare(\n      \"DELETE FROM memory_chunks WHERE project_id = ? AND path = ?\"\n    );\n    const deleteFile = this.db.prepare(\n      \"DELETE FROM memory_files WHERE project_id = ? AND path = ?\"\n    );\n    this.db.transaction(() => {\n      for (const path of paths) {\n        const ids = this.db\n          .prepare(\"SELECT id FROM memory_chunks WHERE project_id = ? AND path = ?\")\n          .all(projectId, path) as Array<{ id: string }>;\n        for (const { id } of ids) {\n          deleteFts.run(id);\n        }\n        deleteChunks.run(projectId, path);\n        deleteFile.run(projectId, path);\n      }\n    })();\n  }\n\n  async getUnembeddedChunkIds(projectId?: number, limit?: number): Promise<Array<{ id: string; text: string; project_id: number; path: string }>> {\n    const conditions = [\"embedding IS NULL\"];\n    const params: (string | number)[] = [];\n\n    if (projectId !== undefined) {\n      conditions.push(\"project_id = ?\");\n      params.push(projectId);\n    }\n\n    const where = \"WHERE \" + conditions.join(\" AND \");\n    // Prioritize real knowledge notes over PAI session/job-search noise.\n    // CASE expression assigns lower priority numbers to knowledge paths.\n    const rows = this.db\n      .prepare(`SELECT id, text, project_id, path FROM memory_chunks ${where}\n        ORDER BY CASE\n          WHEN path LIKE '🧠 Ideaverse/%' THEN 0\n          WHEN path LIKE 'Z - Zettelkasten/%' THEN 0\n          WHEN path LIKE '💼 Business/%' THEN 0\n          WHEN path LIKE '📆 Meetings/%' THEN 1\n          WHEN path LIKE '💡 Insights/%' THEN 1\n          WHEN path LIKE '👨‍💻 People/%' THEN 1\n          WHEN path LIKE 'University/%' THEN 1\n          WHEN path LIKE 'Copilot/%' THEN 1\n          WHEN path LIKE '🗓️ Daily Notes/%' THEN 2\n          WHEN path LIKE 'PAI/%' THEN 3\n          WHEN path LIKE '09-job-search/%' THEN 4\n          WHEN path LIKE 'seriousletter/%' THEN 4\n          WHEN path LIKE 'Attachments/%' THEN 5\n          ELSE 2\n        END, id${limit !== undefined ? \" LIMIT ?\" : \"\"}`)\n      .all(...params, ...(limit !== undefined ? [limit] : [])) as Array<{ id: string; text: string; project_id: number; path: string }>;\n    return rows;\n  }\n\n  async updateEmbedding(chunkId: string, embedding: Buffer): Promise<void> {\n    this.db\n      .prepare(\"UPDATE memory_chunks SET embedding = ? WHERE id = ?\")\n      .run(embedding, chunkId);\n  }\n\n  // -------------------------------------------------------------------------\n  // Search\n  // -------------------------------------------------------------------------\n\n  async searchKeyword(query: string, opts?: SearchOptions): Promise<SearchResult[]> {\n    return searchMemory(this.db, query, opts);\n  }\n\n  async searchSemantic(queryEmbedding: Float32Array, opts?: SearchOptions): Promise<SearchResult[]> {\n    return searchMemorySemantic(this.db, queryEmbedding, opts);\n  }\n\n  // -------------------------------------------------------------------------\n  // Vault operations — not supported on SQLite backend (use Postgres)\n  // -------------------------------------------------------------------------\n\n  private vaultNotSupported(): never {\n    throw new Error(\"Vault operations require the Postgres backend\");\n  }\n\n  async upsertVaultFile(): Promise<void> { this.vaultNotSupported(); }\n  async deleteVaultFile(): Promise<void> { this.vaultNotSupported(); }\n  async getVaultFile(): Promise<null> { this.vaultNotSupported(); }\n  async getVaultFileByInode(): Promise<null> { this.vaultNotSupported(); }\n  async getAllVaultFiles(): Promise<never[]> { this.vaultNotSupported(); }\n  async getRecentVaultFiles(): Promise<never[]> { this.vaultNotSupported(); }\n  async countVaultFiles(): Promise<number> { this.vaultNotSupported(); }\n  async upsertVaultAliases(): Promise<void> { this.vaultNotSupported(); }\n  async deleteVaultAliases(): Promise<void> { this.vaultNotSupported(); }\n  async replaceLinksForSources(): Promise<void> { this.vaultNotSupported(); }\n  async getLinksFromSource(): Promise<never[]> { this.vaultNotSupported(); }\n  async getLinksToTarget(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultLinkGraph(): Promise<never[]> { this.vaultNotSupported(); }\n  async upsertVaultHealth(): Promise<void> { this.vaultNotSupported(); }\n  async getVaultHealth(): Promise<null> { this.vaultNotSupported(); }\n  async getOrphans(): Promise<never[]> { this.vaultNotSupported(); }\n  async getDeadLinks(): Promise<never[]> { this.vaultNotSupported(); }\n  async upsertNameIndex(): Promise<void> { this.vaultNotSupported(); }\n  async replaceNameIndex(): Promise<void> { this.vaultNotSupported(); }\n  async resolveVaultName(): Promise<never[]> { this.vaultNotSupported(); }\n  async searchVaultNameIndex(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultFilesByPaths(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultFilesByPathsAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultLinksFromPaths(): Promise<never[]> { this.vaultNotSupported(); }\n  async getChunksWithEmbeddings(): Promise<never[]> { this.vaultNotSupported(); }\n  async getChunksForPath(): Promise<never[]> { this.vaultNotSupported(); }\n  async searchChunksByText(): Promise<never[]> { this.vaultNotSupported(); }\n  async countVaultFilesWithPrefix(): Promise<number> { this.vaultNotSupported(); }\n  async countVaultFilesAfter(): Promise<number> { this.vaultNotSupported(); }\n  async countVaultLinksWithPrefix(): Promise<number> { this.vaultNotSupported(); }\n  async countVaultLinksAfter(): Promise<number> { this.vaultNotSupported(); }\n  async getDeadLinksWithLineNumbers(): Promise<never[]> { this.vaultNotSupported(); }\n  async getDeadLinksWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n  async getDeadLinksAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getOrphansWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n  async getOrphansAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getLowConnectivity(): Promise<never[]> { this.vaultNotSupported(); }\n  async getLowConnectivityWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n  async getLowConnectivityAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getAllVaultFilePaths(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultFilePathsWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultFilePathsAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultLinkEdges(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultLinkEdgesWithPrefix(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultLinkEdgesAfter(): Promise<never[]> { this.vaultNotSupported(); }\n  async getVaultAlias(): Promise<null> { this.vaultNotSupported(); }\n}\n"],"mappings":"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"}