/** * A persistent, process-shared session backend built on `bun:sqlite` (sync, so * it satisfies `SessionBackend` without a load/save wrapper). Sessions survive * restarts and are shared by every worker pointed at the same file. * * import { setSessionBackend } from "@wrnexus/core"; * import { sqliteSessionStore } from "@wrnexus/db/session"; * setSessionBackend(sqliteSessionStore("./sessions.db")); */ import { Database } from "bun:sqlite"; import type { SessionBackend, SessionEntry } from "@wrnexus/core"; export function sqliteSessionStore(path = "sessions.db"): SessionBackend { const db = new Database(path); db.run("PRAGMA journal_mode = WAL"); db.run( "CREATE TABLE IF NOT EXISTS sessions (id TEXT PRIMARY KEY, data TEXT NOT NULL, expiresAt INTEGER NOT NULL)", ); const getStmt = db.query("SELECT data, expiresAt FROM sessions WHERE id = ?"); const setStmt = db.query( "INSERT INTO sessions (id, data, expiresAt) VALUES (?, ?, ?) " + "ON CONFLICT(id) DO UPDATE SET data = excluded.data, expiresAt = excluded.expiresAt", ); const delStmt = db.query("DELETE FROM sessions WHERE id = ?"); const gcStmt = db.query("DELETE FROM sessions WHERE expiresAt <= ?"); return { get(id): SessionEntry | undefined { const row = getStmt.get(id) as { data: string; expiresAt: number } | null; if (!row) return undefined; try { return { data: JSON.parse(row.data) as Record, expiresAt: row.expiresAt }; } catch (error) { console.warn(`[wrnexus:db] discarded corrupt session '${id}'`, error); return undefined; } }, set(id, entry) { setStmt.run(id, JSON.stringify(entry.data), entry.expiresAt); }, delete(id) { delStmt.run(id); }, gc(now) { gcStmt.run(now); }, }; }