import { createRequire } from "node:module"; import path from "node:path"; export interface SqliteStatement { run: (...params: unknown[]) => unknown; get: (...params: unknown[]) => unknown; all: (...params: unknown[]) => unknown[]; } export interface SqliteDatabase { exec: (sql: string) => void; prepare: (sql: string) => SqliteStatement; close: () => void; } interface NodeSqliteModule { DatabaseSync: new (filename: string) => { exec: (sql: string) => void; prepare: (sql: string) => SqliteStatement; close: () => void; }; } interface BunSqliteStatement { run: (...params: unknown[]) => unknown; get: (...params: unknown[]) => unknown; all: (...params: unknown[]) => unknown[]; } interface BunSqliteModule { Database: new (filename: string) => { exec: (sql: string) => unknown; query: (sql: string) => BunSqliteStatement; close: () => void; }; } function isNodeSqliteUnavailableError(error: unknown): boolean { if (!(error instanceof Error)) { return false; } const code = (error as NodeJS.ErrnoException).code; if (code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND") { return true; } return error.message.includes("node:sqlite"); } function openNodeSqliteDatabase( requireFn: ReturnType, dbPath: string, ): SqliteDatabase { let nodeSqlite: NodeSqliteModule; try { nodeSqlite = requireFn("node:sqlite") as NodeSqliteModule; } catch (error) { if (isNodeSqliteUnavailableError(error)) { throw new Error( "node:sqlite is unavailable in this runtime. Use Node.js 22+ (or run the server with Bun).", { cause: error }, ); } throw error; } if (typeof nodeSqlite.DatabaseSync !== "function") { throw new Error( "node:sqlite was loaded but DatabaseSync is missing. Upgrade Node.js or run the server with Bun.", ); } const db = new nodeSqlite.DatabaseSync(dbPath); return { exec: (sql) => { db.exec(sql); }, prepare: (sql) => db.prepare(sql), close: () => { db.close(); }, }; } function openBunSqliteDatabase( requireFn: ReturnType, dbPath: string, ): SqliteDatabase { const bunSqlite = requireFn("bun:sqlite") as BunSqliteModule; const db = new bunSqlite.Database(dbPath); return { exec: (sql) => { db.exec(sql); }, prepare: (sql) => { const statement = db.query(sql); return { run: (...params: unknown[]) => statement.run(...params), get: (...params: unknown[]) => statement.get(...params), all: (...params: unknown[]) => statement.all(...params), }; }, close: () => { db.close(); }, }; } export function openSqliteDatabase( dbPath: string, requireFn: ReturnType = createRequire( path.join(process.cwd(), "t3code-sqlite-adapter.cjs"), ), runtimeIsBun = Boolean(process.versions.bun), ): SqliteDatabase { if (runtimeIsBun) { // Development mode runs in Bun, so use Bun's SQLite adapter return openBunSqliteDatabase(requireFn, dbPath); } return openNodeSqliteDatabase(requireFn, dbPath); }