@crvouga/mockingbird-service-postgres 3.3.0 → 3.4.1
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/CHANGELOG.md +13 -1
- package/COMPATIBILITY.md +1 -1
- package/README.md +72 -53
- package/dist/admin.js +1535 -365
- package/dist/admin.js.map +4 -4
- package/dist/index.js +7 -5
- package/dist/index.js.map +2 -2
- package/dist/server.js +13 -6
- package/dist/server.js.map +2 -2
- package/dist/storage/columnar-slab.d.ts +2 -0
- package/dist/wire/cli.js +1323 -780
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/fleet.d.ts +210 -1
- package/dist/wire/index.js +7 -5
- package/dist/wire/index.js.map +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-postgres
|
|
2
2
|
|
|
3
|
-
## 3.
|
|
3
|
+
## 3.4.1 (2026-10-15)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- resolve snapshot, promotion and webhook issues ([75fb79f](https://github.com/crvouga/mockingbird/commit/75fb79f9a999bf0af29c4ec07fde4f1d7a1f0c22))
|
|
8
|
+
|
|
9
|
+
## 3.4.0 (2026-10-12)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
- unify the emulator control plane ([2068547](https://github.com/crvouga/mockingbird/commit/2068547bedde4e2e2331c1657c1a78120e84428b))
|
|
14
|
+
|
|
15
|
+
## 3.3.0 (2026-10-10)
|
|
4
16
|
|
|
5
17
|
### Features
|
|
6
18
|
|
package/COMPATIBILITY.md
CHANGED
|
@@ -58,7 +58,7 @@ for the guarantees and their limits (`tests/wire/durable*.test.ts`, `tests/wire/
|
|
|
58
58
|
|
|
59
59
|
Anything a PostgreSQL application can invoke through SQL against the PGlite **18.3** oracle must match observable behavior, except:
|
|
60
60
|
|
|
61
|
-
1. **Snapshot format** — custom binary codec (`PGMM`), not `pg_dump` / on-disk clusters (logical state still round-trips).
|
|
61
|
+
1. **Snapshot format** — custom binary codec (`PGMM`), not `pg_dump` / on-disk clusters (logical state still round-trips). Decoding PGMM v3 preserves bigint-backed SQL datums (including small `bigint`, `money`, `time`, `timestamp`, and `timestamptz` values) using their catalog types, so arithmetic remains valid after repeated restores, including materialized views (`tests/contract/snapshots/basic.test.ts`).
|
|
62
62
|
2. **Deterministic `random()` / `now()`** — seeded PRNG and fixed clock by default (injectable).
|
|
63
63
|
3. **Single-session sync API** — no concurrent sessions or `25P02` aborted-transaction state. The optional wire server separately provides per-session `READ COMMITTED` workspaces and row locks.
|
|
64
64
|
4. **NOT APPLICABLE** rows in `compat/coverage.json` (roles, replication, VACUUM internals, LISTEN/NOTIFY, cursors, full PL/pgSQL, extensions other than `pgcrypto`'s `digest()` and `pg_trgm`'s `similarity` / `<%` / `gin_trgm_ops`).
|
package/README.md
CHANGED
|
@@ -55,44 +55,59 @@ dependency instead of `-D` if you ship it to the browser.
|
|
|
55
55
|
## Usage
|
|
56
56
|
|
|
57
57
|
```ts
|
|
58
|
-
import
|
|
58
|
+
import pg from "pg"
|
|
59
|
+
import { Snapshot } from "@crvouga/mockingbird-service-postgres"
|
|
60
|
+
import { serve } from "@crvouga/mockingbird-service-postgres/wire"
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
// Test setup: start the emulator on a free port.
|
|
63
|
+
const server = await serve({ port: 0 })
|
|
64
|
+
|
|
65
|
+
// Application code: the `pg` client it already uses, given the emulator's connection string.
|
|
66
|
+
const client = new pg.Client({ connectionString: server.connectionString })
|
|
67
|
+
await client.connect()
|
|
61
68
|
|
|
62
|
-
|
|
69
|
+
await client.query(`
|
|
63
70
|
CREATE TABLE users (
|
|
64
71
|
id serial PRIMARY KEY,
|
|
65
72
|
email text UNIQUE NOT NULL,
|
|
66
73
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
67
74
|
)
|
|
68
75
|
`)
|
|
76
|
+
await client.query(`INSERT INTO users (email) VALUES ($1)`, ["ada@example.com"])
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
const users = await client.query<{ id: number; email: string; created_at: Date }>(
|
|
79
|
+
`SELECT * FROM users`,
|
|
80
|
+
)
|
|
81
|
+
console.log(users.rows) // [{ id: 1, email: "ada@example.com", created_at: 2000-01-01T00:00:00.000Z }]
|
|
71
82
|
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
// Test control, snapshots: freeze a template, boot a fork from it, or persist it as bytes.
|
|
84
|
+
const seed = server.snapshot()
|
|
85
|
+
const fork = await serve({ database: seed })
|
|
86
|
+
const restored = await serve({ database: Snapshot.decode(seed.encode()) })
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const db2 = seed.open()
|
|
78
|
-
const bytes = seed.encode()
|
|
79
|
-
const db3 = Snapshot.decode(bytes).open()
|
|
80
|
-
console.log(db2.query(`SELECT count(*) AS n FROM users`), db3.changes) // [{ n: 1n }] 1
|
|
88
|
+
await client.end()
|
|
89
|
+
await Promise.all([server.close(), fork.close(), restored.close()])
|
|
81
90
|
```
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
the
|
|
92
|
+
`postgres.js`, a JDBC client and `psql` connect with the same connection string; see the
|
|
93
|
+
[wire-protocol server](#wire-protocol-server-separate-processes) (Node/Bun only). In the browser,
|
|
94
|
+
where no listener can run, use the embedded `Database` from the package root instead. It shares
|
|
95
|
+
the same in-memory engine (no filesystem) and all its methods are **synchronous**; do not `await`
|
|
96
|
+
them. See [Method semantics](#method-semantics).
|
|
86
97
|
|
|
87
98
|
### Per-test isolation with snapshots
|
|
88
99
|
|
|
89
|
-
Run migrations and fixtures once, `snapshot()` the result, and
|
|
90
|
-
test (microseconds, tables are shared until either side
|
|
100
|
+
Run migrations and fixtures once, `snapshot()` the result, and boot a server from that snapshot
|
|
101
|
+
per test. Each one is a copy-on-write fork (microseconds, tables are shared until either side
|
|
102
|
+
writes); with the embedded `Database`, `seed.open()` is the same fork without a listener:
|
|
91
103
|
|
|
92
104
|
```ts
|
|
93
|
-
import { beforeEach, expect, test } from "bun:test"
|
|
94
|
-
import
|
|
105
|
+
import { afterEach, beforeEach, expect, test } from "bun:test"
|
|
106
|
+
import pg from "pg"
|
|
107
|
+
import { Database } from "@crvouga/mockingbird-service-postgres"
|
|
108
|
+
import { type PostgresServer, serve } from "@crvouga/mockingbird-service-postgres/wire"
|
|
95
109
|
|
|
110
|
+
// Test setup: build the template once, then boot a server from it for every test.
|
|
96
111
|
const template = new Database()
|
|
97
112
|
template.exec(`
|
|
98
113
|
CREATE TABLE accounts (id serial PRIMARY KEY, email text UNIQUE NOT NULL);
|
|
@@ -100,64 +115,68 @@ template.exec(`
|
|
|
100
115
|
`)
|
|
101
116
|
const seed = template.snapshot()
|
|
102
117
|
|
|
103
|
-
let
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
let server: PostgresServer
|
|
119
|
+
let db: pg.Client
|
|
120
|
+
beforeEach(async () => {
|
|
121
|
+
server = await serve({ database: seed })
|
|
122
|
+
db = new pg.Client({ connectionString: server.connectionString })
|
|
123
|
+
await db.connect()
|
|
124
|
+
})
|
|
125
|
+
afterEach(async () => {
|
|
126
|
+
await db.end()
|
|
127
|
+
await server.close()
|
|
106
128
|
})
|
|
107
129
|
|
|
108
|
-
test
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
} catch (caught) {
|
|
113
|
-
error = caught
|
|
114
|
-
}
|
|
115
|
-
expect(error).toBeInstanceOf(PostgresError)
|
|
116
|
-
expect((error as PostgresError).code).toBe("23505")
|
|
117
|
-
expect((error as PostgresError).category).toBe("constraint_unique")
|
|
130
|
+
// Application code under test uses `db`, an ordinary `pg` client.
|
|
131
|
+
test("unique violation surfaces SQLSTATE 23505", async () => {
|
|
132
|
+
const insert = db.query(`INSERT INTO accounts (email) VALUES ($1)`, ["seed@example.com"])
|
|
133
|
+
await expect(insert).rejects.toMatchObject({ code: "23505", constraint: "accounts_email_key" })
|
|
118
134
|
})
|
|
119
135
|
|
|
120
|
-
test("each test starts from the seed", () => {
|
|
121
|
-
|
|
136
|
+
test("each test starts from the seed", async () => {
|
|
137
|
+
const { rows } = await db.query(`SELECT email FROM accounts`)
|
|
138
|
+
expect(rows).toEqual([{ email: "seed@example.com" }])
|
|
122
139
|
})
|
|
123
140
|
```
|
|
124
141
|
|
|
125
|
-
###
|
|
142
|
+
### Code written against `pg`
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
`
|
|
129
|
-
|
|
144
|
+
Code written against `pg` needs no adapter: give `pg.Pool` (or `pg.Client`) the server's connection
|
|
145
|
+
string. `pg` applies its own type parsers, so `int8` / `bigserial` / `count(*)` come back as
|
|
146
|
+
strings, as they do against a real server:
|
|
130
147
|
|
|
131
148
|
```ts
|
|
132
|
-
import
|
|
133
|
-
|
|
134
|
-
const db = new Database({ int8: "string", now: "system" })
|
|
149
|
+
import pg from "pg"
|
|
150
|
+
import { serve } from "@crvouga/mockingbird-service-postgres/wire"
|
|
135
151
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const result = db.prepare(text).result(...values)
|
|
140
|
-
return { rows: result.rows as T[], rowCount: result.rowCount, command: result.command }
|
|
141
|
-
},
|
|
142
|
-
}
|
|
152
|
+
// Test setup: start the emulator and give the pool its connection string.
|
|
153
|
+
const server = await serve({ port: 0 })
|
|
154
|
+
export const pool = new pg.Pool({ connectionString: server.connectionString })
|
|
143
155
|
|
|
156
|
+
// Application code: unchanged `pg` queries.
|
|
144
157
|
await pool.query(`CREATE TABLE notes (id bigserial PRIMARY KEY, body text)`)
|
|
145
158
|
const inserted = await pool.query<{ id: string }>(
|
|
146
159
|
`INSERT INTO notes (body) VALUES ($1) RETURNING id`,
|
|
147
160
|
["hello"],
|
|
148
161
|
)
|
|
149
162
|
console.log(inserted.rows[0]?.id, inserted.rowCount) // "1" 1
|
|
163
|
+
|
|
164
|
+
await pool.end()
|
|
165
|
+
await server.close()
|
|
150
166
|
```
|
|
151
167
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
`
|
|
168
|
+
Errors are `pg`'s own `DatabaseError` with `code` set to the SQLSTATE. Where no listener can run
|
|
169
|
+
(the browser), put the embedded `Database` behind whatever query interface your code uses. The
|
|
170
|
+
differences from node-postgres to account for there: `int8` is a `bigint` unless you pass
|
|
171
|
+
`{ int8: "string" }`; timestamps, dates, `numeric` and `json`/`jsonb` come back as PostgreSQL text
|
|
172
|
+
(node-postgres parses timestamps to `Date` and JSON to objects); `prepare`/`query` accept one
|
|
173
|
+
statement at a time (use `exec` for scripts); errors are `PostgresError` with `code` set to the
|
|
174
|
+
SQLSTATE, like `pg`'s `DatabaseError.code`.
|
|
156
175
|
|
|
157
176
|
### Wire-protocol server (separate processes)
|
|
158
177
|
|
|
159
178
|
When another process must connect over TCP — `pg`, `postgres.js`, a JDBC client, `psql`, a service
|
|
160
|
-
in a local multi-service stack — start the frontend/backend v3 server
|
|
179
|
+
in a local multi-service stack — start the frontend/backend v3 server. It needs
|
|
161
180
|
`node:net`, so it is a separate Node/Bun entry (`/wire`) and the main package stays browser-safe.
|
|
162
181
|
|
|
163
182
|
```ts
|