Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
34980df
refactor(types): move postgres-specific types under src/types/postgres/
ryanrasti May 27, 2026
e33b36f
refactor(types): rename PgFunc/PgOp/PgSrf runtime helpers
ryanrasti May 27, 2026
48bc394
refactor(types): per-dialect deserialize registry
ryanrasti May 27, 2026
b4dd4b0
refactor(builder): add CompileContext + Cast AST node
ryanrasti May 27, 2026
3259797
refactor(types): extract boolAnd/boolOr/boolNot helpers from Bool
ryanrasti May 27, 2026
3908120
refactor(types): extract SqlValue base + Dialect object
ryanrasti Jul 1, 2026
f6c09ce
feat(sqlite): initial SQLite dialect scaffold + runtime inference tool
ryanrasti Jul 1, 2026
47e80be
feat(sqlite): SqliteDriver + end-to-end smoke tests
ryanrasti Jul 1, 2026
83e7f15
feat(sqlite): Table + QueryBuilder work end-to-end over SQLite
ryanrasti Jul 1, 2026
3c21f8e
refactor(compile): CompileContext carries Database; drivers take text…
ryanrasti Jul 1, 2026
312065a
feat(provenance): Ident tagging via db.Table + column re-alias
ryanrasti Jul 1, 2026
5d0355c
feat(provenance): dialect tagging on Func/Op/UnaryOp/Cast/Srf
ryanrasti Jul 1, 2026
87248a5
feat(sqlite): codegen emits funcCall helper (dialect-tagged Func nodes)
ryanrasti Jul 1, 2026
51bfd38
refactor(builders): .where(true) is a matchAll flag, no dialect-speci…
ryanrasti Jul 1, 2026
ba3b203
feat(sqlite): SqliteValue.in() (non-generic signature)
ryanrasti Jul 1, 2026
0fe00e1
chore(provenance): resolve ISSUES.md #16; clean up lint
ryanrasti Jul 1, 2026
96a36da
refactor(api): split Database (metadata) + Connection (runtime)
ryanrasti Jul 1, 2026
68f74cd
docs(issues): mark Database/Connection split complete under #16
ryanrasti Jul 1, 2026
88485f8
feat(provenance): sweep tests to db.Table + provenance-enforcement suite
ryanrasti Jul 1, 2026
66734ae
feat(provenance): remove sql.ident from public helper
ryanrasti Jul 1, 2026
0f26696
chore: address copilot/review feedback (small fixes batch)
ryanrasti Jul 1, 2026
e64c00c
refactor(sqlite): table.test wrapped in txns + type asserts + expecte…
ryanrasti Jul 1, 2026
8f6072e
refactor(compile): ctx auto-detected from Sql tree
ryanrasti Jul 1, 2026
4a84ced
feat(sqlite): codegen emits runtime.match dispatch + JS primitives
ryanrasti Jul 1, 2026
663ca36
docs(issues): revert ISSUES.md #16 — provenance work shipped
ryanrasti Jul 1, 2026
e5634a4
refactor: drop SharedBool alias — PG Bool import is gone, no collision
ryanrasti Jul 1, 2026
d413b2c
refactor(builder): drop pgCtx fallback + isTableClass probe in debug()
ryanrasti Jul 2, 2026
c109d69
refactor: TableBase.database required + drop pgCtx/sqliteCtx sentinels
ryanrasti Jul 2, 2026
a55ae0c
refactor(sql): paramForDialect helper + comment cleanup
ryanrasti Jul 2, 2026
b3af5a6
refactor(live/bus): tag EVENTS_TABLE Ident with the bus's database
ryanrasti Jul 2, 2026
474756b
refactor(live): tag DDL + transformer schema Idents with database
ryanrasti Jul 2, 2026
7dab427
refactor: use database.scopedIdent for library-internal tagged Idents
ryanrasti Jul 3, 2026
d5dbe07
refactor: replace deserialize registry with per-class overrides
ryanrasti Jul 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,20 @@ Conventions that aren't lint-enforced. If you're writing code in this repo, foll
```

Using the typegres `Record` class as a type (e.g. `Record<O, 1>` as a return of `scalar()`) is fine — that's the intended use of the exported class.

## Comments describe present code, not deleted code

- Don't leave comments that reference something you removed ("X removed", "no longer supports Y", "used to do Z"). They rot as history moves on and confuse readers who don't have the deleted code in mind. The commit message and git history are where that context lives.

If a comment needs to explain the *absence* of an obvious thing (e.g. no `sql.ident` helper), phrase it as a positive statement about the current design and why:

```ts
// Don't:
// Untagged Ident construction removed. Callers who need a schema-
// referencing identifier go through `db.scopedIdent(name)`.

// Do:
// No `sql.ident` helper: schema-referencing identifiers must carry
// a DatabaseRef for the compile-time provenance check, so callers
// go through `db.scopedIdent(name)`.
```
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ npm install typegres pg
```typescript
import { typegres, Int8, Text, expose } from "typegres";

const db = await typegres({
const { db, conn } = await typegres({
type: "pg",
connectionString: process.env.DATABASE_URL!,
});
Expand All @@ -43,10 +43,10 @@ const rows = await Users.from()
id: users.id,
name: users.fullName(),
}))
.execute(db);
.execute(conn);

console.log(rows);
await db.close();
await conn.close();
```

For a complete scaffold with migrations + codegen, see
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ export default [
},
},
{
ignores: ["dist/", "src/types/generated/"],
ignores: ["dist/", "src/types/postgres/generated/"],
},
];
2 changes: 1 addition & 1 deletion examples/basic/src/db.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { typegres } from "typegres";

export const db = await typegres({ type: "pglite" });
export const { db, conn } = await typegres({ type: "pglite" });
38 changes: 19 additions & 19 deletions examples/basic/src/dogs.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { test, expect, expectTypeOf, beforeAll } from "vitest";
import { sql } from "typegres";
import { db } from "./db";
import { db, conn } from "./db";
import { Dogs } from "./tables/dogs";
import { Teams } from "./tables/teams";
import { Collars } from "./tables/collars";
Expand All @@ -9,48 +9,48 @@ import { Microchips } from "./tables/microchips";

beforeAll(async () => {
// Create all tables
await db.execute(sql`CREATE TABLE teams (
await conn.execute(sql`CREATE TABLE teams (
id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL
)`);
await db.execute(sql`CREATE TABLE dogs (
await conn.execute(sql`CREATE TABLE dogs (
id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
breed text,
created_at timestamptz NOT NULL DEFAULT now(),
team_id int8 NOT NULL REFERENCES teams(id),
rival_id int8 REFERENCES dogs(id)
)`);
await db.execute(sql`CREATE TABLE collars (
await conn.execute(sql`CREATE TABLE collars (
id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
color text NOT NULL,
dog_id int8 UNIQUE NOT NULL REFERENCES dogs(id)
)`);
await db.execute(sql`CREATE TABLE toys (
await conn.execute(sql`CREATE TABLE toys (
id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name text NOT NULL,
dog_id int8 NOT NULL REFERENCES dogs(id)
)`);
await db.execute(sql`CREATE TABLE microchips (
await conn.execute(sql`CREATE TABLE microchips (
id int8 GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
serial text NOT NULL,
dog_id int8 UNIQUE REFERENCES dogs(id)
)`);

// Seed data
await db.execute(sql`INSERT INTO teams (name) VALUES ('Alpha'), ('Beta')`);
await db.execute(sql`INSERT INTO dogs (name, breed, team_id) VALUES ('Rex', 'Labrador', 1), ('Fido', NULL, 1), ('Buddy', 'Poodle', 2)`);
await db.execute(sql`UPDATE dogs SET rival_id = 2 WHERE name = 'Rex'`);
await db.execute(sql`INSERT INTO collars (color, dog_id) VALUES ('red', 1), ('blue', 2)`);
await db.execute(sql`INSERT INTO toys (name, dog_id) VALUES ('ball', 1), ('bone', 1), ('rope', 2)`);
await db.execute(sql`INSERT INTO microchips (serial, dog_id) VALUES ('MC-001', 1)`);
await db.execute(sql`INSERT INTO microchips (serial) VALUES ('MC-SPARE')`);
await conn.execute(sql`INSERT INTO teams (name) VALUES ('Alpha'), ('Beta')`);
await conn.execute(sql`INSERT INTO dogs (name, breed, team_id) VALUES ('Rex', 'Labrador', 1), ('Fido', NULL, 1), ('Buddy', 'Poodle', 2)`);
await conn.execute(sql`UPDATE dogs SET rival_id = 2 WHERE name = 'Rex'`);
await conn.execute(sql`INSERT INTO collars (color, dog_id) VALUES ('red', 1), ('blue', 2)`);
await conn.execute(sql`INSERT INTO toys (name, dog_id) VALUES ('ball', 1), ('bone', 1), ('rope', 2)`);
await conn.execute(sql`INSERT INTO microchips (serial, dog_id) VALUES ('MC-001', 1)`);
await conn.execute(sql`INSERT INTO microchips (serial) VALUES ('MC-SPARE')`);
});

test("select dogs", async () => {
const rows = await Dogs.from()
.select(({ dogs }) => ({ id: dogs.id, name: dogs.name, breed: dogs.breed }))
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ id: string; name: string; breed: string | null }[]>();
expect(rows).toHaveLength(3);
Expand All @@ -67,7 +67,7 @@ test("relation: outbound FK NOT NULL → cardinality 'one'", async () => {
team: dogs.team().select(({ teams }) => ({ name: teams.name })).scalar(),
}))
.where(({ dogs }) => dogs.name["="]("Rex"))
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ name: string; team: { name: string } }[]>();
expect(rows).toEqual([{ name: "Rex", team: { name: "Alpha" } }]);
Expand All @@ -81,7 +81,7 @@ test("relation: outbound FK nullable → cardinality 'maybe'", async () => {
rival: dogs.rival().select(({ dogs: d }) => ({ name: d.name })).scalar(),
}))
.orderBy(({ dogs }) => dogs.name)
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ name: string; rival: { name: string } | null }[]>();
expect(rows).toEqual([
Expand All @@ -99,7 +99,7 @@ test("relation: inbound FK non-unique → cardinality 'many'", async () => {
toys: dogs.toys().select(({ toys }) => ({ name: toys.name })).scalar(),
}))
.orderBy(({ dogs }) => dogs.name)
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ name: string; toys: { name: string }[] }[]>();
expect(rows).toEqual([
Expand All @@ -117,7 +117,7 @@ test("relation: inbound FK unique NOT NULL → cardinality 'one'", async () => {
collar: dogs.collars().select(({ collars }) => ({ color: collars.color })).scalar(),
}))
.where(({ dogs }) => dogs.name["="]("Rex"))
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ name: string; collar: { color: string } }[]>();
expect(rows).toEqual([{ name: "Rex", collar: { color: "red" } }]);
Expand All @@ -131,7 +131,7 @@ test("relation: inbound FK unique nullable → cardinality 'maybe'", async () =>
chip: dogs.microchips().select(({ microchips }) => ({ serial: microchips.serial })).scalar(),
}))
.orderBy(({ dogs }) => dogs.name)
.execute(db);
.execute(conn);

expectTypeOf(rows).toEqualTypeOf<{ name: string; chip: { serial: string } | null }[]>();
expect(rows).toEqual([
Expand Down
Loading