Skip to content

How it fits together ​

The package runs in three places: the views and the client in the page, the host in the app's backend and a native helper beside it.

Three layers ​

LayerEntry pointRuns in
Views and the client@adecore/databaseThe page: React 19, in a browser window
Host@adecore/database/hostThe app's backend: Bun or Node
Helperadecore-databaseA native process the host starts

Two entry points import nothing from React or Node, so any layer can use them. @adecore/database/protocol holds the message types, and @adecore/database/testing the in-memory transport. The platform packages (@adecore/database-darwin-arm64 and three others) carry the prebuilt helper.

page            views, createDatabaseClient(transport)
  |
app channel     ipcMain.handle, a utility process, a WebSocket
  |
backend         createDatabaseHost, spawnHelper
  |
helper          adecore-database, one JSON message per line
  |
server          SQLite file, MySQL or MariaDB

The views never talk to a server. They call a DatabaseSession, and the client sends protocol requests through a DatabaseTransport, one function from request to response. The app implements it over a channel it already has, such as Electron IPC or a WebSocket.

At the other end, the app hands each request to the host. The host checks the request's shape, keeps each owner's sessions apart, asks the app about connections, files and discovery, and forwards to the helper. The package registers no channel, because the app must check the sender first. See Security.

DatabaseProvider takes onAction, files and storage, so the app decides where a table opens, which file dialog appears and where settings live.

Why a separate helper ​

The database drivers live in a Rust binary, not in the backend:

  • A driver crash takes down only the helper. The host fails the running requests with helper-exited and starts a new helper on the next request.
  • The same binary runs under Bun, Node and Electron's utility process, with no native module to rebuild.
  • The helper pages rows and cuts long cells to a preview, so the backend never holds a whole table.

What crosses the wire ​

Every message is JSON.

  • Integers beyond Number.MAX_SAFE_INTEGER, decimals, dates and times arrive as text. The column's kind says how to read them.
  • A long cell arrives as a preview: { kind: 'longText', preview, length } for text and { kind: 'binary', hex, length } for bytes. The cell method fetches the whole value.
  • Rows come in pages that say whether more follow. Counting rows is a separate, slower request. An export streams rows to a file without passing them through the page.

Licenses and third-party notices are listed in each package.