> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# UrsulaDatabase

Last updated 29 October 2026

`UrsulaDatabase` is the connection [open()](https://tonbo.dev/docs/reference/nodejs/open.md) returns: a [node:sqlite](https://nodejs.org/docs/latest-v22.x/api/sqlite.html) [DatabaseSync](https://nodejs.org/docs/latest-v22.x/api/sqlite.html#class-databasesync) with the members below.

## UrsulaDatabase

Exported by `@tonbo/sqlite-ursula` and `/tonbo`.

```text
class UrsulaDatabase extends DatabaseSync {
  readonly name: string;
  readonly boundPath: string;
  synced(options?: { signal?: AbortSignal }): Promise<void>;
  status(): UrsulaStatus;
  on(event: "reset", listener: ResetListener): this;
  off(event: "reset", listener: ResetListener): this;
  close(): void;
}

type ResetListener = (error: UrsulaResetError) => void;
```

Every `DatabaseSync` method works as in `node:sqlite`. The ones that run SQL (`exec`, `applyChangeset`, and `run`, `get`, `all` and `iterate` of the statements `prepare` returns) throw an [UrsulaError](https://tonbo.dev/docs/reference/nodejs/errors.md#ursula-error) for a failure of the extension and every other error unchanged. Get a connection only from `open()`: one constructed directly, as a library may do through `db.constructor`, is a plain `DatabaseSync` that classifies nothing.

| Member | What it is |
| --- | --- |
| `name` | The stream’s path, such as `/acme/notes`. |
| `boundPath` | The local database file the extension bound, as `status().path` reports it. |
| `synced()` | Resolves once the stream holds every earlier commit. See [synced()](#synced). |
| `status()` | The database’s state, synchronously. See [Status](https://tonbo.dev/docs/reference/nodejs/status.md). |
| `on("reset", listener)`, `off("reset", listener)` | Add or remove a [reset listener](#reset-listener). Another event name throws `TypeError`. Both return the database. |
| `close()` | Closes the connection. See [close()](#close). |

## synced()

```text
synced(options?: { signal?: AbortSignal }): Promise<void>
```

Resolves once the stream holds every commit this process logged before the call, and a round trip that started after the call found this process still the owner. Await it before anything outside the process acts on what you wrote or read; [gate()](https://tonbo.dev/docs/reference/nodejs/gate-and-import.md#gate) does that for a handler. It does not block the event loop, and it keeps the process alive while it waits.

- Network failures never reject it: it waits. Past `maxPendingBytes`, writes fail with `backlog_full` meanwhile.
- It rejects with the `UrsulaResetError` on a reset, and with `signal.reason` when `signal` fires; the commits keep replicating.
- On a reader cache (a `readOnly` open in a process that does not own the stream) it resolves at once: the reader holds no commit.
- On a database that retired it rejects with `UrsulaSetupError`.

What the stream then holds, and how durably, is on [When a commit is durable](https://tonbo.dev/docs/databases/how-it-works.md#commit).

## Reset listeners

`ResetListener` is `(error: UrsulaResetError) => void`. When the database is reset, its pending `synced()` calls reject first, then each listener runs once with the reset. A listener that throws does not stop the others: its error is rethrown outside, as an uncaught exception, as an `EventEmitter`’s would be.

```js
db.on("reset", (error) => {
  if (error.action === "stop") process.exit(1); // another process took it over
  // reopen or fix: stop taking work, then open() the database again
});
```

## After a reset

Every statement of the database throws the [UrsulaResetError](https://tonbo.dev/docs/reference/nodejs/errors.md#ursula-reset-error), reads included, except reads in a read transaction it had open, which keep their snapshot; a write in it still fails. `open()` the database again, or stop when the action is `stop`. The new open need not wait for the old connection to close: it works on a copy of the local cache, which carries every commit the old one made. Close the old connection once you no longer need it.

A refused write or a reset rolls SQLite’s whole transaction back. A `ROLLBACK` that a transaction helper sends afterwards succeeds as a prepared statement, and through `exec` it throws the same `UrsulaError` again, so the error you handle is the package’s, not “no transaction is active”.

## close()

Closes the connection, synchronously. Once a binding’s last connection closes, the commits left replicate in the background and the binding retires; a process that exits before then leaves them in the local log, and the next open sends them. To use the database again, call the package’s `open()`: `DatabaseSync`’s own `open()` method binds nothing.

## Salvage

A local cache an open could not keep is moved into `salvage/`, and [status().salvage](https://tonbo.dev/docs/reference/nodejs/status.md#ursula-salvage) lists it. When that happens and what to do with it is on [Backups and salvage](https://tonbo.dev/docs/databases/backups.md#salvage).
