src / db.ts
// Uses Node's built-in node:sqlite (still experimental, hence the warning on
// startup) instead of better-sqlite3: LM Studio runs plugins inside its own
// hardened-runtime-signed node binary with library validation enabled, which
// refuses to dlopen any native addon not signed by LM Studio's own team.
// node:sqlite is compiled into the node executable itself, so there's no
// dlopen and no signature check to fail.
import { DatabaseSync } from "node:sqlite";
import { mkdirSync } from "fs";
import { dirname } from "path";
import { SCHEMA_SQL } from "./schema";
const connections = new Map<string, DatabaseSync>();
export function getDb(databasePath: string): DatabaseSync {
const existing = connections.get(databasePath);
if (existing) {
return existing;
}
mkdirSync(dirname(databasePath), { recursive: true });
const db = new DatabaseSync(databasePath);
db.exec("PRAGMA journal_mode = WAL");
db.exec("PRAGMA foreign_keys = ON");
db.exec(SCHEMA_SQL);
connections.set(databasePath, db);
return db;
}
export function closeAllConnections(): void {
for (const db of connections.values()) {
db.close();
}
connections.clear();
}
// node:sqlite has no db.transaction() helper like better-sqlite3; wrap
// BEGIN/COMMIT/ROLLBACK by hand.
export function withTransaction<T>(db: DatabaseSync, fn: () => T): T {
db.exec("BEGIN");
try {
const result = fn();
db.exec("COMMIT");
return result;
} catch (err) {
db.exec("ROLLBACK");
throw err;
}
}
src / db.ts
// Uses Node's built-in node:sqlite (still experimental, hence the warning on
// startup) instead of better-sqlite3: LM Studio runs plugins inside its own
// hardened-runtime-signed node binary with library validation enabled, which
// refuses to dlopen any native addon not signed by LM Studio's own team.
// node:sqlite is compiled into the node executable itself, so there's no
// dlopen and no signature check to fail.
import { DatabaseSync } from "node:sqlite";
import { mkdirSync } from "fs";
import { dirname } from "path";
import { SCHEMA_SQL } from "./schema";
const connections = new Map<string, DatabaseSync>();
export function getDb(databasePath: string): DatabaseSync {
const existing = connections.get(databasePath);
if (existing) {
return existing;
}
mkdirSync(dirname(databasePath), { recursive: true });
const db = new DatabaseSync(databasePath);
db.exec("PRAGMA journal_mode = WAL");
db.exec("PRAGMA foreign_keys = ON");
db.exec(SCHEMA_SQL);
connections.set(databasePath, db);
return db;
}
export function closeAllConnections(): void {
for (const db of connections.values()) {
db.close();
}
connections.clear();
}
// node:sqlite has no db.transaction() helper like better-sqlite3; wrap
// BEGIN/COMMIT/ROLLBACK by hand.
export function withTransaction<T>(db: DatabaseSync, fn: () => T): T {
db.exec("BEGIN");
try {
const result = fn();
db.exec("COMMIT");
return result;
} catch (err) {
db.exec("ROLLBACK");
throw err;
}
}