@crvouga/mockingbird-service-postgres 3.4.0 → 3.5.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/dist/server.js CHANGED
@@ -26967,7 +26967,7 @@ function readSlabCell(column, rowIndex, intern, rowCount) {
26967
26967
  }
26968
26968
  if (pack === PACK_INT) {
26969
26969
  const integer2 = view.getBigInt64(nonNullIndex * 8, true);
26970
- return integer2 <= BigInt(Number.MAX_SAFE_INTEGER) && integer2 >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer2) : integer2;
26970
+ return !column.integerBigInt && integer2 <= BigInt(Number.MAX_SAFE_INTEGER) && integer2 >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer2) : integer2;
26971
26971
  }
26972
26972
  if (pack === PACK_TEXT_INTERN) {
26973
26973
  const id = view.getUint32(nonNullIndex * 4, true);
@@ -27528,7 +27528,8 @@ function decodeInner(snapshot, prng, clock) {
27528
27528
  if (meta.stats) state.tableStats.set(table.oid, meta.stats);
27529
27529
  const rowCount = readVarintU32(r);
27530
27530
  const slabColumns = [];
27531
- for (let c = 0; c < meta.columns.length; c++) slabColumns.push(readPackedColumn(r, rowCount, intern));
27531
+ for (let c = 0; c < meta.columns.length; c++)
27532
+ slabColumns.push(readPackedColumn(r, rowCount, intern, meta.columns[c].type.id));
27532
27533
  table.attachSlab(new ColumnarSlab(snapshot, rowCount, slabColumns, intern));
27533
27534
  schema.tables.set(table.name, table);
27534
27535
  }
@@ -27541,7 +27542,7 @@ function decodeInner(snapshot, prng, clock) {
27541
27542
  const matRowCount = readVarintU32(r);
27542
27543
  const width = readVarintU32(r);
27543
27544
  const cols = [];
27544
- for (let c = 0; c < width; c++) cols.push(readPackedColumn(r, matRowCount, intern));
27545
+ for (let c = 0; c < width; c++) cols.push(readPackedColumn(r, matRowCount, intern, meta.matColumns?.[c]?.type));
27545
27546
  const slab = new ColumnarSlab(snapshot, matRowCount, cols, intern);
27546
27547
  matRows = slab.materialize();
27547
27548
  }
@@ -27813,7 +27814,7 @@ function writePackedColumn(w, rows, col, internId, forceId) {
27813
27814
  writeTaggedDatum(w, rows[i][col], internId, forceId);
27814
27815
  }
27815
27816
  }
27816
- function readPackedColumn(r, n, intern) {
27817
+ function readPackedColumn(r, n, intern, type) {
27817
27818
  const pack = r.u8();
27818
27819
  if (pack === PACK_NULL) return { pack, nullBitmap: null, payload: new Uint8Array(0) };
27819
27820
  const bits = r.raw(n + 7 >> 3);
@@ -27826,7 +27827,8 @@ function readPackedColumn(r, n, intern) {
27826
27827
  r.skipAlign4();
27827
27828
  const width = 8;
27828
27829
  const payload = r.raw(nonNull * width);
27829
- return { pack, nullBitmap: bits, payload };
27830
+ const integerBigInt = type !== void 0 && ["int8", "money", "time", "timestamp", "timestamptz"].includes(type);
27831
+ return { pack, nullBitmap: bits, payload, integerBigInt };
27830
27832
  }
27831
27833
  if (pack === PACK_TEXT_INTERN) {
27832
27834
  r.skipAlign4();
@@ -30531,6 +30533,9 @@ var start = async (options) => {
30531
30533
  });
30532
30534
  };
30533
30535
 
30536
+ // src/wire/fleet.ts
30537
+ import { createHash as createHash4 } from "node:crypto";
30538
+
30534
30539
  // ../core/dist/clock.js
30535
30540
  var createClock = (source = Date.now) => {
30536
30541
  let offsetMs = 0;
@@ -30572,6 +30577,310 @@ var createClock = (source = Date.now) => {
30572
30577
  };
30573
30578
  };
30574
30579
 
30580
+ // ../core/dist/controls.js
30581
+ var ControlError = class extends RangeError {
30582
+ constructor(message2) {
30583
+ super(message2);
30584
+ this.name = "ControlError";
30585
+ }
30586
+ };
30587
+ var UNITS = {
30588
+ ms: 1,
30589
+ s: 1e3,
30590
+ m: 6e4,
30591
+ h: 36e5,
30592
+ d: 864e5
30593
+ };
30594
+ var parseDuration = (value) => {
30595
+ if (typeof value === "number")
30596
+ return Number.isFinite(value) ? value : void 0;
30597
+ if (typeof value !== "string")
30598
+ return void 0;
30599
+ const match = /^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h|d)?$/.exec(value.trim());
30600
+ if (!match)
30601
+ return void 0;
30602
+ return Number(match[1]) * UNITS[match[2] ?? "ms"];
30603
+ };
30604
+ var parseInstant = (value) => {
30605
+ if (typeof value === "number")
30606
+ return Number.isFinite(value) ? value : void 0;
30607
+ if (typeof value !== "string")
30608
+ return void 0;
30609
+ const text = value.trim();
30610
+ if (/^-?\d+$/.test(text))
30611
+ return Number(text);
30612
+ const parsed = Date.parse(text);
30613
+ return Number.isNaN(parsed) ? void 0 : parsed;
30614
+ };
30615
+ var durationMs = (value, label, negative = false) => {
30616
+ const parsed = parseDuration(value);
30617
+ if (parsed === void 0)
30618
+ throw new ControlError(`${label}: expected milliseconds or a duration like "250ms" or "15m"`);
30619
+ if (!negative && parsed < 0)
30620
+ throw new ControlError(`${label}: must not be negative`);
30621
+ return parsed;
30622
+ };
30623
+ var instantMs = (value, label) => {
30624
+ const parsed = parseInstant(value);
30625
+ if (parsed === void 0)
30626
+ throw new ControlError(`${label}: expected epoch ms or ISO-8601`);
30627
+ return parsed;
30628
+ };
30629
+ var normalizeSeed = (seed, label = "seed") => {
30630
+ if (typeof seed === "number" && Number.isFinite(seed))
30631
+ return seed;
30632
+ if (typeof seed === "string" && seed !== "")
30633
+ return /^\d+$/.test(seed) ? Number(seed) : seed;
30634
+ throw new ControlError(`${label}: expected a number or a non-empty string`);
30635
+ };
30636
+ var SNAPSHOT_FORMAT = "mockingbird.snapshot/v1";
30637
+ var NAMESPACE_KEYS = /* @__PURE__ */ new Set(["seed", "clock", "virtualTime", "latency", "faults", "snapshot"]);
30638
+ var MOCK_KEYS = /* @__PURE__ */ new Set([...NAMESPACE_KEYS, "credentials", "namespaces"]);
30639
+ var REPORTED_KEYS = /* @__PURE__ */ new Set(["namespace", "snapshots"]);
30640
+ var REPORTED_CLOCK_KEYS = /* @__PURE__ */ new Set(["iso", "offsetMs"]);
30641
+ var KEY_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
30642
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
30643
+ var unknownKeys = (value, known, label, reported) => {
30644
+ for (const key of Object.keys(value)) {
30645
+ if (!known.has(key) && !reported?.has(key))
30646
+ throw new ControlError(`${label}: unknown control ${JSON.stringify(key)}; expected ${[...known].join(", ")}`);
30647
+ }
30648
+ };
30649
+ var latencyRule = (value, label) => {
30650
+ if (!isRecord2(value))
30651
+ return { delayMs: durationMs(value, label), jitterMs: 0 };
30652
+ unknownKeys(value, /* @__PURE__ */ new Set(["delay", "jitter", "delayMs", "jitterMs", "operations"]), label);
30653
+ return {
30654
+ delayMs: durationMs(value.delay ?? value.delayMs ?? 0, `${label}.delay`),
30655
+ jitterMs: durationMs(value.jitter ?? value.jitterMs ?? 0, `${label}.jitter`)
30656
+ };
30657
+ };
30658
+ var normalizeLatency = (value, label = "latency") => {
30659
+ const base = latencyRule(value, label);
30660
+ const operations = {};
30661
+ if (isRecord2(value) && value.operations !== void 0) {
30662
+ if (!isRecord2(value.operations))
30663
+ throw new ControlError(`${label}.operations: expected an object keyed by operationId`);
30664
+ for (const [operationId, rule] of Object.entries(value.operations)) {
30665
+ if (isRecord2(rule) && rule.operations !== void 0)
30666
+ throw new ControlError(`${label}.operations.${operationId}: cannot nest operations`);
30667
+ operations[operationId] = latencyRule(rule, `${label}.operations.${operationId}`);
30668
+ }
30669
+ }
30670
+ return { ...base, operations };
30671
+ };
30672
+ var normalizeClock = (value, label = "clock") => {
30673
+ if (!isRecord2(value))
30674
+ throw new ControlError(`${label}: expected an object`);
30675
+ unknownKeys(value, /* @__PURE__ */ new Set(["now", "set", "frozen", "freeze", "autoAdvance", "autoAdvanceMs"]), label, REPORTED_CLOCK_KEYS);
30676
+ const plan = {};
30677
+ const now = value.now ?? value.set;
30678
+ if (now !== void 0)
30679
+ plan.now = instantMs(now, `${label}.now`);
30680
+ const frozen = value.frozen ?? value.freeze;
30681
+ if (frozen !== void 0) {
30682
+ if (typeof frozen !== "boolean")
30683
+ throw new ControlError(`${label}.frozen: expected a boolean`);
30684
+ plan.frozen = frozen;
30685
+ }
30686
+ const auto = value.autoAdvance ?? value.autoAdvanceMs;
30687
+ if (auto !== void 0)
30688
+ plan.autoAdvanceMs = durationMs(auto, `${label}.autoAdvance`);
30689
+ return plan;
30690
+ };
30691
+ var assertSnapshotDocument = (value, label = "snapshot") => {
30692
+ if (!isRecord2(value))
30693
+ throw new ControlError(`${label}: expected a snapshot document`);
30694
+ if (value.format !== SNAPSHOT_FORMAT)
30695
+ throw new ControlError(`${label}.format: expected ${JSON.stringify(SNAPSHOT_FORMAT)}`);
30696
+ if (!Array.isArray(value.records))
30697
+ throw new ControlError(`${label}.records: expected a list`);
30698
+ value.records.forEach((row, index) => {
30699
+ if (!isRecord2(row) || typeof row.collection !== "string" || typeof row.id !== "string" || row.value === void 0 && typeof row.raw !== "string" || row.seq !== void 0 && !Number.isSafeInteger(row.seq))
30700
+ throw new ControlError(`${label}.records[${index}]: expected {collection, id, value, seq?}`);
30701
+ });
30702
+ if (value.sequences !== void 0) {
30703
+ if (!Array.isArray(value.sequences))
30704
+ throw new ControlError(`${label}.sequences: expected a list`);
30705
+ value.sequences.forEach((row, index) => {
30706
+ if (!isRecord2(row) || typeof row.name !== "string" || typeof row.kind !== "string" || !Number.isSafeInteger(row.value))
30707
+ throw new ControlError(`${label}.sequences[${index}]: expected {name, kind, value}`);
30708
+ });
30709
+ }
30710
+ if (value.clock !== void 0) {
30711
+ if (!isRecord2(value.clock) || typeof value.clock.now !== "number" || !Number.isFinite(value.clock.now) || typeof value.clock.frozen !== "boolean")
30712
+ throw new ControlError(`${label}.clock: expected {now, frozen}`);
30713
+ }
30714
+ return value;
30715
+ };
30716
+ var normalizeControls = (value, label = "controls") => {
30717
+ if (!isRecord2(value))
30718
+ throw new ControlError(`${label}: expected an object`);
30719
+ unknownKeys(value, NAMESPACE_KEYS, label, REPORTED_KEYS);
30720
+ const plan = {};
30721
+ if (value.seed !== void 0)
30722
+ plan.seed = normalizeSeed(value.seed, `${label}.seed`);
30723
+ if (value.clock !== void 0)
30724
+ plan.clock = normalizeClock(value.clock, `${label}.clock`);
30725
+ if (value.virtualTime !== void 0) {
30726
+ if (typeof value.virtualTime !== "boolean")
30727
+ throw new ControlError(`${label}.virtualTime: expected a boolean`);
30728
+ plan.virtualTime = value.virtualTime;
30729
+ }
30730
+ if (value.latency !== void 0)
30731
+ plan.latency = normalizeLatency(value.latency, `${label}.latency`);
30732
+ if (value.faults !== void 0) {
30733
+ if (!Array.isArray(value.faults) || !value.faults.every(isRecord2))
30734
+ throw new ControlError(`${label}.faults: expected a list of fault rules`);
30735
+ plan.faults = value.faults;
30736
+ }
30737
+ if (value.snapshot !== void 0)
30738
+ plan.snapshot = assertSnapshotDocument(value.snapshot, `${label}.snapshot`);
30739
+ return plan;
30740
+ };
30741
+ var normalizeBootControls = (value, label = "controls") => {
30742
+ if (value === void 0)
30743
+ return { shared: {}, namespaces: {}, credentials: {} };
30744
+ if (!isRecord2(value))
30745
+ throw new ControlError(`${label}: expected an object`);
30746
+ unknownKeys(value, MOCK_KEYS, label);
30747
+ const { credentials, namespaces, ...shared } = value;
30748
+ const plan = {
30749
+ shared: normalizeControls(shared, label),
30750
+ namespaces: {},
30751
+ credentials: {}
30752
+ };
30753
+ if (namespaces !== void 0) {
30754
+ if (!isRecord2(namespaces))
30755
+ throw new ControlError(`${label}.namespaces: expected an object`);
30756
+ for (const [name, each] of Object.entries(namespaces)) {
30757
+ if (!KEY_PATTERN.test(name))
30758
+ throw new ControlError(`${label}.namespaces: bad namespace ${JSON.stringify(name)}`);
30759
+ plan.namespaces[name] = normalizeControls(each, `${label}.namespaces.${name}`);
30760
+ }
30761
+ }
30762
+ if (credentials !== void 0) {
30763
+ if (!isRecord2(credentials))
30764
+ throw new ControlError(`${label}.credentials: expected an object`);
30765
+ for (const [credential, namespace] of Object.entries(credentials)) {
30766
+ if (typeof namespace !== "string" || !KEY_PATTERN.test(namespace))
30767
+ throw new ControlError(`${label}.credentials: bad namespace for a credential`);
30768
+ plan.credentials[credential] = namespace;
30769
+ }
30770
+ }
30771
+ return plan;
30772
+ };
30773
+ var CONTROL_HEADERS = {
30774
+ namespace: "x-mockingbird-namespace",
30775
+ adminKey: "x-mockingbird-admin-key",
30776
+ branch: "x-mockingbird-branch",
30777
+ at: "x-mockingbird-at",
30778
+ reset: "x-mockingbird-reset",
30779
+ restore: "x-mockingbird-restore",
30780
+ seed: "x-mockingbird-seed",
30781
+ time: "x-mockingbird-time",
30782
+ advance: "x-mockingbird-advance",
30783
+ virtualTime: "x-mockingbird-virtual-time",
30784
+ fault: "x-mockingbird-fault",
30785
+ delay: "x-mockingbird-delay",
30786
+ timeout: "x-mockingbird-timeout",
30787
+ snapshot: "x-mockingbird-snapshot"
30788
+ };
30789
+ var CONTROL_HEADER_REFERENCE = [
30790
+ {
30791
+ header: CONTROL_HEADERS.namespace,
30792
+ value: "<key>",
30793
+ scope: "select",
30794
+ guarded: false,
30795
+ summary: "Isolation key. Each key has its own records, clock, random streams, faults, latency, journal and snapshots."
30796
+ },
30797
+ {
30798
+ header: CONTROL_HEADERS.branch,
30799
+ value: "<name>",
30800
+ scope: "select",
30801
+ guarded: false,
30802
+ summary: "Read and write a named copy-on-write branch of the namespace's history."
30803
+ },
30804
+ {
30805
+ header: CONTROL_HEADERS.at,
30806
+ value: "<checkpoint>",
30807
+ scope: "select",
30808
+ guarded: false,
30809
+ summary: "Read the namespace as it was at a checkpoint. With a branch, start the branch there."
30810
+ },
30811
+ {
30812
+ header: CONTROL_HEADERS.reset,
30813
+ value: "true",
30814
+ scope: "namespace",
30815
+ guarded: true,
30816
+ summary: "Reset the namespace to its boot state before the request is handled."
30817
+ },
30818
+ {
30819
+ header: CONTROL_HEADERS.restore,
30820
+ value: "<snapshot>",
30821
+ scope: "namespace",
30822
+ guarded: true,
30823
+ summary: "Restore a snapshot before the request is handled."
30824
+ },
30825
+ {
30826
+ header: CONTROL_HEADERS.seed,
30827
+ value: "<seed>",
30828
+ scope: "namespace",
30829
+ guarded: true,
30830
+ summary: "Restart the namespace's random streams from this seed."
30831
+ },
30832
+ {
30833
+ header: CONTROL_HEADERS.time,
30834
+ value: "<epoch ms | ISO-8601>",
30835
+ scope: "namespace",
30836
+ guarded: true,
30837
+ summary: "Freeze the namespace clock at this instant."
30838
+ },
30839
+ {
30840
+ header: CONTROL_HEADERS.advance,
30841
+ value: "<duration>",
30842
+ scope: "namespace",
30843
+ guarded: true,
30844
+ summary: "Move the namespace clock by this much, forward or (negative) back."
30845
+ },
30846
+ {
30847
+ header: CONTROL_HEADERS.virtualTime,
30848
+ value: "true | false",
30849
+ scope: "namespace",
30850
+ guarded: true,
30851
+ summary: "Spend injected waits on the mock clock (true) or the wall clock (false)."
30852
+ },
30853
+ {
30854
+ header: CONTROL_HEADERS.delay,
30855
+ value: "<duration>",
30856
+ scope: "request",
30857
+ guarded: true,
30858
+ summary: "Hold this response back this long, on top of the namespace's latency."
30859
+ },
30860
+ {
30861
+ header: CONTROL_HEADERS.timeout,
30862
+ value: "<duration>",
30863
+ scope: "request",
30864
+ guarded: true,
30865
+ summary: "Never answer this request: hold it this long, then drop the connection."
30866
+ },
30867
+ {
30868
+ header: CONTROL_HEADERS.fault,
30869
+ value: '<status> | drop | <preset> | key=value; \u2026 | {"status": 503, \u2026}',
30870
+ scope: "request",
30871
+ guarded: true,
30872
+ summary: "Fault this request only. Stored fault rules are not consulted when it answers."
30873
+ },
30874
+ {
30875
+ header: CONTROL_HEADERS.snapshot,
30876
+ value: "<name>",
30877
+ scope: "namespace",
30878
+ guarded: true,
30879
+ summary: "Snapshot the namespace after the request is handled. Echoed on the response."
30880
+ }
30881
+ ];
30882
+ var GUARDED = CONTROL_HEADER_REFERENCE.filter((each) => each.guarded).map((each) => each.header);
30883
+
30575
30884
  // src/wire/probe.ts
30576
30885
  import { createHash as createHash3, createHmac as createHmac2, pbkdf2Sync as pbkdf2Sync2, randomBytes as randomBytes2 } from "node:crypto";
30577
30886
  import { createConnection } from "node:net";
@@ -30676,26 +30985,38 @@ function probe(connectionString) {
30676
30985
  async function startProtocol(entry) {
30677
30986
  const namespaces = /* @__PURE__ */ new Map();
30678
30987
  const selectors = { default: entry.database ?? "postgres", ...entry.namespaces };
30988
+ if (entry.seed !== void 0 && (!String(entry.seed).trim() || !Number.isFinite(Number(entry.seed))))
30989
+ throw new Error("Postgres seed must be a finite number");
30990
+ let admission;
30991
+ const boot = normalizeBootControls(entry.adminControls);
30992
+ const create = async (name, databaseName, port = 0) => {
30993
+ if (!/^[A-Za-z0-9_.-]{1,64}$/.test(name) || typeof databaseName !== "string" || !databaseName.length)
30994
+ throw new Error("invalid Postgres namespace/database name");
30995
+ const clock = createClock();
30996
+ const control = { ...boot.shared.clock, ...boot.namespaces[name]?.clock };
30997
+ if (control.frozen) clock.freeze();
30998
+ if (control.now !== void 0) clock.set(control.now);
30999
+ const database = new Database({
31000
+ now: () => new Date(clock.now()),
31001
+ ...entry.seed !== void 0 ? { seed: Number(entry.seed) } : {}
31002
+ });
31003
+ const server = await serve({
31004
+ database,
31005
+ databaseName,
31006
+ allowDatabaseSelection: true,
31007
+ host: entry.host ?? "127.0.0.1",
31008
+ port,
31009
+ ...entry.password !== void 0 ? { password: entry.password } : {},
31010
+ ...entry.user !== void 0 ? { user: entry.user } : {}
31011
+ });
31012
+ server.server.prependListener("connection", (socket) => {
31013
+ if (admission?.(name) === false) socket.destroy();
31014
+ });
31015
+ namespaces.set(name, { server, clock });
31016
+ };
30679
31017
  try {
30680
- for (const [name, databaseName] of Object.entries(selectors)) {
30681
- if (!/^[A-Za-z0-9_.-]{1,64}$/.test(name) || typeof databaseName !== "string")
30682
- throw new Error("invalid Postgres namespace/database name");
30683
- const clock = createClock();
30684
- const database = new Database({
30685
- now: () => new Date(clock.now()),
30686
- ...entry.seed !== void 0 ? { seed: Number(entry.seed) } : {}
30687
- });
30688
- const server = await serve({
30689
- database,
30690
- databaseName,
30691
- allowDatabaseSelection: true,
30692
- host: entry.host ?? "127.0.0.1",
30693
- port: name === "default" ? entry.port ?? 0 : 0,
30694
- ...entry.password !== void 0 ? { password: entry.password } : {},
30695
- ...entry.user !== void 0 ? { user: entry.user } : {}
30696
- });
30697
- namespaces.set(name, { server, clock });
30698
- }
31018
+ for (const [name, databaseName] of Object.entries(selectors))
31019
+ await create(name, databaseName, name === "default" ? entry.port ?? 0 : 0);
30699
31020
  } catch (error) {
30700
31021
  await Promise.allSettled([...namespaces.values()].map(({ server }) => server.close()));
30701
31022
  throw error;
@@ -30717,17 +31038,143 @@ async function startProtocol(entry) {
30717
31038
  protocol: "postgres",
30718
31039
  url: publicUrl("default"),
30719
31040
  connection: get("default").server.connectionString,
30720
- connections: Object.fromEntries([...namespaces].map(([name, { server }]) => [name, server.connectionString])),
30721
- namespaces: {
30722
- mechanism: "endpoint",
30723
- endpoints: Object.fromEntries([...namespaces.keys()].map((name) => [name, publicUrl(name)]))
31041
+ get connections() {
31042
+ return Object.fromEntries([...namespaces].map(([name, { server }]) => [name, server.connectionString]));
31043
+ },
31044
+ get namespaces() {
31045
+ return {
31046
+ mechanism: "endpoint",
31047
+ endpoints: Object.fromEntries([...namespaces.keys()].map((name) => [name, publicUrl(name)]))
31048
+ };
31049
+ },
31050
+ async createNamespace(name) {
31051
+ if (namespaces.has(name)) throw new Error("namespace already exists");
31052
+ await create(name, entry.database ?? "postgres");
31053
+ origins.set(name, get(name).server.snapshotAll());
31054
+ },
31055
+ async deleteNamespace(name) {
31056
+ const { server } = get(name);
31057
+ if (server.connections) throw new Error("Postgres namespace has open sessions");
31058
+ await server.close();
31059
+ for (const database of server.databaseNames()) server.getDatabase(database).close();
31060
+ namespaces.delete(name);
31061
+ origins.delete(name);
31062
+ },
31063
+ async exportCheckpoint(namespace, handle) {
31064
+ const point = typeof handle === "object" && handle !== null ? snapshots.get(handle) : void 0;
31065
+ if (!point || point.namespace !== namespace) throw new Error("invalid Postgres checkpoint");
31066
+ return {
31067
+ format: "mockingbird.postgres/v1",
31068
+ inventory: Object.fromEntries(
31069
+ [...point.databases].map(([name, snapshot]) => {
31070
+ const database = snapshot.open();
31071
+ try {
31072
+ const tables = database.prepare("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'public'").all();
31073
+ return [
31074
+ name,
31075
+ Object.fromEntries(
31076
+ tables.map((table) => {
31077
+ const identifier2 = table.table_name.replaceAll('"', '""');
31078
+ const rows = database.prepare(`SELECT * FROM public."${identifier2}"`).all();
31079
+ return [
31080
+ table.table_name,
31081
+ {
31082
+ count: rows.length,
31083
+ records: Object.fromEntries(
31084
+ rows.map((row, index) => [
31085
+ String(row.id ?? index),
31086
+ createHash4("sha256").update(
31087
+ JSON.stringify(
31088
+ row,
31089
+ (_key, value) => typeof value === "bigint" ? String(value) : value
31090
+ )
31091
+ ).digest("hex")
31092
+ ])
31093
+ )
31094
+ }
31095
+ ];
31096
+ })
31097
+ )
31098
+ ];
31099
+ } finally {
31100
+ database.close();
31101
+ }
31102
+ })
31103
+ ),
31104
+ databases: [...point.databases].map(([name, snapshot]) => ({
31105
+ name,
31106
+ bytes: Buffer.from(snapshot.encode()).toString("base64")
31107
+ })),
31108
+ clock: point.clock,
31109
+ faults: point.faults
31110
+ };
31111
+ },
31112
+ async importCheckpoint(namespace, document) {
31113
+ const doc = document;
31114
+ if (doc?.format !== "mockingbird.postgres/v1" || !Array.isArray(doc.databases) || !doc.databases.length || !Number.isFinite(doc.clock?.now) || typeof doc.clock?.frozen !== "boolean")
31115
+ throw new Error("invalid Postgres archive");
31116
+ const databases = /* @__PURE__ */ new Map();
31117
+ for (const row of doc.databases) {
31118
+ if (typeof row.name !== "string" || databases.has(row.name) || typeof row.bytes !== "string" || !/^[A-Za-z0-9+/]*={0,2}$/.test(row.bytes))
31119
+ throw new Error("invalid Postgres archive database");
31120
+ databases.set(row.name, Snapshot.decode(Buffer.from(row.bytes, "base64")));
31121
+ }
31122
+ const handle = {};
31123
+ snapshots.set(handle, { namespace, databases, clock: doc.clock, faults: doc.faults ?? {} });
31124
+ return handle;
31125
+ },
31126
+ async inspect(namespace, { offset, limit, collection }) {
31127
+ const { server } = get(namespace);
31128
+ const database = server.getDatabase(server.databaseNames()[0]);
31129
+ const tables = database.prepare("SELECT table_schema, table_name FROM information_schema.tables WHERE table_schema = 'public'").all();
31130
+ if (!collection) return { tables };
31131
+ const table = collection.replaceAll('"', '""');
31132
+ return {
31133
+ records: database.prepare(`SELECT * FROM public."${table}" LIMIT ${limit} OFFSET ${offset}`).all(),
31134
+ offset
31135
+ };
31136
+ },
31137
+ async initializationAccess() {
31138
+ const privateServers = [];
31139
+ const connections = {};
31140
+ try {
31141
+ for (const [name, { server }] of namespaces) {
31142
+ const databaseName = server.databaseNames()[0];
31143
+ const seedServer = await serve({
31144
+ database: server.getDatabase(databaseName),
31145
+ databaseName,
31146
+ port: 0,
31147
+ host: "127.0.0.1",
31148
+ ...entry.user ? { user: entry.user } : {},
31149
+ ...entry.password ? { password: entry.password } : {}
31150
+ });
31151
+ privateServers.push(seedServer);
31152
+ connections[name] = seedServer.connectionString;
31153
+ }
31154
+ } catch (error) {
31155
+ await Promise.allSettled(privateServers.map((s) => s.close()));
31156
+ throw error;
31157
+ }
31158
+ return {
31159
+ connections,
31160
+ close: async () => {
31161
+ await Promise.all(privateServers.map((s) => s.close()));
31162
+ }
31163
+ };
31164
+ },
31165
+ setAdmission(allow) {
31166
+ admission = allow;
30724
31167
  },
30725
31168
  async ready() {
31169
+ if (admission) return [...namespaces.values()].every(({ server }) => server.server.listening);
30726
31170
  const results = await Promise.all(
30727
31171
  [...namespaces.values()].map(
30728
31172
  ({ server }) => server.server.listening ? probe(server.connectionString) : false
30729
31173
  )
30730
31174
  );
31175
+ const deadline = Date.now() + 2e3;
31176
+ while ([...namespaces.values()].some(({ server }) => server.connections > 0) && Date.now() < deadline)
31177
+ await new Promise((resolve) => setTimeout(resolve, 1));
30731
31178
  return results.every(Boolean);
30732
31179
  },
30733
31180
  async close() {
@@ -30789,7 +31236,11 @@ async function startProtocol(entry) {
30789
31236
  for (const name of server.databaseNames()) server.getDatabase(name).now = () => new Date(clock.now());
30790
31237
  server.clearFaults();
30791
31238
  clock.reset();
31239
+ const control = { ...boot.shared.clock, ...boot.namespaces[namespace]?.clock };
31240
+ if (control.frozen) clock.freeze();
31241
+ if (control.now !== void 0) clock.set(control.now);
30792
31242
  },
31243
+ nextEvent: () => void 0,
30793
31244
  clock: (namespace) => get(namespace).clock
30794
31245
  };
30795
31246
  }