npm package: @meck93/bun-sqlite-change-hooks
Description: 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).
Latest version: 0.1.4

# @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

```bash
bun add @meck93/bun-sqlite-change-hooks
```

## Usage

```typescript
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

```typescript
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

```typescript
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 service
- `initialize(database)` — compile + load C extension, start polling
- `shutdown()` — stop polling, release resources
- `isEnabled()` / `isInitialized()` — status checks
- `getHealthStatus()` — health info object

### `createSQLiteChangeHookWorkerRuntime`

- `createSQLiteChangeHookWorkerRuntime({ database, config?, logger? })` — start worker message handling
- Handles `sqlite-change-hooks:init` and `sqlite-change-hooks:shutdown` protocol messages
- Emits `sqlite-change-hooks:event` messages with `DatabaseChangePayload`
- Returns `{ isInitialized, dispose }`

### `SQLiteChangeHookWorkerClient`

- `new SQLiteChangeHookWorkerClient({ worker, onChange, requestTimeoutMs?, logger? })`
- `initialize(config?)` — starts the worker-side hook service
- `shutdown()` — stops the worker-side hook service
- `dispose()` — removes listeners and rejects pending requests
- `isInitialized()` — 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