@crvouga/mockingbird-service-postgres 0.3.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -10,10 +10,15 @@ Guidance for humans and coding agents editing this repository. For install and c
10
10
 
11
11
  1. Custom snapshot codec (`PGMM`), not `pg_dump` / on-disk clusters
12
12
  2. Deterministic `random()` / `gen_random_uuid()` and fixed `now()` by default (injectable)
13
- 3. Single-session engine: no MVCC across connections, no wire protocol, no aborted-transaction (`25P02`) state
13
+ 3. Single-session **engine**: no MVCC across connections, no aborted-transaction (`25P02`) state in the sync API
14
14
  4. `NOT APPLICABLE` items: roles/auth enforcement, replication, VACUUM internals, storage params, full PL/pgSQL
15
15
 
16
- **Non-goals:** speaking the wire protocol, matching `pg`/`postgres.js` client APIs, full PL/pgSQL, or multi-session concurrency.
16
+ **Non-goals:** matching `pg`/`postgres.js` **client APIs**, full PL/pgSQL, or MVCC. The optional
17
+ wire-protocol server (`src/wire/`, a Node/Bun-only entry over the one engine) is in scope: it
18
+ speaks frontend/backend v3 and coordinates connections by serializing transaction blocks, adding
19
+ advisory locks, `LISTEN`/`NOTIFY`, `CancelRequest` and per-connection `25P02`. It deliberately does
20
+ **not** add row-level lock contention (`SELECT … FOR UPDATE SKIP LOCKED` distribution) or `COPY`
21
+ streaming.
17
22
 
18
23
  ## SQL pipeline
19
24
 
@@ -49,6 +54,7 @@ Everything is **typed values** (`TypedValue = { t: TypeId, v: Datum }` in [`src/
49
54
  | `transactions/` | BEGIN / COMMIT / SAVEPOINT (clones state + PRNG) |
50
55
  | `runtime/` | Clock, PRNG, `DatabaseOptions` |
51
56
  | `serialization/` | `PGMM` snapshot codec |
57
+ | `wire/` | Wire-protocol (frontend/backend v3) TCP server: `serve()`, CLI, per-connection state machine, cross-connection `Cluster` (Node/Bun only) |
52
58
  | `tsearch/` | `tsvector` / `tsquery` text search |
53
59
  | `errors/` | `PostgresError` with SQLSTATE, `unsupported()` |
54
60
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog — @crvouga/mockingbird-service-postgres
2
2
 
3
+ ## 1.1.0 (2026-09-26)
4
+
5
+ ### Features
6
+
7
+ - wire-protocol server so separate processes can connect over TCP ([85000c8](https://github.com/crvouga/mockingbird/commit/85000c88fe57dc28c42caa19dcf3b825d94a361b))
8
+
9
+ ### Fixes and improvements
10
+
11
+ - expose the wire server under ./wire, not the reserved ./server export ([12734c7](https://github.com/crvouga/mockingbird/commit/12734c7ecb39585ac3c7565d6367b3611d8c38a6))
12
+
13
+ ## 1.0.0 (2026-09-24)
14
+
15
+ ### ⚠️ Breaking changes
16
+
17
+ - vendor branding for every service; purge internal mocks and consumer names ([f689534](https://github.com/crvouga/mockingbird/commit/f6895343543550dafcb7d76b426cae7635e03226))
18
+
3
19
  ## 0.3.0 (2026-09-22)
4
20
 
5
21
  ### Features
package/COMPATIBILITY.md CHANGED
@@ -25,7 +25,7 @@ Intentional differences are finite and machine-readable in `compat/divergences.j
25
25
  | **VERIFIED** | Differential contracts (+ fuzz where applicable) cover happy path **and** meaningful edges vs oracle |
26
26
  | **PARTIALLY VERIFIED** | Implemented; coverage thin or known edges remain |
27
27
  | **UNSUPPORTED** | Missing from SQL surface (must fail loud; gate fails if oracle-exposed and unregistered) |
28
- | **NOT APPLICABLE** | Outside the in-memory single-session dialect surface (roles/auth, replication, storage, wire protocol) |
28
+ | **NOT APPLICABLE** | Outside the SQL-dialect surface (roles/auth, replication, storage). The wire protocol is provided by the optional `/server` entry, documented in the README. |
29
29
 
30
30
  ## Scope bound
31
31
 
package/README.md CHANGED
@@ -15,7 +15,7 @@ isolation.
15
15
  - **Synchronous**, ESM-only API (no Promises, no `require`)
16
16
  - **SQL dialect verified** against real PostgreSQL 18.3 (PGlite by default; optional native server)
17
17
  via differential contracts and a fail-closed gate
18
- - **Not** a drop-in for the `pg` / `postgres.js` client APIs, the wire protocol, or on-disk clusters
18
+ - In-process it is **not** a drop-in for the `pg` / `postgres.js` client APIs or on-disk clusters, but it also ships an **optional wire-protocol server** (`@crvouga/mockingbird-service-postgres/wire`, Node/Bun) that unmodified clients connect to over TCP
19
19
  - Intentional differences: deterministic `random()` / `now()` by default, and a custom snapshot
20
20
  format (not `pg_dump`)
21
21
 
@@ -79,7 +79,8 @@ console.log(db2.query(`SELECT count(*) AS n FROM users`), db3.changes) // [{ n:
79
79
  ```
80
80
 
81
81
  All methods are **synchronous**; do not `await` them. Browser and Node/Bun share the same
82
- in-memory surface (no filesystem, no server, no wire protocol).
82
+ in-memory surface (no filesystem). For a real TCP listener separate processes can connect to, see
83
+ the [wire-protocol server](#wire-protocol-server-separate-processes) (Node/Bun only).
83
84
 
84
85
  ### Per-test isolation with snapshots
85
86
 
@@ -151,6 +152,53 @@ back as PostgreSQL text (node-postgres parses timestamps to `Date` and JSON to o
151
152
  `prepare`/`query` accept one statement at a time (use `exec` for scripts); errors are
152
153
  `PostgresError` with `code` set to the SQLSTATE, like `pg`'s `DatabaseError.code`.
153
154
 
155
+ ### Wire-protocol server (separate processes)
156
+
157
+ When another process must connect over TCP — `pg`, `postgres.js`, a JDBC client, `psql`, a service
158
+ in a local multi-service stack — start the frontend/backend v3 server instead of a shim. It needs
159
+ `node:net`, so it is a separate Node/Bun entry (`/wire`) and the main package stays browser-safe.
160
+
161
+ ```ts
162
+ import { serve } from "@crvouga/mockingbird-service-postgres/wire"
163
+
164
+ const server = await serve({ port: 0 }) // 0 → a free port, reported as server.port
165
+ // postgres://postgres@127.0.0.1:${server.port}/db — no initdb, no OS user, pure TypeScript
166
+ await server.close()
167
+ ```
168
+
169
+ Or from the command line (installs a `mockingbird-postgres` bin):
170
+
171
+ ```bash
172
+ mockingbird-postgres serve --port 55432 # trust auth
173
+ mockingbird-postgres serve --password secret --log # SCRAM-SHA-256, log each statement
174
+ ```
175
+
176
+ `serve({ database })` shares an existing `Database`, and `serve({ database: snapshot })` boots every
177
+ server from one frozen template, so a seeded stack starts from the same bytes each time.
178
+ `server.snapshot()` freezes the live state; `server.fault({ dropConnection | delayStatementMs |
179
+ failCommit })` arms the next statement or connection for a drop, a delay, or a `40001` commit
180
+ failure.
181
+
182
+ One engine is shared by every connection. The engine runs one statement at a time, so a connection
183
+ inside an explicit `BEGIN` block holds it until `COMMIT`/`ROLLBACK` and other connections queue
184
+ behind it — which keeps read-committed visibility (an uncommitted row is never seen by another
185
+ connection) by serializing transaction blocks rather than by MVCC. Across connections the server
186
+ adds what a single engine does not: per-session advisory locks (`pg_advisory_lock` /
187
+ `pg_try_advisory_lock` / `_xact_` / `_unlock`) with in-order waiters and deadlock detection
188
+ (`40P01`), `LISTEN`/`NOTIFY` delivered to idle listeners, `CancelRequest` (a blocked statement ends
189
+ `57014` and the session stays usable), and per-connection aborted-transaction state (a failed
190
+ statement in a block is `25P02` until `ROLLBACK`, with `ReadyForQuery` reporting `I`/`T`/`E`).
191
+ SCRAM-SHA-256 and trust auth, the extended query protocol (`Parse`/`Bind`/`Describe`/`Execute`/
192
+ `Sync`) with text and binary parameters and results, real type OIDs in `RowDescription`, and
193
+ `23505`/`23502` errors carrying the constraint, table and column the engine names, all work over
194
+ the wire.
195
+
196
+ **Not modelled by the server:** row-level lock contention, so `SELECT … FOR UPDATE SKIP LOCKED`
197
+ parses and returns rows but does not distribute disjoint rows across concurrent workers (there are
198
+ no row locks); `COPY` streaming (`CopyInResponse`/`CopyData`) — use `copyFrom` on a shared
199
+ `Database`; and the binary parameter formats beyond the common scalar types (a client that sends
200
+ another binary type gets `0A000`, and can switch that parameter to text).
201
+
154
202
  ### Method semantics
155
203
 
156
204
  | Method | Behaviour |
@@ -247,20 +295,21 @@ Goal: **SQL dialect** behavioural parity vs PostgreSQL **18.3** for the sync API
247
295
  [COMPATIBILITY.md](./COMPATIBILITY.md). Contract:
248
296
  [DROP-IN-CONTRACT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/postgres/docs/DROP-IN-CONTRACT.md).
249
297
 
250
- There is no wire protocol, no async client, no connection pooling, no `pg_dump` codec, and no
251
- multi-session concurrency. Intentional differences: custom `PGMM` snapshots; seeded `random()` /
252
- fixed `now()` by default (`{ random: "os" }` / `{ now: "system" }` match PostgreSQL entropy and
253
- wall clock); single session, no MVCC across connections.
298
+ The in-process API has no async client, no connection pooling and no `pg_dump` codec; the optional
299
+ [wire-protocol server](#wire-protocol-server-separate-processes) adds a TCP listener and coordinates
300
+ several connections over the one engine, but by serializing transaction blocks, not by MVCC.
301
+ Intentional differences: custom `PGMM` snapshots; seeded `random()` / fixed `now()` by default
302
+ (`{ random: "os" }` / `{ now: "system" }` match PostgreSQL entropy and wall clock).
254
303
 
255
304
  **Thin or partial areas** (do not assume full oracle fidelity):
256
305
 
257
306
  - `EXPLAIN`: stub plan shapes, not real planner output
258
- - Failed statements inside `BEGIN` do **not** poison the transaction (`25P02` aborted-state is not implemented)
307
+ - The in-process API does **not** poison a transaction after a failed statement (no `25P02`); the wire-protocol server does, per connection
259
308
  - Triggers fire in **creation order** (PostgreSQL: name order); `UPDATE OF` column lists are ignored; `INSTEAD OF` is unsupported
260
309
  - `COMMENT ON` parses but comments are not stored
261
310
  - `round(float8)` rounds ties away from zero (PostgreSQL: half-to-even); numeric `round()` has full parity
262
311
  - `'1e400'::float8` saturates to `Infinity` instead of raising `22003`
263
- - `MERGE`, `CALL`/procedures, cursors (`DECLARE`/`FETCH`), `LISTEN`/`NOTIFY`, and full PL/pgSQL (packages, NOTICE, cursors) fail loud (`0A000`)
312
+ - In the engine `MERGE`, `CALL`/procedures, cursors (`DECLARE`/`FETCH`), `LISTEN`/`NOTIFY`, and full PL/pgSQL (packages, NOTICE, cursors) fail loud (`0A000`); the wire-protocol server implements `LISTEN`/`NOTIFY` itself
264
313
  - `VACUUM` / `ANALYZE` / `CLUSTER` / `REINDEX` / `CHECKPOINT` / `GRANT` / `REVOKE` / `LOCK` are parsed no-ops
265
314
  - Collation is `C` semantics (byte order); locale/ICU-dependent ordering is out of scope
266
315
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};