/** * SQLite adapter, built on Bun's zero-dependency `bun:sqlite`. Use a file URL * (`file:./dev.db`) or the default in-memory database (great for tests). This is * the reference adapter — it needs no external service to run. */ import { Database } from "bun:sqlite"; import type { Driver, ExecResult, Row, TxHandle } from "../driver.ts"; /** SQLite can only bind numbers/strings/bigints/null/blobs — coerce JS values. */ function bind(params: unknown[]): unknown[] { return params.map((p) => { if (p === true) return 1; if (p === false) return 0; if (p === undefined) return null; if (p instanceof Date) return p.toISOString(); return p; }); } /** Create a SQLite driver. `url` may be `file:./x.db`, a path, or `:memory:`. */ export function sqlite(url = ":memory:"): Driver { const path = url.replace(/^(file:|sqlite:)/, "") || ":memory:"; const database = new Database(path); database.exec("PRAGMA foreign_keys = ON;"); const runner: TxHandle = { async query(sql, params = []): Promise { return database.query(sql).all(...(bind(params) as never[])) as Row[]; }, async exec(sql, params = []): Promise { if (params.length === 0) { database.exec(sql); // DDL / multi-statement return { changes: 0 }; } const result = database.query(sql).run(...(bind(params) as never[])); return { changes: result.changes, lastInsertId: Number(result.lastInsertRowid) }; }, }; return { dialect: "sqlite", query: runner.query, exec: runner.exec, async transaction(fn) { database.exec("BEGIN"); try { const result = await fn(runner); database.exec("COMMIT"); return result; } catch (err) { database.exec("ROLLBACK"); throw err; } }, close() { database.close(); }, }; }