From 415e49327e5549c472ddc9d025b94b932adb028b Mon Sep 17 00:00:00 2001 From: Jared Miller Date: Tue, 27 Jan 2026 21:15:23 -0500 Subject: [PATCH] Add bun:sqlite persistence layer --- .gitignore | 2 ++ src/db.test.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/db.ts | 36 ++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/db.test.ts create mode 100644 src/db.ts diff --git a/.gitignore b/.gitignore index a14702c..6a76534 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json # Finder (MacOS) folder config .DS_Store + +*.db diff --git a/src/db.test.ts b/src/db.test.ts new file mode 100644 index 0000000..9b5c33c --- /dev/null +++ b/src/db.test.ts @@ -0,0 +1,52 @@ +import { describe, it, expect, beforeEach, afterEach } from "bun:test"; +import { initDb, saveUpdate, getUpdates, close } from "./db"; +import { unlinkSync } from "fs"; + +const TEST_DB = ":memory:"; + +describe("db persistence", () => { + beforeEach(() => { + initDb(TEST_DB); + }); + + afterEach(() => { + close(); + }); + + it("saves and retrieves updates for a room", () => { + const room = "test-room"; + const update1 = new Uint8Array([1, 2, 3]); + const update2 = new Uint8Array([4, 5, 6]); + + saveUpdate(room, update1); + saveUpdate(room, update2); + + const updates = getUpdates(room); + expect(updates).toHaveLength(2); + expect(updates[0]).toEqual(update1); + expect(updates[1]).toEqual(update2); + }); + + it("returns empty array for unknown room", () => { + const updates = getUpdates("nonexistent"); + expect(updates).toEqual([]); + }); + + it("rooms are isolated", () => { + const room1 = "room-one"; + const room2 = "room-two"; + const update1 = new Uint8Array([1, 1, 1]); + const update2 = new Uint8Array([2, 2, 2]); + + saveUpdate(room1, update1); + saveUpdate(room2, update2); + + const room1Updates = getUpdates(room1); + const room2Updates = getUpdates(room2); + + expect(room1Updates).toHaveLength(1); + expect(room2Updates).toHaveLength(1); + expect(room1Updates[0]).toEqual(update1); + expect(room2Updates[0]).toEqual(update2); + }); +}); diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..2104c7b --- /dev/null +++ b/src/db.ts @@ -0,0 +1,36 @@ +import { Database } from "bun:sqlite"; + +let db: Database | null = null; + +export function initDb(path: string): void { + db = new Database(path); + db.exec(` + CREATE TABLE IF NOT EXISTS updates ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + room TEXT NOT NULL, + data BLOB NOT NULL, + created_at INTEGER DEFAULT (unixepoch()) + ); + CREATE INDEX IF NOT EXISTS idx_room ON updates(room); + `); +} + +export function saveUpdate(room: string, data: Uint8Array): void { + if (!db) throw new Error("Database not initialized"); + const stmt = db.prepare("INSERT INTO updates (room, data) VALUES (?, ?)"); + stmt.run(room, data); +} + +export function getUpdates(room: string): Uint8Array[] { + if (!db) throw new Error("Database not initialized"); + const stmt = db.prepare("SELECT data FROM updates WHERE room = ? ORDER BY id ASC"); + const rows = stmt.all(room) as Array<{ data: Uint8Array }>; + return rows.map(row => row.data); +} + +export function close(): void { + if (db) { + db.close(); + db = null; + } +}