@meck93/bun-sqlite-change-hooks
v0.1.4
Published
SQLite change notification hooks for Bun. Uses a C extension (`sqlite3_update_hook`) with a ring-buffer and polling service to deliver row-level change events (INSERT/UPDATE/DELETE).
Readme
@meck93/bun-sqlite-change-hooks
SQLite change notification hooks for Bun. Uses a C extension (sqlite3_update_hook) with a ring-buffer and polling service to deliver row-level change events (INSERT/UPDATE/DELETE).
Bun has no native sqlite3_update_hook support, so this package fills the gap.
Install
bun add @meck93/bun-sqlite-change-hooksUsage
import { configureCustomSqlite, SQLiteChangeHookService } from "@meck93/bun-sqlite-change-hooks";
import Database from "bun:sqlite";
// macOS: use Homebrew SQLite (required for extension loading)
configureCustomSqlite();
const db = new Database("my.db");
const hooks = new SQLiteChangeHookService({
onChange: event => {
console.log(event); // { operation, tableName, rowId, timestamp }
},
config: { pollIntervalMs: 50, maxChangesPerPoll: 256 }, // optional
logger: console, // optional
});
await hooks.initialize(db);
// ... changes to db will trigger onChange callbacks
hooks.shutdown();Worker Mode (Optional)
When your SQLite connection lives inside a Bun worker, run the hook service in the worker and forward
events to the main thread with SQLiteChangeHookWorkerClient.
Worker file
import { configureCustomSqlite, createSQLiteChangeHookWorkerRuntime } from "@meck93/bun-sqlite-change-hooks";
import Database from "bun:sqlite";
configureCustomSqlite();
const database = new Database("my.db");
createSQLiteChangeHookWorkerRuntime({
database,
config: { pollIntervalMs: 50, maxChangesPerPoll: 256 },
});Main thread
import { SQLiteChangeHookWorkerClient } from "@meck93/bun-sqlite-change-hooks";
const worker = new Worker(new URL("./sqlite.worker.ts", import.meta.url), { type: "module" });
const hooks = new SQLiteChangeHookWorkerClient({
worker,
onChange: event => {
console.log(event);
},
requestTimeoutMs: 5000, // optional
});
await hooks.initialize();
// ...
await hooks.shutdown();
hooks.dispose();
worker.terminate();API
SQLiteChangeHookService
new SQLiteChangeHookService({ onChange, config?, logger? })— create serviceinitialize(database)— compile + load C extension, start pollingshutdown()— stop polling, release resourcesisEnabled()/isInitialized()— status checksgetHealthStatus()— health info object
createSQLiteChangeHookWorkerRuntime
createSQLiteChangeHookWorkerRuntime({ database, config?, logger? })— start worker message handling- Handles
sqlite-change-hooks:initandsqlite-change-hooks:shutdownprotocol messages - Emits
sqlite-change-hooks:eventmessages withDatabaseChangePayload - Returns
{ isInitialized, dispose }
SQLiteChangeHookWorkerClient
new SQLiteChangeHookWorkerClient({ worker, onChange, requestTimeoutMs?, logger? })initialize(config?)— starts the worker-side hook serviceshutdown()— stops the worker-side hook servicedispose()— removes listeners and rejects pending requestsisInitialized()— client-side initialization status
configureCustomSqlite()
On macOS, configures Bun to use Homebrew's SQLite (supports extension loading). No-op on other platforms.
resolveSqliteIncludeDir()
Resolves the SQLite header include directory for C extension compilation.
Environment Variables
| Variable | Description |
| ------------------------------------ | ---------------------------------------------- |
| SQLITE_CHANGE_HOOKS_ENABLED | Enable/disable hooks (default: true) |
| SQLITE_CHANGE_POLL_INTERVAL_MS | Poll interval in ms (default: 50) |
| SQLITE_CHANGE_MAX_CHANGES_PER_POLL | Max events per poll (default: 256) |
| SQLITE_CHANGE_HOOKS_DEBUG | Enable debug logging (default: false) |
| SQLITE_CHANGE_HOOKS_BUILD_DIR | Extension build directory (default: OS tmpdir) |
| SQLITE_CHANGE_HOOKS_EXTENSION_PATH | Pre-built extension path (skips compilation) |
| SQLITE_CUSTOM_LIB_PATH | Custom SQLite library path (macOS) |
| SQLITE_CHANGE_HOOKS_INCLUDE_DIR | SQLite header include directory |
License
MIT
