typegres() is a sync schema handle; drivers imported explicitly - #95
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors typegres initialization to make typegres() a synchronous, module-load-safe schema handle (returning Database), moves async work into driver factories (only pglite() is async), renames attach() to connect(), and switches to explicit driver imports from typegres/drivers/* to remove dynamic-import machinery while keeping optional peers out of bundles that don’t use them.
Changes:
- Change
typegres()to a sync factory returningDatabase, and renamedb.attach(...)→db.connect(...)with dialect adopted from the first connected driver. - Introduce explicit driver entrypoints/factories under
typegres/drivers/*(pg,pglite,sqlite,doSqlite) and remove dynamic-import patterns in drivers. - Update tests/examples/site code and README to match the new initialization and driver-import model.
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.config.ts | Adds Vite alias mappings for the new typegres/drivers/* entrypoints in tests. |
| tsconfig.json | Adds TS path mappings for typegres/drivers/* subpath imports. |
| src/types/sqlite/table.test.ts | Updates SQLite test setup to use sync SqliteDriver.create() and db.connect(). |
| src/types/sqlite/smoke.test.ts | Updates SQLite smoke test to use db.connect() and sync driver creation. |
| src/types/postgres/index.test.ts | Updates pg tests to use sync PgDriver.create() and db.connect(). |
| src/test-helpers.ts | Updates shared pg test helper to use sync PgDriver.create() and db.connect(). |
| src/readme.test.ts | Makes README usage test self-contained (SQLite) and updates install/timeout assumptions. |
| src/live/sqlite/live.do-test.ts | Renames attach → connect in Durable Object live tests. |
| src/live/sqlite/db-live.test.ts | Renames attach → connect and updates SQLite driver creation in live tests. |
| src/live/exoeval-live.test.ts | Updates sqlite connection setup to db.connect(SqliteDriver.create(...)). |
| src/index.ts | Replaces async typegres(opts) with sync typegres(): Database and documents the new model. |
| src/hydrate.test.ts | Updates pglite setup to db.connect(await pglite()) with explicit driver import. |
| src/drivers/sqlite.ts | Makes SqliteDriver.create() synchronous and exports sqlite() driver factory. |
| src/drivers/pglite.ts | Removes dynamic import, keeps async driver creation, exports pglite() factory. |
| src/drivers/pg.ts | Removes dynamic import, makes PgDriver.create() synchronous, exports pg() factory. |
| src/drivers/do.ts | Adds doSqlite() factory and updates docs/examples to prefer it. |
| src/demo/demo.ts | Updates demo to sync typegres() plus db.connect(await pglite()). |
| src/database.ts | Implements optional dialect with adoption on first connect(), renames attach → connect. |
| src/database.test.ts | Updates database tests for sync PgDriver.create() and db.connect(). |
| src/builder/insert.test.ts | Updates SQLite insert tests to use typegres() + explicit sqlite driver factory. |
| site/src/demo/runtime.ts | Updates browser demo runtime to sync schema handle + awaited pglite() driver. |
| site/migrate.ts | Updates migration script to typegres() + db.connect(pg(...)). |
| README.md | Updates usage/backends docs to the new sync schema handle + explicit driver imports. |
| examples/sqlite/src/db.ts | Updates sqlite example to sync typegres() + db.connect(sqlite()). |
| examples/chat/worker/chat-do.ts | Updates DO example to db.connect(doSqlite(...)). |
| examples/chat/worker/api.ts | Switches schema handle to typegres() for module-load-safe table declarations. |
| examples/basic/src/db.ts | Updates basic example to sync typegres() + awaited pglite() driver connection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Zero setup: `typegres()` is a synchronous schema handle, and `sqlite()` | ||
| // with no filename is an in-memory database. Swap the driver for Postgres | ||
| // — everything below is unchanged. See Backends. |
| import { pg } from "typegres/drivers/pg"; // node-postgres | ||
| import { pglite } from "typegres/drivers/pglite"; // in-process WASM Postgres | ||
| import { sqlite } from "typegres/drivers/sqlite"; // better-sqlite3 | ||
| import { doSqlite} from "typegres/drivers/do"; // Cloudflare Durable Object |
| // `pg` is an *optional* peer dep (see package.json#peerDependenciesMeta), | ||
| // imported statically because this module only loads when the caller | ||
| // imports `typegres/drivers/pg` — browser and Worker bundles that never | ||
| // do never resolve it. Pool construction is synchronous; pg connects | ||
| // lazily on first query. |
| export const doSqlite = (storage: DoStorageLike): DoSqliteDriver => | ||
| new DoSqliteDriver(storage); |
There was a problem hiding this comment.
Here and other drivers: just expose the *Driver class and construct with .create(...). Don't expose a secondary top-level function.
| // `dialect` may be left unset at construction, in which case the first | ||
| // `connect()` adopts the driver's — so `typegres()` takes no arguments and | ||
| // the backend is named exactly once, where the driver is built. Declaring | ||
| // it up front is still allowed and buys a startup-time mismatch check. |
There was a problem hiding this comment.
Nope -- dialect shouldn't be on database at all. The driver should be source of truth and subsequent driver attaches should be same dialect.
| if (opts.name) { this.name = opts.name; } | ||
| } | ||
|
|
||
| // Known from construction, or adopted from the first connected driver. |
There was a problem hiding this comment.
Just remove dialect altogether from database -- or if is better, can be a passthrough to first driver's dialect.
| // Zero setup: `typegres()` is a synchronous schema handle, and `sqlite()` | ||
| // with no filename is an in-memory database. Swap the driver for Postgres | ||
| // — everything below is unchanged. See Backends. | ||
| const db = typegres(); |
| - [x] `Database.defaultConnection` — implicit connection for one-connection | ||
| deployments like Durable Objects |
There was a problem hiding this comment.
Remove this bullet point.
typegres() now takes no arguments and returns a Database, so table classes declare at module load with no top-level await. attach() becomes connect(), always synchronous — the async-ness moves to the drivers, and only PgliteDriver.create() is awaited. Drivers are imported explicitly from typegres/drivers/* and constructed with .create(), which deletes the dynamic-import machinery and its lint exemptions while still keeping optional peers out of the root bundle. Dialect is gone from Database: the driver is the source of truth, db.dialect is a passthrough to the first connected one, and later connects must agree. Compile-only suites (provenance, extractor, type-level match) connect a dialect-only test driver instead of declaring a dialect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
f1cd44c to
d2f7f19
Compare
typegres() now takes no arguments and returns a Database, so table classes declare at module load with no top-level await. attach() becomes connect(), always synchronous — the async-ness moves to the driver factories, and only pglite() is awaited.
Drivers come from typegres/drivers/* as explicit imports (sqlite, pg, pglite, doSqlite), which deletes the dynamic-import machinery and its lint exemptions while still keeping optional peers out of the root bundle. Dialect is adopted from the first connected driver.