Files
suaveplan/packages/communication/messaging/imessage-adapter/src/sqlite/bun-sqlite-reader.md
2026-05-27 23:29:53 +01:00

2.9 KiB

bunSqliteReader

(no submodules)

Purpose

Production-grade SqliteReader backed by Bun's built-in bun:sqlite driver. The reader is the single seam through which IMessageWatcher reaches Apple's chat.db SQLite database; replacing upstream's require("bun:sqlite") as any cast with a typed, swappable factory lets tests inject a reader pointing at a real temp chat.db without re-implementing the SQL layer.

Features

  • Lazy module resolution via createRequire(import.meta.url) — the bun:sqlite import is not added to the static graph, so vite and vitest do not attempt to resolve it on non-Bun runners during build / type-check / lint passes.
  • Errors thrown from the load step or the new Database(...) call are wrapped in IMessageDbOpenFailedError carrying the offending path on context.path, so the watcher can surface a stable error code to consumers.
  • Returns a SqliteHandle whose execScript member maps to Database.run (Bun's bulk-SQL entry point) and whose prepare member returns a SqliteStatement wrapping Bun's per-statement all / get / run methods structurally — production and test code share the exact same call shape.
  • Read-only by default: open(path, { readonly: true }) opens the database read-only as required for ~/Library/Messages/chat.db, while tests open with readonly: false to populate the fixture.

Usage: Basic

import { bunSqliteReader } from "@suaveplan/imessage-adapter";

const handle = bunSqliteReader.open("/path/to/chat.db", { readonly: true });
const rows = handle.prepare("SELECT ROWID FROM message LIMIT 1").all();
handle.close();

Usage: Advanced

import { createBunSqliteReader } from "@suaveplan/imessage-adapter";

// Inject a custom loader (e.g. for vitest under WSL2 where bun:sqlite is
// reached through a different worker bridge).
const reader = createBunSqliteReader(() => require("bun:sqlite"));
const handle = reader.open(path, { readonly: false });
handle.execScript("CREATE TABLE foo (id INTEGER PRIMARY KEY)");

API Reference

  • bunSqliteReader: SqliteReader — default reader, ready for production wiring.
  • createBunSqliteReader(loader?: BunSqliteLoader): SqliteReader — factory accepting a custom loader; defaults to a createRequire-based one that resolves bun:sqlite against the module's own URL.
  • BunSqliteLoader = () => BunSqliteModule — the loader contract.

Implementation Notes

createRequire(import.meta.url) is the documented Node + Bun bridge for resolving CommonJS / built-in modules from ESM without statically declaring them. We use it because static import "bun:sqlite" would make vite resolve the specifier at build time and fail on non-Bun hosts (CI, lint). The BunDatabase interface is intentionally minimal — it only declares the methods the watcher actually calls, so changes to Bun's broader Database surface cannot bleed into the package.