61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
/**
|
|
* 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<Row[]> {
|
|
return database.query(sql).all(...(bind(params) as never[])) as Row[];
|
|
},
|
|
async exec(sql, params = []): Promise<ExecResult> {
|
|
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();
|
|
},
|
|
};
|
|
}
|