Skip to content

Pluggable DB adapter: async API, sql.js becomes opt-in - #44

Merged
chtitux merged 5 commits into
mainfrom
pluggable-db-adapter
Apr 20, 2026
Merged

Pluggable DB adapter: async API, sql.js becomes opt-in#44
chtitux merged 5 commits into
mainfrom
pluggable-db-adapter

Conversation

@chtitux

@chtitux chtitux commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements documents/pluggable-db-adapter.md.

  • Core library no longer imports sql.js. GtfsSqlJs talks to a narrow async GtfsDatabase interface.
  • sql.js adapter ships at subpath gtfs-sqljs/adapters/sql-js (createSqlJsAdapter). Other drivers (better-sqlite3, op-sqlite, expo-sqlite) plug in via the same interface.
  • Reference BetterSqlite3Adapter in examples/adapters/, exercised by a new CI end-to-end test (tests/e2e-better-sqlite3.test.ts).
  • New GtfsSqlJs.attach(db, options?) entry point for caller-managed handles (file-backed drivers).
  • sql.js moves from dependencies to optional peerDependency; better-sqlite3 added to devDependencies.

Breaking changes

  • Every gtfs.* query method is now async / returns Promise<T> — call sites must await.

  • options.adapter is required on fromZip / fromZipData / fromDatabase. The sql.js migration is:

    - const gtfs = await GtfsSqlJs.fromZip(url, { locateFile });
    - const routes = gtfs.getRoutes();
    + import { createSqlJsAdapter } from 'gtfs-sqljs/adapters/sql-js';
    + const gtfs = await GtfsSqlJs.fromZip(url, { adapter: await createSqlJsAdapter({ locateFile }) });
    + const routes = await gtfs.getRoutes();
  • SQL / locateFile removed from GtfsSqlJsOptions (moved onto createSqlJsAdapter).

  • sql.js type re-exports dropped from the public surface; consumers migrate to GtfsDatabase or import from sql.js directly.

  • Cache layer catches ExportNotSupportedError from adapters that cannot serialize in-memory and logs a warning instead of failing — file-backed drivers persist their own DB on disk.

Test plan

  • npm run lint — clean
  • npm run typecheck — clean
  • npm test — 143 tests passing across sql.js + better-sqlite3 adapters
  • npm run build — clean (dist/index.js + dist/adapters/sql-js/index.js)
  • CI green on Node 18 / 20 / 21

🤖 Generated with Claude Code

chtitux and others added 5 commits April 20, 2026 18:53
Implements the plan in documents/pluggable-db-adapter.md.

The core library no longer imports sql.js. `GtfsSqlJs` now talks to a
narrow async `GtfsDatabase` interface; consumers pick an adapter. A
sql.js adapter ships as a subpath module (`gtfs-sqljs/adapters/sql-js`),
and a reference better-sqlite3 adapter lives under `examples/adapters/`.

Key changes:
- New `src/adapters/types.ts` (GtfsDatabase, GtfsStatement,
  GtfsDatabaseAdapter, SqlValue, Row, ExportNotSupportedError)
- New `src/adapters/sql-js/index.ts` with `createSqlJsAdapter`
- All query / loader / schema code now `async` and uses GtfsDatabase
- `GtfsSqlJs.attach(db, options)` entry point for caller-managed
  handles (file-backed drivers: better-sqlite3, op-sqlite, expo-sqlite)
- `options.adapter` is required on fromZip / fromZipData / fromDatabase
- Cache layer catches ExportNotSupportedError and warns + no-ops
- sql.js moves to optional peerDependency; better-sqlite3 is a
  devDependency powering a new end-to-end CI test
  (`tests/e2e-better-sqlite3.test.ts`)
- Breaking: every gtfs.* method returns `Promise<T>`; call sites must
  `await`. CHANGELOG documents the migration diff.

All 143 tests pass (sql.js + better-sqlite3 paths). Build, typecheck,
and lint are clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Node 18 does not expose `crypto` as a global, so `crypto.subtle.digest`
threw `ReferenceError: crypto is not defined` whenever the cache path
ran (first surfaced by the new better-sqlite3 e2e test). Guard the
lookup and lazily import `node:crypto`'s webcrypto when
`globalThis.crypto` is absent. Browsers and RN always go through the
global path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves the reference adapter from `examples/adapters/BetterSqlite3Adapter.ts`
into `src/adapters/better-sqlite3/index.ts` and ships it as an opt-in
subpath export, identical in shape to the sql.js adapter:

- New subpath: `gtfs-sqljs/adapters/better-sqlite3`, exporting
  `wrapBetterSqlite3` and `createBetterSqlite3Adapter`.
- `tsup.config.ts` gains the third entry; built artifact lives at
  `dist/adapters/better-sqlite3/index.js`.
- `package.json`: new `exports` entry and `better-sqlite3` added as an
  optional `peerDependency` (kept in `devDependencies` for the CI test).
- `tests/e2e-better-sqlite3.test.ts` now imports from the real adapter
  path; the ad-hoc `tests/helpers/better-sqlite3-adapter.ts` copy is
  deleted.

No behavior change — same wrapping code, just promoted. All 143 tests
still pass; build, typecheck, and lint are clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
README:
- Expand tagline: adapters for sql.js (browser / Node WASM) and
  better-sqlite3 (Node native), RN support noted.
- Installation now names the adapter peer dependencies.
- Quick Start shows both sql.js (fromZip) and better-sqlite3 (attach).
- New "Adapters" section with the two built-in subpaths and the
  factory vs. attach() entry points.
- API reference: all instance methods return Promise<T>; `adapter` is
  required on fromZip/fromZipData/fromDatabase; document `attach()`.
- TypeScript type-imports section includes the adapter surface.

Usage Guide (documents/guide.md):
- New "Adapters overview" section up front.
- Rewritten "Loading the sql.js WASM File" around createSqlJsAdapter.
- Rewritten "Creating an Instance" with six scenarios covering sql.js
  (ZIP URL / ZIP bytes / .db bytes) and better-sqlite3 (attach file /
  factory in-memory / factory file-backed), plus a decision table and
  a sketch of a custom adapter.
- Every query example now `await`s.
- Fixed the `getCalendars({ serviceId })` reference (the real method
  is `getCalendarByServiceId(serviceId)`); added `getCalendarDatesForDate`.
- Cache examples plumb the adapter through; added a note on
  ExportNotSupportedError behavior with file-backed adapters.
- Direct Database Access rewritten around the async GtfsDatabase surface.
- Complete Example at the bottom rewritten to await everything and
  close the DB properly (previous version had `await` inside a
  non-async `.map`).

No source changes — docs only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous version listed the breaking changes as bullets but did not
actually walk a user through the upgrade. It was missing:
- the `npm install sql.js` step (sql.js is now a peer dep)
- before/after diffs for the common sql.js call sites
- the `getDatabase()` return-type shift (now returns a GtfsDatabase
  with async methods — silent failure for anyone doing direct
  prepare/step/getAsObject calls)
- the better-sqlite3 `attach()` path as a concrete alternative
- a table mapping removed options (`SQL`, `locateFile`,
  re-exported sql.js types) to their new homes
- pointers into README and documents/guide.md

Restructured as: unchanged → install → diff your queries → diff
getDatabase() → attach() option → removed/renamed options table.

Docs only; no source changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chtitux
chtitux merged commit 313157e into main Apr 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant