Problem
The plugin currently depends on better-sqlite3 as its SQLite engine for Node.js and Electron runtimes, while using bun:sqlite for Bun. This creates several maintenance burdens:
better-sqlite3 is a native C++ addon — requires node-gyp/prebuild to compile per-platform, causing install failures in environments without build tooling.
- Electron ABI mismatches — the plugin ships with a custom Electron prebuild downloader (
native-binding.ts, ~250 lines of complex fetch/cache/probe logic) to work around better-sqlite3's prebuild incompatibility with Electron's NODE_MODULE_VERSION.
- Two code paths — Bun (
bun:sqlite) vs Node/Electron (better-sqlite3) use slightly different APIs, requiring careful wrapper code in sqlite.ts.
better-sqlite3 still ships in the bundle even when running on Bun, bloating the plugin artifact with a dependency that is never loaded.
Context
Magic Context's SQLite wrapper (src/shared/sqlite.ts) already dynamically detects the runtime and imports the correct backend. The current dispatch is:
| Runtime |
Backend |
Status |
| Bun |
bun:sqlite |
Built-in, zero deps |
| Node (current, < 24) |
better-sqlite3 |
External native addon |
| Node 24+ |
node:sqlite |
Built-in since 24.0.0 |
| Electron |
better-sqlite3 + custom ABI downloader |
External native addon |
Node.js 24 (released April 2026) ships a stable node:sqlite module with synchronous APIs (DatabaseSync, StatementSync), making it a viable drop-in alternative to better-sqlite3 for the Node path. Combined with bun:sqlite for Bun, the plugin could eliminate all external native SQLite dependencies.
Request
Replace better-sqlite3 with node:sqlite (Node 24+) as the default SQLite backend for Node.js/Electron runtimes, falling back to better-sqlite3 only for Node < 24.
Proposed Solution
-
Create a DatabaseSync abstraction (src/shared/sqlite-backend.ts or similar) with a unified interface that better-sqlite3, bun:sqlite, and node:sqlite can all satisfy:
new Database(path, opts) — constructor
db.prepare(sql) → statement with .run(), .get(), .all(), .transaction(fn)
db.exec(sql) — multi-statement
db.close()
-
Three-way dispatch in sqlite.ts:
- Bun →
bun:sqlite (unchanged)
- Node >= 24 →
node:sqlite (new)
- Node < 24 / Electron →
better-sqlite3 (legacy fallback)
-
Remove native-binding.ts once Electron ships a Node 24+ runtime that includes node:sqlite — the custom Electron prebuild downloader becomes unnecessary.
-
Drop better-sqlite3 from dependencies (or move to optionalDependencies) so Bun-only installs don't ship it at all.
Key Considerations
-
API surface: node:sqlite uses DatabaseSync and StatementSync with a slightly different API from better-sqlite3/bun:sqlite. The wrapper needs to bridge:
better-sqlite3: new Database(path), prepare(sql).run(...params), prepare(sql).get(...params), prepare(sql).all(...params)
node:sqlite: new DatabaseSync(path), stmt.prepare(sql), stmt.run(...params), stmt.get(...params), stmt.all(...params)
bun:sqlite: mostly API-compatible with better-sqlite3's surface
-
Transactions: node:sqlite has db.exec("BEGIN/COMMIT/ROLLBACK") but no built-in transaction(fn) wrapper. The existing better-sqlite3.transaction() pattern needs to be replicated.
-
WAL mode / pragmas: All three engines support PRAGMA via exec(), so no issue there.
-
Bundling: The /* @vite-ignore */ + "bun:" + "sqlite" specifier-mangling pattern in sqlite.ts will need an additional variant for "node:" + "sqlite" to defeat esbuild static analysis.
Out of Scope for This Issue
- Adding SQLite support for Pi/CLI on Node < 24 —
better-sqlite3 stays as a fallback.
- Removing the
native-binding.ts Electron prebuild downloader — that can be cleaned up separately once Electron's underlying Node version reaches 24.
Additional Notes
The chokepoint in src/shared/sqlite.ts already has the right architecture for this change. The statement and transaction primitives across call sites use a common subset (prepare().run/get/all, db.transaction(), db.exec(), db.close()), which makes it feasible to introduce a third backend without touching the 150+ call sites.
Problem
The plugin currently depends on
better-sqlite3as its SQLite engine for Node.js and Electron runtimes, while usingbun:sqlitefor Bun. This creates several maintenance burdens:better-sqlite3is a native C++ addon — requiresnode-gyp/prebuild to compile per-platform, causing install failures in environments without build tooling.native-binding.ts, ~250 lines of complex fetch/cache/probe logic) to work aroundbetter-sqlite3's prebuild incompatibility with Electron's NODE_MODULE_VERSION.bun:sqlite) vs Node/Electron (better-sqlite3) use slightly different APIs, requiring careful wrapper code insqlite.ts.better-sqlite3still ships in the bundle even when running on Bun, bloating the plugin artifact with a dependency that is never loaded.Context
Magic Context's SQLite wrapper (
src/shared/sqlite.ts) already dynamically detects the runtime and imports the correct backend. The current dispatch is:bun:sqlitebetter-sqlite3node:sqlitebetter-sqlite3+ custom ABI downloaderNode.js 24 (released April 2026) ships a stable
node:sqlitemodule with synchronous APIs (DatabaseSync,StatementSync), making it a viable drop-in alternative tobetter-sqlite3for the Node path. Combined withbun:sqlitefor Bun, the plugin could eliminate all external native SQLite dependencies.Request
Replace
better-sqlite3withnode:sqlite(Node 24+) as the default SQLite backend for Node.js/Electron runtimes, falling back tobetter-sqlite3only for Node < 24.Proposed Solution
Create a
DatabaseSyncabstraction (src/shared/sqlite-backend.tsor similar) with a unified interface thatbetter-sqlite3,bun:sqlite, andnode:sqlitecan all satisfy:new Database(path, opts)— constructordb.prepare(sql)→ statement with.run(),.get(),.all(),.transaction(fn)db.exec(sql)— multi-statementdb.close()Three-way dispatch in
sqlite.ts:bun:sqlite(unchanged)node:sqlite(new)better-sqlite3(legacy fallback)Remove
native-binding.tsonce Electron ships a Node 24+ runtime that includesnode:sqlite— the custom Electron prebuild downloader becomes unnecessary.Drop
better-sqlite3fromdependencies(or move tooptionalDependencies) so Bun-only installs don't ship it at all.Key Considerations
API surface:
node:sqliteusesDatabaseSyncandStatementSyncwith a slightly different API frombetter-sqlite3/bun:sqlite. The wrapper needs to bridge:better-sqlite3:new Database(path),prepare(sql).run(...params),prepare(sql).get(...params),prepare(sql).all(...params)node:sqlite:new DatabaseSync(path),stmt.prepare(sql),stmt.run(...params),stmt.get(...params),stmt.all(...params)bun:sqlite: mostly API-compatible withbetter-sqlite3's surfaceTransactions:
node:sqlitehasdb.exec("BEGIN/COMMIT/ROLLBACK")but no built-intransaction(fn)wrapper. The existingbetter-sqlite3.transaction()pattern needs to be replicated.WAL mode / pragmas: All three engines support
PRAGMAviaexec(), so no issue there.Bundling: The
/* @vite-ignore */+"bun:" + "sqlite"specifier-mangling pattern insqlite.tswill need an additional variant for"node:" + "sqlite"to defeat esbuild static analysis.Out of Scope for This Issue
better-sqlite3stays as a fallback.native-binding.tsElectron prebuild downloader — that can be cleaned up separately once Electron's underlying Node version reaches 24.Additional Notes
The chokepoint in
src/shared/sqlite.tsalready has the right architecture for this change. Thestatementandtransactionprimitives across call sites use a common subset (prepare().run/get/all,db.transaction(),db.exec(),db.close()), which makes it feasible to introduce a third backend without touching the 150+ call sites.