SQLite persistence
Use this backend for a file-backed store or when an application already works with SQLite. It uses Node's built-in node:sqlite module and passes its string location to DatabaseSync.
EventStore.persistence.sqlite<TMap>(
location?: string
): Promise<TypedEventStore<TMap>>The optional TMap schema map is described in EventStore; it defaults to an unconstrained map.
import * as EventStore from "@jolly-pixel/event-store";
using store = await EventStore.persistence.sqlite("./events.sqlite");Passing a file path creates or opens a durable database, and creates the directories leading to it. The default location is ":memory:", which lasts only until its SQLite connection closes and touches no filesystem path.
Journal
For a file location, the factory sets these before running the schema:
| Pragma | Value | Effect |
|---|---|---|
auto_vacuum | INCREMENTAL | A file created without it is converted once with VACUUM |
journal_mode | WAL | Writes go to <location>-wal; readers do not block the writer |
synchronous | NORMAL | No fsync per commit; a power loss can drop the last commits, a process crash cannot |
SQLite keeps <location>-wal and <location>-shm next to the database while a connection is open and removes them on a clean close.
The factory returns a promise because the main package entry point loads the Node-only backend when the factory is called. Importing the main entry point is safe in a browser bundle as long as the SQLite factory is not called.
Node-only entry point
Node.js applications can import the factory directly:
import { createSqliteEventStore } from "@jolly-pixel/event-store/node";
using store = await createSqliteEventStore("./events.sqlite");The factory owns the connection it opens and closes it with the store.
Schema initialization
The factory runs the schema before returning. It creates the events and tags tables and their indexes when they do not exist, so a database made before tags existed gains the tags table the next time it is opened.
tags.create checks that the event exists and inserts the tag in one statement. The schema's unique (asset_id, name) constraint turns a duplicate name into an EventTagConflictError, whichever connection created the first one.
CREATE TABLE IF NOT EXISTS does not alter an existing table. A database made with a schema that predates the unique (asset_id, event_version) constraint must be migrated by the application or recreated.
Multiple connections
Each insert assigns the next asset version in the INSERT statement, while the schema's unique (asset_id, event_version) constraint prevents two connections from storing the same version for one asset. If SQLite rejects the insert, writer.append returns a Result error under the shared EventStore contract.
Compaction
compact deletes superseded rows. With the default reclaim: true, it releases the freed pages after a deletion: with PRAGMA incremental_vacuum when the database uses incremental auto-vacuum (the factory's default), with VACUUM otherwise. Pass reclaim: false to keep the freed pages; later inserts reuse them, so the file stops growing without shrinking.
A conditional append (expectedVersion) checks the asset's newest version in the same INSERT statement that assigns the next one.