Reference
Architecture
How the element, sql.js and IndexedDB fit together, how the package is built, and what its limits are.
Runtime
application ──> <jeep-sqlite> element ──> Database (one per connection) ──> sql.js (WebAssembly)
│ │
│ └── export() on save, new SQL.Database(bytes) on open
└── localForage store "jeepSqliteStore" / table "databases" ──> IndexedDB- The element (
src/components/jeep-sqlite/jeep-sqlite.tsx) is the whole public surface. Every method is an@Methodreturning a promise, and every failure rejects with a string. It owns the connection dictionary: connections are keyedRW_<name>orRO_<name>, so a database can be open for writing and for reading at once. Database(src/utils/database.ts) is one connection. It initializessql.jswith alocateFilethat points atwasmPath(default/assets), loads the stored bytes, and runs statements.- The
utils-*modules hold the work that does not need the element: statement splitting and parsing, JSON import and export for synchronization, upgrade statements, table drops and deletes, and the store reads and writes. - The store is a single localForage instance. Each database is one
Uint8Arrayvalue under the key<name>SQLite.db. Nothing is written untilclose,closeConnection,saveToStore, or, withautosave, after each mutating statement. A tab that crashes between writes loses the writes since the last save.
Data flow and invariants
- Open reads the bytes from the store, or creates an empty database and stores it, then
constructs a
sql.jsdatabase from them. The whole file is in memory while open. - Statements run against the in-memory database.
executeandexecuteSetrun several statements,runone with bound values,queryreturns rows. A transaction opened withbeginTransactionstays open until committed or rolled back. - Save exports the in-memory database to bytes and replaces the stored value
(
removeItemthensetItem). - Synchronization is optional. A
sync_tableholds a last-synchronized date, tables carrylast_modifiedandsql_deletedcolumns, andimportFromJsonandexportToJsonmove only rows changed since that date.deleteExportedRowsphysically removes rows flagged deleted before the last export. - Versioning uses
PRAGMA user_versionwith incremental upgrade statements registered throughaddUpgradeStatement; each upgrade runs over the previous version.
Build
The package is built by Stencil (stencil build --docs), configured in stencil.config.ts.
| Output | Path | Used by |
|---|---|---|
| Lazy-loaded ES modules and CommonJS | dist/esm, dist/cjs, dist/index.js, dist/index.cjs.js | bundlers, through main, module and es2017 |
| Loader | loader/ | jeep-sqlite-current-sqljs/loader, defineCustomElements |
| Script build | dist/jeep-sqlite/ | <script type="module"> and CDNs |
| Custom elements bundle | dist/components/ | apps that want one chunk |
| Collection and types | dist/collection, dist/types | Stencil consumers and TypeScript |
www | www/ | the development server and the manual test pages in src/index_*.html; not published |
Choices that matter to a consumer:
- Polyfills are bundled.
sql.jsandjszipreference Node'sbufferandprocess.rollup-plugin-node-polyfillsis wired into the Rollup pipeline instencil.config.ts, andscripts/verify-browser-dist.mjsfails the build if any distributed module still imports either bare. Applications therefore need no polyfill plugin of their own. extras.enableImportInjectionis on, so production builds of Vite and similar bundlers resolve the lazy chunks.- Loader and WebAssembly are one pair. The embedded sql.js glue and
sql-wasm*.wasmmust come from the same sql.js release, or instantiation fails with aLinkError.sql.jsis pinned to an exact version, andscripts/copy-wasm.mjscopies both binaries from it intowasm/at build time, where they ship and are exported as./sql-wasm-browser.wasmand./sql-wasm.wasm. The two files have identical bytes; the glue asks for one name or the other depending on whether a bundler picks sql.js's browser build, and the browser build is the one embedded here.scripts/verify-package.mjschecks the shipped files against the installedsql.jsand that every name the builtdist/asks for is packed. - Shadow DOM. The element renders two buttons (pick a database, save to disk) inside a
shadow root; they appear only when
getFromLocalDiskToStoreorsaveToLocalDiskis called.
Tests
Unit specs run on Vitest through @stencil/vitest, whose stencil environment mounts the
element in a DOM. They live beside the source as *.spec.tsx. The interactive pages in
src/index_*.html are manual regression pages for individual upstream issues, served by
pnpm start; they are not automated.
Limits
- The whole database is in memory while open, and a save writes the whole file. Large databases and frequent saves are expensive.
IndexedDBquotas and eviction apply; the browser may evict storage for origins that have not requested persistent storage.- Encrypted databases are not supported on the web.
- WAL mode is accepted but there is one writer, the page; WAL2 is not supported.
- Two tabs open on the same database each hold their own in-memory copy and the last save wins.