@crvouga/mockingbird-service-postgres 1.6.0 → 1.7.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/CHANGELOG.md +17 -0
- package/COMPATIBILITY.md +4 -0
- package/README.md +7 -0
- package/dist/admin.js +64 -13
- package/dist/admin.js.map +2 -2
- package/dist/server.d.ts +9 -0
- package/dist/server.js +25808 -0
- package/dist/server.js.map +7 -0
- package/dist/wire/cli.js +1190 -107
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/cluster.d.ts +3 -0
- package/dist/wire/fleet.d.ts +80 -0
- package/dist/wire/index.d.ts +6 -0
- package/dist/wire/index.js +23 -3
- package/dist/wire/index.js.map +2 -2
- package/dist/wire/probe.d.ts +2 -0
- package/package.json +13 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-postgres
|
|
2
2
|
|
|
3
|
+
## 1.7.0 (2026-10-02)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- coordinate HTTP and protocol mocks ([3168653](https://github.com/crvouga/mockingbird/commit/31686534c55a9823fdaa92afc9cc456cf741b809))
|
|
8
|
+
|
|
9
|
+
### Fixes and improvements
|
|
10
|
+
|
|
11
|
+
- decouple smoke types from built packages ([a4b31c6](https://github.com/crvouga/mockingbird/commit/a4b31c6205d92aa0c6f7c9de3518d5ae44d38829))
|
|
12
|
+
- protect child admin endpoints ([5410b94](https://github.com/crvouga/mockingbird/commit/5410b948a30dcedc21e9a70e44fd415c1ea25394))
|
|
13
|
+
- explain discovery and controls ([dd0fed6](https://github.com/crvouga/mockingbird/commit/dd0fed6f5f94cac51ae71f2d2e7d58a6b01ccc63))
|
|
14
|
+
- harden protocol startup and state ([6f5b557](https://github.com/crvouga/mockingbird/commit/6f5b557e87b48b31985949e04f3b5ef58f51d295))
|
|
15
|
+
- preserve checkpoints and lifecycle ([50e8c32](https://github.com/crvouga/mockingbird/commit/50e8c322feded2f7f45afce9d8cdef43c37ab9ab))
|
|
16
|
+
- clean branch state on reset ([3b714d4](https://github.com/crvouga/mockingbird/commit/3b714d4a5df8e75fee0bb31e98d7c7566e164381))
|
|
17
|
+
- type signal listener cleanup ([5279852](https://github.com/crvouga/mockingbird/commit/5279852e56ea60262ccf17aa6e46b3c31091a34a))
|
|
18
|
+
|
|
3
19
|
## 1.6.0 (2026-10-01)
|
|
4
20
|
|
|
5
21
|
### Features
|
|
@@ -36,6 +52,7 @@
|
|
|
36
52
|
- allow unpopulated materialized views to reference each other ([3ce1d8f](https://github.com/crvouga/mockingbird/commit/3ce1d8f555ed4008dd894aed20ebf64ab0253774))
|
|
37
53
|
- support CREATE EXTENSION pg_trgm ([2296ba2](https://github.com/crvouga/mockingbird/commit/2296ba2f3d1a9ea7ce8ec9f528e979034692ebf1))
|
|
38
54
|
- install pgcrypto digest() ([b5f3ccf](https://github.com/crvouga/mockingbird/commit/b5f3ccf79c6fedcabab157f1ee07af18a7871057))
|
|
55
|
+
- boot Bolt over Socket Mode ([01322df](https://github.com/crvouga/mockingbird/commit/01322df315c66b1f2f5b53094cbbeeeef6d3ae6c))
|
|
39
56
|
|
|
40
57
|
## 1.3.1 (2026-09-29)
|
|
41
58
|
|
package/COMPATIBILITY.md
CHANGED
|
@@ -10,6 +10,10 @@ See [COMPATIBILITY-AUDIT.md](COMPATIBILITY-AUDIT.md) for the latest evidence-bas
|
|
|
10
10
|
|
|
11
11
|
Reference oracle: **PostgreSQL 18.3** via PGlite (`@electric-sql/pglite`, real Postgres compiled to WASM, in-process). Inventory: `bun run inventory`. Construct catalog: `bun run scenarios` → [`compat/scenarios.ts`](compat/scenarios.ts). Divergences: [`compat/divergences.json`](compat/divergences.json). Requirements matrix: `bun run requirements` → `compat/requirements.json` + `compat/coverage.json`.
|
|
12
12
|
|
|
13
|
+
The Node `/server` entry participates in protocol fleets. Acceptance tests use `pg@8.23.0`
|
|
14
|
+
for authenticated readiness, shutdown, namespace isolation, full-catalog reset/snapshot/restore and
|
|
15
|
+
clock controls. This supervisor surface does not change the SQL dialect compatibility contract.
|
|
16
|
+
|
|
13
17
|
## Proof surface
|
|
14
18
|
|
|
15
19
|
Differential tests compare a **tuple** per statement: rows (normalized to canonical PostgreSQL text where typed), column names and type names where requested, error SQLSTATE class + normalized message, command tag / `rowCount`, and transaction status, plus a **logical state dump** (catalog names, column definitions, row payloads, sequence values) after write sequences.
|
package/README.md
CHANGED
|
@@ -193,6 +193,13 @@ server from one frozen template, so a seeded stack starts from the same bytes ea
|
|
|
193
193
|
surface as every other mock, including the table explorer (`GET /sql/tables`, `POST /sql/query`).
|
|
194
194
|
The wire server does not speak that HTTP API.
|
|
195
195
|
|
|
196
|
+
`mockingbird-postgres serve --config mockingbird.json` also supervises a mixed HTTP/Postgres/Redis
|
|
197
|
+
fleet through the Node `/server` entry. See [fleet configuration and controls](../../../docs/FLEETS.md)
|
|
198
|
+
for ephemeral discovery, namespace endpoints, aggregate health, snapshots and clocks. Each declared
|
|
199
|
+
namespace has an independent cluster. Fleet checkpoints restore its entire catalog, including created
|
|
200
|
+
and dropped databases, records, deterministic random state, faults and time; live client sessions
|
|
201
|
+
return a conflict until closed.
|
|
202
|
+
|
|
196
203
|
`server.snapshot()` freezes the live state; `server.fault({ dropConnection | delayStatementMs |
|
|
197
204
|
failCommit })` arms the next statement or connection for a drop, a delay, or a `40001` commit
|
|
198
205
|
failure.
|
package/dist/admin.js
CHANGED
|
@@ -1561,29 +1561,30 @@ var createControlPlane = (context) => {
|
|
|
1561
1561
|
return json(200, { status: "ok", reset: target === "*" ? context.namespaces() : [target] });
|
|
1562
1562
|
},
|
|
1563
1563
|
"GET /namespaces": () => json(200, { default: context.defaultNamespace, namespaces: context.namespaces() }),
|
|
1564
|
-
"GET /clock": () => json(200, context.clock.state()),
|
|
1565
|
-
"POST /clock": ({ body }) => {
|
|
1564
|
+
"GET /clock": ({ namespace }) => json(200, (context.namespaceClock?.(namespace) ?? context.clock).state()),
|
|
1565
|
+
"POST /clock": ({ body, namespace }) => {
|
|
1566
|
+
const clock = context.namespaceClock?.(namespace) ?? context.clock;
|
|
1566
1567
|
if (!isRecord2(body))
|
|
1567
1568
|
return adminError(400, "expected a JSON object");
|
|
1568
1569
|
if (body.reset === true)
|
|
1569
|
-
|
|
1570
|
+
clock.reset();
|
|
1570
1571
|
if (body.freeze === true)
|
|
1571
|
-
|
|
1572
|
+
clock.freeze();
|
|
1572
1573
|
if (body.set !== void 0) {
|
|
1573
1574
|
const instant = parseInstant(body.set);
|
|
1574
1575
|
if (instant === void 0)
|
|
1575
1576
|
return adminError(400, "set: expected epoch ms or ISO-8601");
|
|
1576
|
-
|
|
1577
|
+
clock.set(instant);
|
|
1577
1578
|
}
|
|
1578
1579
|
if (body.advance !== void 0) {
|
|
1579
1580
|
const delta = parseDuration(body.advance);
|
|
1580
1581
|
if (delta === void 0)
|
|
1581
1582
|
return adminError(400, 'advance: expected ms or "15m"-style');
|
|
1582
|
-
|
|
1583
|
+
clock.advance(delta);
|
|
1583
1584
|
}
|
|
1584
1585
|
if (body.freeze === false)
|
|
1585
|
-
|
|
1586
|
-
return json(200,
|
|
1586
|
+
clock.unfreeze();
|
|
1587
|
+
return json(200, clock.state());
|
|
1587
1588
|
},
|
|
1588
1589
|
"GET /faults": () => json(200, { faults: context.faults.list() }),
|
|
1589
1590
|
"POST /faults": ({ body, namespace }) => {
|
|
@@ -2425,7 +2426,33 @@ var faultResponse = (rule) => {
|
|
|
2425
2426
|
};
|
|
2426
2427
|
var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) => {
|
|
2427
2428
|
const entries = [];
|
|
2429
|
+
let isolated = false;
|
|
2430
|
+
const scopedRngs = /* @__PURE__ */ new Map();
|
|
2431
|
+
const random = (namespace) => {
|
|
2432
|
+
if (!isolated || namespace === "default")
|
|
2433
|
+
return rng;
|
|
2434
|
+
let scoped = scopedRngs.get(namespace);
|
|
2435
|
+
if (!scoped) {
|
|
2436
|
+
scoped = createRng(rng.seed);
|
|
2437
|
+
scopedRngs.set(namespace, scoped);
|
|
2438
|
+
}
|
|
2439
|
+
return scoped;
|
|
2440
|
+
};
|
|
2428
2441
|
return {
|
|
2442
|
+
isolateNamespaces() {
|
|
2443
|
+
isolated = true;
|
|
2444
|
+
},
|
|
2445
|
+
snapshot(namespace) {
|
|
2446
|
+
return {
|
|
2447
|
+
rules: structuredClone(entries.map((entry, position) => ({
|
|
2448
|
+
...entry.rule,
|
|
2449
|
+
remaining: entry.remaining,
|
|
2450
|
+
hits: entry.hits,
|
|
2451
|
+
position
|
|
2452
|
+
})).filter((entry) => entry.namespace === namespace)),
|
|
2453
|
+
rngState: random(namespace).state()
|
|
2454
|
+
};
|
|
2455
|
+
},
|
|
2429
2456
|
add(rule) {
|
|
2430
2457
|
const existing = entries.findIndex((e) => e.rule.id === rule.id);
|
|
2431
2458
|
const entry = { rule, remaining: rule.count ?? null, hits: 0 };
|
|
@@ -2445,6 +2472,16 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
|
|
|
2445
2472
|
},
|
|
2446
2473
|
clear() {
|
|
2447
2474
|
entries.length = 0;
|
|
2475
|
+
rng.reset();
|
|
2476
|
+
scopedRngs.clear();
|
|
2477
|
+
},
|
|
2478
|
+
restore(namespace, state) {
|
|
2479
|
+
for (let i = entries.length - 1; i >= 0; i--)
|
|
2480
|
+
if (entries[i]?.rule.namespace === namespace)
|
|
2481
|
+
entries.splice(i, 1);
|
|
2482
|
+
for (const { remaining, hits, position, ...rule } of state.rules)
|
|
2483
|
+
entries.splice(Math.min(position, entries.length), 0, { rule, remaining, hits });
|
|
2484
|
+
random(namespace).setState(state.rngState);
|
|
2448
2485
|
},
|
|
2449
2486
|
async take(candidate) {
|
|
2450
2487
|
const hits = [];
|
|
@@ -2454,7 +2491,7 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
|
|
|
2454
2491
|
if (!matches(entry.rule, candidate))
|
|
2455
2492
|
continue;
|
|
2456
2493
|
const rate = entry.rule.rate ?? 1;
|
|
2457
|
-
if (
|
|
2494
|
+
if (random(candidate.namespace).next() >= rate)
|
|
2458
2495
|
continue;
|
|
2459
2496
|
entry.hits++;
|
|
2460
2497
|
if (entry.remaining !== null)
|
|
@@ -2521,7 +2558,8 @@ var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
|
|
|
2521
2558
|
};
|
|
2522
2559
|
|
|
2523
2560
|
// ../core/dist/metrics.js
|
|
2524
|
-
var createMetrics = () => {
|
|
2561
|
+
var createMetrics = (partition = true) => {
|
|
2562
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
2525
2563
|
let requests = 0;
|
|
2526
2564
|
let faults = 0;
|
|
2527
2565
|
let totalDurationMs = 0;
|
|
@@ -2529,6 +2567,14 @@ var createMetrics = () => {
|
|
|
2529
2567
|
const unmatched = /* @__PURE__ */ new Map();
|
|
2530
2568
|
return {
|
|
2531
2569
|
record(entry) {
|
|
2570
|
+
if (partition) {
|
|
2571
|
+
let scoped = namespaces.get(entry.namespace);
|
|
2572
|
+
if (!scoped) {
|
|
2573
|
+
scoped = createMetrics(false);
|
|
2574
|
+
namespaces.set(entry.namespace, scoped);
|
|
2575
|
+
}
|
|
2576
|
+
scoped.record(entry);
|
|
2577
|
+
}
|
|
2532
2578
|
requests++;
|
|
2533
2579
|
totalDurationMs += entry.durationMs;
|
|
2534
2580
|
if (entry.faultId !== void 0)
|
|
@@ -2540,7 +2586,7 @@ var createMetrics = () => {
|
|
|
2540
2586
|
unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
|
|
2541
2587
|
}
|
|
2542
2588
|
},
|
|
2543
|
-
report: () => ({
|
|
2589
|
+
report: (namespace) => namespace !== void 0 ? (namespaces.get(namespace) ?? createMetrics(false)).report() : {
|
|
2544
2590
|
requests,
|
|
2545
2591
|
byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
|
|
2546
2592
|
unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
|
|
@@ -2549,8 +2595,13 @@ var createMetrics = () => {
|
|
|
2549
2595
|
}),
|
|
2550
2596
|
faults,
|
|
2551
2597
|
totalDurationMs
|
|
2552
|
-
}
|
|
2553
|
-
reset() {
|
|
2598
|
+
},
|
|
2599
|
+
reset(namespace) {
|
|
2600
|
+
if (namespace !== void 0) {
|
|
2601
|
+
namespaces.delete(namespace);
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
namespaces.clear();
|
|
2554
2605
|
requests = 0;
|
|
2555
2606
|
faults = 0;
|
|
2556
2607
|
totalDurationMs = 0;
|