/* eslint-disable @typescript-eslint/no-explicit-any */ import { MaybePromise } from "@fabric/core"; import SQLite from "sqlite3"; export class SQLiteDatabase { db: SQLite.Database; private cachedStatements = new Map(); constructor(private readonly path: string) { this.db = new SQLite.Database(path); } async init() { await this.run("PRAGMA journal_mode = WAL"); await this.run("PRAGMA foreign_keys = ON"); } async close() { await this.finalizeStatements(); await new Promise((resolve, reject) => { this.db.close((err: Error | null) => { if (err) { reject(err); } else { resolve(); } }); }); } async withTransaction(fn: () => MaybePromise) { try { await this.run("BEGIN TRANSACTION"); await fn(); await this.run("COMMIT"); } catch { await this.run("ROLLBACK"); } } run(sql: string, params?: Record) { return new Promise((resolve, reject) => { this.db.run(sql, params, (err: Error | null) => { if (err) { reject(err); } else { resolve(); } }); }); } runPrepared(sql: string, params?: Record) { const cachedStmt = this.getCachedStatement(sql); return new Promise((resolve, reject) => { cachedStmt.run(params, (err: Error | null) => { if (err) { reject(err); } else { resolve(); } }); }); } allPrepared( sql: string, params?: Record, transformer?: (row: any) => any, ) { const cachedStmt = this.getCachedStatement(sql); return new Promise((resolve, reject) => { cachedStmt.all( params, (err: Error | null, rows: Record[]) => { if (err) { reject(err); } else { resolve(transformer ? rows.map(transformer) : rows); } }, ); }); } onePrepared( sql: string, params?: Record, transformer?: (row: any) => any, ) { const cachedStmt = this.getCachedStatement(sql); return new Promise((resolve, reject) => { cachedStmt.all( params, (err: Error | null, rows: Record[]) => { if (err) { reject(err); } else { resolve(transformer ? rows.map(transformer)[0] : rows[0]); } }, ); }); } private getCachedStatement(sql: string) { let cached = this.cachedStatements.get(sql); if (!cached) { const stmt = this.db.prepare(sql); this.cachedStatements.set(sql, stmt); cached = stmt; } return cached; } private async finalizeStatements() { for (const stmt of this.cachedStatements.values()) { await new Promise((resolve, reject) => { stmt.finalize((err: Error | null) => { if (err) { reject(err); } else { resolve(); } }); }); } } }