Skip to content

Commit 8230ddb

Browse files
committed
chore(pglite): productize adapter + pglite-test for publish
- Make @electric-sql/pglite a peerDependency of @pgpmjs/pglite-adapter and pglite-test (kept as a pinned devDependency for the monorepo build/tests), so consumers pin the PGlite version and avoid duplicate installs. - Harden pglite-test getConnections: restore the previous client factory on teardown (instead of resetting to default) and unwind seams if setup throws. - Rename workflow pglite-poc.yaml -> pglite.yaml (job pglite-socket-e2e); it is the standalone wire-protocol e2e demo. The in-process path already runs in the no-services unit-tests (pglite) job in run-tests.yaml. - Add CHANGELOG.md stubs for both new packages.
1 parent bb269ce commit 8230ddb

10 files changed

Lines changed: 98 additions & 66 deletions

File tree

.github/workflows/pglite-poc.yaml

Lines changed: 0 additions & 35 deletions
This file was deleted.

.github/workflows/pglite.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: PGlite (socket e2e)
2+
3+
# End-to-end demonstration of the pgpm migrate engine deploying real migrations
4+
# into PGlite (WASM Postgres) over the pg-gateway wire-protocol shim, using the
5+
# UNMODIFIED published @pgpmjs/core. Standalone (own lockfile, outside the pnpm
6+
# workspace). Deliberately has NO `services:` block — PGlite runs in-process, so
7+
# no Postgres service container is needed.
8+
#
9+
# The in-process driver path (@pgpmjs/pglite-adapter + pglite-test) is covered
10+
# separately by the no-services `unit-tests (pglite ...)` job in run-tests.yaml.
11+
12+
on:
13+
push:
14+
branches: [main, v1]
15+
paths:
16+
- 'poc/pglite/**'
17+
- '.github/workflows/pglite.yaml'
18+
pull_request:
19+
branches: [main, v1]
20+
paths:
21+
- 'poc/pglite/**'
22+
- '.github/workflows/pglite.yaml'
23+
workflow_dispatch:
24+
25+
jobs:
26+
pglite-socket-e2e:
27+
if: github.event.pull_request.draft != true
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 10
30+
steps:
31+
- uses: actions/checkout@v4
32+
- uses: actions/setup-node@v4
33+
with:
34+
node-version: '22'
35+
- name: Install (standalone, not the monorepo)
36+
working-directory: poc/pglite
37+
run: npm ci
38+
- name: Deploy pgpm migrations into PGlite (no Postgres service)
39+
working-directory: poc/pglite
40+
run: npm start

pnpm-lock.yaml

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

poc/pglite/DESIGN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ single-writer backend, but is not required for this rollout.
177177
2. **`@pgpmjs/pglite-adapter`** (in-process driver, all pglite deps here). ✅ **DONE**`registerPglite()` registers a PGlite-backed `QueryablePool`; 6 tests deploy→verify→revert an unmodified pgpm plan into in-process PGlite (no socket), with `useTransaction: true`.
178178
2b. **`pgsql-client` client-factory seam** (default = `new pg.Client`; zero behavior change). ✅ **DONE**`registerPgClientFactory` / `defaultPgClientFactory` / `getActivePgClientFactory`, mirroring the `pg-cache` pool seam; `PgClient` routes its underlying client through it; 4 unit tests. Needed because `pgsql-test`/`pgsql-client` build `pg.Client`s directly rather than via `getPgPool`.
179179
3. **`pglite-test`** drop-in `getConnections` (instance-per-suite). ✅ **DONE** — composes `registerPglite()` (pool seam) + `registerPgClientFactory` (client seam) so `pg`/`db` share one in-process PGlite session; a `SharedTxn` ref-counter keeps the standard two-client `beforeEach`/`afterEach` savepoint harness working over a single session; seeds via `seed.pgpm()`; no `createdb`/`psql`. 5 tests cover deploy, per-test isolation, and RLS role/JWT switching (incl. `WITH CHECK`). No separate PGlite-aware `DbAdmin` subclass was needed — the instance *is* the database and extension/role bootstrap runs via `pglite.extensionSql`.
180-
4. This PoC's socket-shim CI job (`pglite-poc.yaml`) is **kept as-is**it's a standalone, out-of-workspace demonstration of the `pg-gateway` wire-protocol path (own lockfile, published `@pgpmjs/core`). The canonical in-process path is now proven by the `@pgpmjs/pglite-adapter` and `pglite-test` jest suites in `run-tests.yaml` (no-services tier).
180+
4. The socket-shim CI job (`pglite.yaml`, job `pglite-socket-e2e`) is **kept** — a standalone, out-of-workspace demonstration of the `pg-gateway` wire-protocol path (own lockfile, published `@pgpmjs/core`). The canonical in-process path is proven by the `@pgpmjs/pglite-adapter` and `pglite-test` jest suites in the no-services `unit-tests (pglite ...)` job in `run-tests.yaml`.
181181
5. (Optional) Core transaction-client fix — only if a *multi-connection* single-writer backend is ever targeted; the in-process adapter does not need it.
182182
6. (Future) `pgpm init --pglite` scaffolder — CLI-layer convenience; no PGlite dep in core.
183183

poc/pglite/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ npm ci
1111
npm start # boots PGlite in-process, runs pgpm deploy -> verify -> revert, asserts
1212
```
1313

14-
This runs in CI via `.github/workflows/pglite-poc.yaml` with no `services:` block —
14+
This runs in CI via `.github/workflows/pglite.yaml` with no `services:` block —
1515
the whole point: PGlite needs no external database.
1616

1717
## What it does
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

postgres/pglite-adapter/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,15 @@
4040
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
4141
},
4242
"devDependencies": {
43+
"@electric-sql/pglite": "0.5.4",
4344
"@pgpmjs/core": "workspace:^",
4445
"@types/pg": "^8.20.0",
4546
"makage": "^0.3.0"
4647
},
4748
"dependencies": {
48-
"@electric-sql/pglite": "0.5.4",
4949
"pg-cache": "workspace:^"
50+
},
51+
"peerDependencies": {
52+
"@electric-sql/pglite": ">=0.5.0"
5053
}
5154
}

postgres/pglite-test/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

postgres/pglite-test/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,19 @@
4141
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
4242
},
4343
"devDependencies": {
44+
"@electric-sql/pglite": "0.5.4",
4445
"@types/pg": "^8.20.0",
4546
"makage": "^0.3.0"
4647
},
4748
"dependencies": {
48-
"@electric-sql/pglite": "0.5.4",
4949
"@pgpmjs/env": "workspace:^",
5050
"@pgpmjs/pglite-adapter": "workspace:^",
5151
"pg-cache": "workspace:^",
5252
"pg-env": "workspace:^",
5353
"pgsql-client": "workspace:^",
5454
"pgsql-test": "workspace:^"
55+
},
56+
"peerDependencies": {
57+
"@electric-sql/pglite": ">=0.5.0"
5558
}
5659
}

postgres/pglite-test/src/index.ts

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import type { PGlite } from '@electric-sql/pglite';
3333
import { createPgliteClient,registerPglite } from '@pgpmjs/pglite-adapter';
3434
import { getPgEnvOptions, PgConfig } from 'pg-env';
35-
import { registerPgClientFactory } from 'pgsql-client';
35+
import { getActivePgClientFactory, registerPgClientFactory } from 'pgsql-client';
3636
import { DbAdmin, GetConnectionOpts, PgTestClient, seed, SeedAdapter } from 'pgsql-test';
3737

3838
import { PgliteTestClient, SharedTxn } from './txn';
@@ -75,6 +75,11 @@ export const getConnections = async (
7575
cn: PgliteConnectionOpts = {},
7676
seedAdapters: SeedAdapter[] = [seed.pgpm()]
7777
): Promise<PgliteConnectionResult> => {
78+
// Capture the previously-active client factory so teardown restores it
79+
// (rather than clobbering to the default), mirroring how registerPglite
80+
// restores the previous pool factory. Keeps nested/sequential suites clean.
81+
const previousClientFactory = getActivePgClientFactory();
82+
7883
const handle = await registerPglite({
7984
dataDir: cn.pglite?.dataDir,
8085
extensions: cn.pglite?.extensions,
@@ -85,30 +90,38 @@ export const getConnections = async (
8590
// Route pgsql-client's PgClient at the same in-process PGlite session.
8691
registerPgClientFactory(() => createPgliteClient(handle.db));
8792

88-
const config: PgConfig = getPgEnvOptions({ database: 'postgres', ...cn.pg });
93+
try {
94+
const config: PgConfig = getPgEnvOptions({ database: 'postgres', ...cn.pg });
8995

90-
const txn = new SharedTxn();
91-
const pg = new PgliteTestClient(config, {}, txn);
92-
const db = new PgliteTestClient(config, { auth: cn.db?.auth, roles: cn.db?.roles }, txn);
96+
const txn = new SharedTxn();
97+
const pg = new PgliteTestClient(config, {}, txn);
98+
const db = new PgliteTestClient(config, { auth: cn.db?.auth, roles: cn.db?.roles }, txn);
9399

94-
// seed.pgpm deploys via getPgPool -> PGlite; sqlfile/json/csv/fn use ctx.pg.
95-
if (seedAdapters.length) {
96-
await seed.compose(seedAdapters).seed({
97-
connect: cn.db ?? {},
98-
admin: undefined as unknown as DbAdmin, // unused by the PGlite-safe adapters
99-
config,
100-
pg
101-
});
102-
}
100+
// seed.pgpm deploys via getPgPool -> PGlite; sqlfile/json/csv/fn use ctx.pg.
101+
if (seedAdapters.length) {
102+
await seed.compose(seedAdapters).seed({
103+
connect: cn.db ?? {},
104+
admin: undefined as unknown as DbAdmin, // unused by the PGlite-safe adapters
105+
config,
106+
pg
107+
});
108+
}
103109

104-
const teardown = async (): Promise<void> => {
105-
registerPgClientFactory(undefined);
106-
await pg.close();
107-
await db.close();
108-
await handle.close();
109-
};
110+
const teardown = async (): Promise<void> => {
111+
registerPgClientFactory(previousClientFactory);
112+
await pg.close();
113+
await db.close();
114+
await handle.close();
115+
};
110116

111-
return { pg, db, instance: handle.db, teardown };
117+
return { pg, db, instance: handle.db, teardown };
118+
} catch (err) {
119+
// Setup failed after registering the seams — unwind so a broken suite
120+
// doesn't leak factories/instance into the next one.
121+
registerPgClientFactory(previousClientFactory);
122+
await handle.close();
123+
throw err;
124+
}
112125
};
113126

114127
export { PgliteTestClient, SharedTxn } from './txn';

0 commit comments

Comments
 (0)