{"version":3,"file":"sqlite.cjs","names":["namespace: string","config: SQLiteRecordManagerOptions","Database","statement: Statement<[]>","keys: string[]","updateOptions?: UpdateOptions","options?: ListKeyOptions","values: (string | number | string[])[]"],"sources":["../../src/indexes/sqlite.ts"],"sourcesContent":["import Database, { Database as DatabaseType, Statement } from \"better-sqlite3\";\nimport {\n  ListKeyOptions,\n  RecordManagerInterface,\n  UpdateOptions,\n} from \"@langchain/core/indexing\";\n\ninterface TimeRow {\n  epoch: number;\n}\n\ninterface KeyRecord {\n  key: string;\n}\n\n/**\n * Options for configuring the SQLiteRecordManager class.\n */\nexport type SQLiteRecordManagerOptions = {\n  /**\n   * The file path of the SQLite database.\n   * One of either `localPath` or `connectionString` is required.\n   */\n  localPath?: string;\n  /**\n   * The connection string of the SQLite database.\n   * One of either `localPath` or `connectionString` is required.\n   */\n  connectionString?: string;\n  /**\n   * The name of the table in the SQLite database.\n   */\n  tableName: string;\n};\n\nexport class SQLiteRecordManager implements RecordManagerInterface {\n  lc_namespace = [\"langchain\", \"recordmanagers\", \"sqlite\"];\n\n  tableName: string;\n\n  db: DatabaseType;\n\n  namespace: string;\n\n  constructor(namespace: string, config: SQLiteRecordManagerOptions) {\n    const { localPath, connectionString, tableName } = config;\n    if (!connectionString && !localPath) {\n      throw new Error(\n        \"One of either `localPath` or `connectionString` is required.\"\n      );\n    }\n    if (connectionString && localPath) {\n      throw new Error(\n        \"Only one of either `localPath` or `connectionString` is allowed.\"\n      );\n    }\n    this.namespace = namespace;\n    this.tableName = tableName;\n    this.db = new Database(connectionString ?? localPath);\n  }\n\n  async createSchema(): Promise<void> {\n    try {\n      this.db.exec(`\nCREATE TABLE IF NOT EXISTS \"${this.tableName}\" (\n  uuid TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),\n  key TEXT NOT NULL,\n  namespace TEXT NOT NULL,\n  updated_at REAL NOT NULL,\n  group_id TEXT,\n  UNIQUE (key, namespace)\n);\nCREATE INDEX IF NOT EXISTS updated_at_index ON \"${this.tableName}\" (updated_at);\nCREATE INDEX IF NOT EXISTS key_index ON \"${this.tableName}\" (key);\nCREATE INDEX IF NOT EXISTS namespace_index ON \"${this.tableName}\" (namespace);\nCREATE INDEX IF NOT EXISTS group_id_index ON \"${this.tableName}\" (group_id);`);\n    } catch (error) {\n      console.error(\"Error creating schema\");\n      throw error; // Re-throw the error to let the caller handle it\n    }\n  }\n\n  async getTime(): Promise<number> {\n    try {\n      const statement: Statement<[]> = this.db.prepare(\n        \"SELECT strftime('%s', 'now') AS epoch\"\n      );\n      const { epoch } = statement.get() as TimeRow;\n      return Number(epoch);\n    } catch (error) {\n      console.error(\"Error getting time in SQLiteRecordManager:\");\n      throw error;\n    }\n  }\n\n  async update(keys: string[], updateOptions?: UpdateOptions): Promise<void> {\n    if (keys.length === 0) {\n      return;\n    }\n\n    const updatedAt = await this.getTime();\n    const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {};\n\n    if (timeAtLeast && updatedAt < timeAtLeast) {\n      throw new Error(\n        `Time sync issue with database ${updatedAt} < ${timeAtLeast}`\n      );\n    }\n\n    const groupIds = _groupIds ?? keys.map(() => null);\n\n    if (groupIds.length !== keys.length) {\n      throw new Error(\n        `Number of keys (${keys.length}) does not match number of group_ids (${groupIds.length})`\n      );\n    }\n\n    const recordsToUpsert = keys.map((key, i) => [\n      key,\n      this.namespace,\n      updatedAt,\n      groupIds[i] ?? null, // Ensure groupIds[i] is null if undefined\n    ]);\n\n    // Consider using a transaction for batch operations\n    const updateTransaction = this.db.transaction(() => {\n      for (const row of recordsToUpsert) {\n        this.db\n          .prepare(\n            `\nINSERT INTO \"${this.tableName}\" (key, namespace, updated_at, group_id)\nVALUES (?, ?, ?, ?)\nON CONFLICT (key, namespace) DO UPDATE SET updated_at = excluded.updated_at`\n          )\n          .run(...row);\n      }\n    });\n    updateTransaction();\n  }\n\n  async exists(keys: string[]): Promise<boolean[]> {\n    if (keys.length === 0) {\n      return [];\n    }\n\n    // Prepare the placeholders and the query\n    const placeholders = keys.map(() => `?`).join(\", \");\n    const sql = `\nSELECT key\nFROM \"${this.tableName}\"\nWHERE namespace = ? AND key IN (${placeholders})`;\n\n    // Initialize an array to fill with the existence checks\n    const existsArray = new Array(keys.length).fill(false);\n\n    try {\n      // Execute the query\n      const rows = this.db\n        .prepare(sql)\n        .all(this.namespace, ...keys) as KeyRecord[];\n      // Create a set of existing keys for faster lookup\n      const existingKeysSet = new Set(rows.map((row) => row.key));\n      // Map the input keys to booleans indicating if they exist\n      keys.forEach((key, index) => {\n        existsArray[index] = existingKeysSet.has(key);\n      });\n      return existsArray;\n    } catch (error) {\n      console.error(\"Error checking existence of keys\");\n      throw error; // Allow the caller to handle the error\n    }\n  }\n\n  async listKeys(options?: ListKeyOptions): Promise<string[]> {\n    const { before, after, limit, groupIds } = options ?? {};\n    let query = `SELECT key FROM \"${this.tableName}\" WHERE namespace = ?`;\n    const values: (string | number | string[])[] = [this.namespace];\n\n    if (before) {\n      query += ` AND updated_at < ?`;\n      values.push(before);\n    }\n\n    if (after) {\n      query += ` AND updated_at > ?`;\n      values.push(after);\n    }\n\n    if (limit) {\n      query += ` LIMIT ?`;\n      values.push(limit);\n    }\n\n    if (groupIds && Array.isArray(groupIds)) {\n      query += ` AND group_id IN (${groupIds\n        .filter((gid) => gid !== null)\n        .map(() => \"?\")\n        .join(\", \")})`;\n      values.push(...groupIds.filter((gid): gid is string => gid !== null));\n    }\n\n    query += \";\";\n\n    // Directly using try/catch with async/await for cleaner flow\n    try {\n      const result = this.db.prepare(query).all(...values) as { key: string }[];\n      return result.map((row) => row.key);\n    } catch (error) {\n      console.error(\"Error listing keys.\");\n      throw error; // Re-throw the error to be handled by the caller\n    }\n  }\n\n  async deleteKeys(keys: string[]): Promise<void> {\n    if (keys.length === 0) {\n      return;\n    }\n\n    const placeholders = keys.map(() => \"?\").join(\", \");\n    const query = `DELETE FROM \"${this.tableName}\" WHERE namespace = ? AND key IN (${placeholders});`;\n    const values = [this.namespace, ...keys].map((v) =>\n      typeof v !== \"string\" ? `${v}` : v\n    );\n\n    // Directly using try/catch with async/await for cleaner flow\n    try {\n      this.db.prepare(query).run(...values);\n    } catch (error) {\n      console.error(\"Error deleting keys\");\n      throw error; // Re-throw the error to be handled by the caller\n    }\n  }\n}\n"],"mappings":"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"}