Skip to content

Commit bd2ec16

Browse files
committed
Live queries for SQLite / Durable Objects
- Bus is dialect agnostic, event source differs - PG: a shadow table that is polled (unchanged) - Sqlite: assumes single thread access (the DO model), so updates are immediately pushed to the bus with special handling to only flush events at commit in case of a transaction - Opt-in to live is now through a { live: true } flag on the db.Table meta-class
1 parent 8d8998b commit bd2ec16

43 files changed

Lines changed: 2284 additions & 375 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

package-lock.json

Lines changed: 733 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
],
5959
"peerDependencies": {
6060
"@electric-sql/pglite": "^0.4.4",
61-
"pg": "^8.20.0",
62-
"better-sqlite3": "^12.11.1"
61+
"better-sqlite3": "^12.11.1",
62+
"pg": "^8.20.0"
6363
},
6464
"peerDependenciesMeta": {
6565
"@electric-sql/pglite": {
@@ -78,14 +78,16 @@
7878
"scripts": {
7979
"build": "tsdown",
8080
"codegen": "node --experimental-strip-types src/types/postgres/emit.ts && node --experimental-strip-types src/types/sqlite/emit.ts",
81-
"codegen:check": "tmp=$(mktemp -d) && node --experimental-strip-types src/types/postgres/emit.ts --out-dir \"$tmp/pg\" && { diff -r \"$tmp/pg\" src/types/postgres/generated || { echo 'src/types/postgres/generated is stale run `npm run codegen` and commit.' >&2; rm -rf \"$tmp\"; exit 1; }; } && node --experimental-strip-types src/types/sqlite/emit.ts --out-dir \"$tmp/sqlite\" && { diff -r \"$tmp/sqlite/generated\" src/types/sqlite/generated || { echo 'src/types/sqlite/generated is stale run `npm run codegen` and commit.' >&2; rm -rf \"$tmp\"; exit 1; }; } && rm -rf \"$tmp\"",
81+
"codegen:check": "tmp=$(mktemp -d) && node --experimental-strip-types src/types/postgres/emit.ts --out-dir \"$tmp/pg\" && { diff -r \"$tmp/pg\" src/types/postgres/generated || { echo 'src/types/postgres/generated is stale - run `npm run codegen` and commit.' >&2; rm -rf \"$tmp\"; exit 1; }; } && node --experimental-strip-types src/types/sqlite/emit.ts --out-dir \"$tmp/sqlite\" && { diff -r \"$tmp/sqlite/generated\" src/types/sqlite/generated || { echo 'src/types/sqlite/generated is stale - run `npm run codegen` and commit.' >&2; rm -rf \"$tmp\"; exit 1; }; } && rm -rf \"$tmp\"",
8282
"lint": "eslint src",
8383
"typecheck": "tsgo --noEmit",
8484
"format": "prettier --write src",
8585
"test": "vitest run",
86-
"check": "npm run lint && npm run typecheck && npm run test && npm --prefix examples/basic run check"
86+
"check": "npm run lint && npm run typecheck && npm run test && npm --prefix examples/basic run check",
87+
"test:do": "vitest run --project workerd"
8788
},
8889
"devDependencies": {
90+
"@cloudflare/vitest-pool-workers": "^0.18.6",
8991
"@electric-sql/pglite": "^0.5.4",
9092
"@eslint/js": "^10.0.1",
9193
"@standard-schema/spec": "^1.1.0",

site/src/demo/runtime.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Uses top-level await so schema files can import a ready `db` and
33
// define tables at module-eval time.
44

5-
import { typegres } from "typegres";
5+
import { ensurePgLiveEventsTable, typegres } from "typegres";
66
import { runMigrations, runSeed } from "./seed";
77
import type { UserRoot } from "./server/api";
88

@@ -11,9 +11,8 @@ export const { db, conn } = await typegres<UserRoot>({ type: "pglite" });
1111
await runMigrations(conn);
1212
await runSeed(conn);
1313

14-
// `installLiveEvents` is a one-time DDL — production callers run it as
14+
// `ensurePgLiveEventsTable` is a one-time DDL — production callers run it as
1515
// part of their migrations. The demo's storage doesn't survive page
16-
// reloads, so we run it on every boot. `startLive` then assumes the
17-
// events table exists and only spins up the polling bus.
18-
await conn.installLiveEvents();
19-
await conn.startLive();
16+
// reloads, so we run it on every boot. The live engine itself is wired
17+
// at attach and its poller starts lazily on first .live() use.
18+
await ensurePgLiveEventsTable(conn);

site/src/demo/schema/customers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { TypegresLiveEvents, expose } from "typegres";
1+
import { expose } from "typegres";
22
import { Int8, Text } from "typegres/postgres";
33
import { db } from "../runtime";
44
import { Orders } from "./orders";
55
import { Organizations } from "./organizations";
6-
export class Customers extends db.Table("customers", { transformer: TypegresLiveEvents.makeTransformer() }) {
6+
export class Customers extends db.Table("customers", { live: true }) {
77
// @generated-start
88
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
99
@expose() organization_id = (Int8<1>).column({ nonNull: true });

site/src/demo/schema/inventory_positions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { Connection, TypegresLiveEvents, sql, expose } from "typegres";
1+
import { Connection, sql, expose } from "typegres";
22
import { Int8, Text } from "typegres/postgres";
33
import { z } from "zod";
44
import { db } from "../runtime";
55
import { Locations } from "./locations";
66
import { Organizations } from "./organizations";
77
import { OrderLines } from "./order_lines";
8-
export class InventoryPositions extends db.Table("inventory_positions", { transformer: TypegresLiveEvents.makeTransformer() }) {
8+
export class InventoryPositions extends db.Table("inventory_positions", { live: true }) {
99
// @generated-start
1010
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
1111
@expose() organization_id = (Int8<1>).column({ nonNull: true });

site/src/demo/schema/locations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { TypegresLiveEvents, expose } from "typegres";
1+
import { expose } from "typegres";
22
import { Int8, Text } from "typegres/postgres";
33
import { db } from "../runtime";
44
import { InventoryPositions } from "./inventory_positions";
55
import { Organizations } from "./organizations";
66

7-
export class Locations extends db.Table("locations", { transformer: TypegresLiveEvents.makeTransformer() }) {
7+
export class Locations extends db.Table("locations", { live: true }) {
88
// @generated-start
99
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
1010
@expose() organization_id = (Int8<1>).column({ nonNull: true });

site/src/demo/schema/order_lines.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { TypegresLiveEvents, expose } from "typegres";
1+
import { expose } from "typegres";
22
import { Int8, Text } from "typegres/postgres";
33
import { db } from "../runtime";
44
import { InventoryPositions } from "./inventory_positions";
55
import { Orders } from "./orders";
66

7-
export class OrderLines extends db.Table("order_lines", { transformer: TypegresLiveEvents.makeTransformer() }) {
7+
export class OrderLines extends db.Table("order_lines", { live: true }) {
88
// @generated-start
99
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
1010
@expose() order_id = (Int8<1>).column({ nonNull: true });

site/src/demo/schema/orders.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Connection, TypegresLiveEvents, sql, expose } from "typegres";
1+
import { Connection, sql, expose } from "typegres";
22
import { Int8, Text, Timestamptz } from "typegres/postgres";
33
import { z } from "zod";
44
import { db } from "../runtime";
@@ -7,7 +7,7 @@ import { OrderLines } from "./order_lines";
77
import { Organizations } from "./organizations";
88
import { Shipments } from "./shipments";
99

10-
export class Orders extends db.Table("orders", { transformer: TypegresLiveEvents.makeTransformer() }) {
10+
export class Orders extends db.Table("orders", { live: true }) {
1111
// @generated-start
1212
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
1313
@expose() organization_id = (Int8<1>).column({ nonNull: true });

site/src/demo/schema/organizations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TypegresLiveEvents, expose } from "typegres";
1+
import { expose } from "typegres";
22
import { Int8, Text } from "typegres/postgres";
33
import { db } from "../runtime";
44
import { Customers } from "./customers";
@@ -7,7 +7,7 @@ import { Locations } from "./locations";
77
import { Users } from "./users";
88
import { Orders } from "./orders";
99
import { Shipments } from "./shipments";
10-
export class Organizations extends db.Table("organizations", { transformer: TypegresLiveEvents.makeTransformer() }) {
10+
export class Organizations extends db.Table("organizations", { live: true }) {
1111
// @generated-start
1212
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
1313
@expose() name = (Text<1>).column({ nonNull: true });

site/src/demo/schema/shipments.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { TypegresLiveEvents, sql, expose } from "typegres";
1+
import { sql, expose } from "typegres";
22
import { Int8, Text, Timestamptz } from "typegres/postgres";
33
import { db } from "../runtime";
44
import { Orders } from "./orders";
55
import { Organizations } from "./organizations";
6-
export class Shipments extends db.Table("shipments", { transformer: TypegresLiveEvents.makeTransformer() }) {
6+
export class Shipments extends db.Table("shipments", { live: true }) {
77
// @generated-start
88
@expose() id = (Int8<1>).column({ nonNull: true, generated: true });
99
@expose() organization_id = (Int8<1>).column({ nonNull: true });

0 commit comments

Comments
 (0)