import type { ISQLiteAPI, ISQLiteAPIFactory, ISQLiteAPIOptions } from "../sqlite/type" import { getSQLiteAPI_BetterSQLite } from "../sqlite/sqlite_better" import { setUnEnumerable } from "../../../objects" import { IKvliteCollectionOptions, KvliteCollection } from "./KvliteCollection" import { IKvliteArrayOptions, KvliteArray } from "./KvliteArray" const KvliteMap = new Map() /** * 使用 Kvlite 数据库,相同文件路径的实例会被复用 * @warning 注意:如果使用内存数据库 ":memory:",则每次调用都会复用同一个实例 * 如果需要区分不同内存数据库,请使用不同的标识符,如 ":memory:1"、":memory:2" * @param dbPath 数据库文件路径,或者使用 ":memory:" 表示内存数据库 */ export async function useKvlite(dbPath: string, options?: IKvliteOptions) { let dbPathFin = dbPath let kvlite = KvliteMap.get(dbPath) if (!kvlite) { if (dbPath.startsWith(":memory:")) { dbPathFin = ":memory:" } let findOptions = Object.assign({}, options) if (!options?.sqliteGetter) { const globalAny = globalThis as any if (globalAny?.Bun) { findOptions.sqliteGetter = await import("../sqlite/sqlite_bun").then((mod) => mod.getSQLiteAPI_Bun) } } kvlite = new Kvlite(dbPathFin, findOptions) KvliteMap.set(dbPath, kvlite) } return kvlite } export interface IKvliteOptions extends ISQLiteAPIOptions { sqliteGetter?: ISQLiteAPIFactory } /** * 键值数据库,对 SQLite 进行的简单封装 */ export class Kvlite { _options!: ISQLiteAPIOptions _sqlite!: ISQLiteAPI _allCollection!: Map constructor( /** sqlite 数据库文件路径或 Buffer */ dbPath: string | Buffer | ":memory:", options?: IKvliteOptions ) { const globalAny = globalThis as any let sqliteGetter: ISQLiteAPIFactory | undefined = options?.sqliteGetter || globalAny.__Kvlite_SqliteGetter if (!sqliteGetter) { if (globalAny?.Bun) { throw new Error( "Kvlite: Bun 环境下请通过 options.sqliteGetter 或 globalThis.__Kvlite_SqliteGetter 显式注入 getSQLiteAPI_Bun。\n或者使用 useKvlite() 方法,该方法会自动处理 Bun 环境的 sqliteGetter 注入。" ) } else { sqliteGetter = getSQLiteAPI_BetterSQLite } } let sqlite = sqliteGetter(dbPath, options) // 注册 REGEXP 函数 sqlite.function("REGEXP", (regex: string, text: string) => { return new RegExp(regex).test(text) ? 1 : 0 }) setUnEnumerable(this, "_sqlite", sqlite) setUnEnumerable(this, "_options", options) setUnEnumerable(this, "_allCollection", new Map()) } /** * 使用指定的 Kvlite 键值数据结构的合集 * 如果合集不存在则创建,通名合集实例会被复用 */ useCollection(collectionName: string, options?: IKvliteCollectionOptions): KvliteCollection { let key = `@collection:${collectionName}` let collection = this._allCollection?.get(key) if (!collection) { collection = new KvliteCollection(this, collectionName, options) this._allCollection.set(key, collection) } return collection } /** * 使用指定的 Kvlite 数组数据结构的合集 * 如果合集不存在则创建,通名合集实例会被复用 */ useArray(collectionName: string, options?: IKvliteArrayOptions): KvliteArray { let key = `@array:${collectionName}` let array = this._allCollection?.get(key) if (!array) { array = new KvliteArray(this, collectionName, options) this._allCollection.set(key, array) } return array } /** * 关闭数据库连接 */ close(): boolean { try { if (!this._options.safeMode) { this._sqlite.pragma("wal_checkpoint(FULL)") } this._sqlite.close() return true } catch (error) { throw error } } /** * 测试数据库连接是否可用 */ testDb(): boolean { try { this._sqlite.exec("SELECT 1 + 1 AS result") return true } catch (error) { throw error } } /** * 优化数据库尺寸 * Sqlite 的操作会产生碎片,使用 VACUUM 可以优化数据库尺寸 */ vacuum() { this._sqlite.exec("VACUUM;") } /** * 将数据库导出为文件 * @example * ```ts * let buffer = kvlite1.toBuffer() * * let kvlite2 = new Kvlite(buffer) * ``` */ toBuffer() { return this._sqlite.serialize() } }