@fuzdev/fuz_app 0.70.0 → 0.71.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/dist/db/CLAUDE.md +3 -3
- package/dist/db/cell_history_ddl.d.ts +1 -1
- package/dist/db/cell_history_ddl.js +1 -1
- package/dist/db/fact_ddl.d.ts +11 -11
- package/dist/db/fact_ddl.d.ts.map +1 -1
- package/dist/db/fact_ddl.js +13 -13
- package/dist/db/fact_queries.d.ts +9 -9
- package/dist/db/fact_queries.d.ts.map +1 -1
- package/dist/db/fact_queries.js +18 -18
- package/dist/db/fact_store.d.ts +3 -3
- package/dist/db/fact_store.js +2 -2
- package/dist/testing/CLAUDE.md +3 -3
- package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts +59 -0
- package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/create_schema_parity_global_setup.js +27 -0
- package/dist/testing/cross_backend/setup.d.ts +16 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +16 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_stub_backend_config.js +5 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +59 -3
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +33 -3
- package/dist/testing/schema_introspect.d.ts +74 -58
- package/dist/testing/schema_introspect.d.ts.map +1 -1
- package/dist/testing/schema_introspect.js +77 -15
- package/dist/testing/schema_parity.d.ts +7 -17
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +3 -37
- package/package.json +1 -1
package/dist/db/CLAUDE.md
CHANGED
|
@@ -80,10 +80,10 @@ Content-addressed byte store. Cells reference facts by blake3 hash
|
|
|
80
80
|
(`cell.refs`, auto-extracted from `data`); the fact layer stores the bytes.
|
|
81
81
|
Optional — minimal consumers never migrate it.
|
|
82
82
|
|
|
83
|
-
- **`fact_ddl.ts`** — `
|
|
84
|
-
`external_url`, CHECK enforces exactly one) + `
|
|
83
|
+
- **`fact_ddl.ts`** — `fact` (content-addressed bytes: embedded `bytes` xor
|
|
84
|
+
`external_url`, CHECK enforces exactly one) + `fact_ref` (declared
|
|
85
85
|
dependency edges; `target_hash` deliberately not an FK, for federation) +
|
|
86
|
-
`
|
|
86
|
+
`memo` (`(fn_id, input_hash) → output_hash`). `FACT_MIGRATION_NS`, namespace
|
|
87
87
|
`fuz_facts`.
|
|
88
88
|
- **`fact_queries.ts`** — mechanical `query_put_fact` (idempotent `ON CONFLICT
|
|
89
89
|
DO NOTHING`), `_put_fact_refs`, `_get_fact` / `_get_fact_meta` / `_has_fact`
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* downstream code can target a stable schema. The table ships
|
|
9
9
|
* present-but-unwritten.
|
|
10
10
|
*
|
|
11
|
-
* `fact_hash` is intentionally **not** a foreign key to `
|
|
11
|
+
* `fact_hash` is intentionally **not** a foreign key to `fact(hash)` —
|
|
12
12
|
* snapshots may be evicted by GC policy while history rows remain as audit
|
|
13
13
|
* traces, and federation may target facts on another instance.
|
|
14
14
|
*
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* downstream code can target a stable schema. The table ships
|
|
9
9
|
* present-but-unwritten.
|
|
10
10
|
*
|
|
11
|
-
* `fact_hash` is intentionally **not** a foreign key to `
|
|
11
|
+
* `fact_hash` is intentionally **not** a foreign key to `fact(hash)` —
|
|
12
12
|
* snapshots may be evicted by GC policy while history rows remain as audit
|
|
13
13
|
* traces, and federation may target facts on another instance.
|
|
14
14
|
*
|
package/dist/db/fact_ddl.d.ts
CHANGED
|
@@ -3,32 +3,32 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Three tables:
|
|
5
5
|
*
|
|
6
|
-
* - `
|
|
6
|
+
* - `fact` — content-addressed bytes. `hash = 'blake3:<hex64>'`. Either
|
|
7
7
|
* embedded (`bytes`) or referenced (`external_url`); the CHECK constraint
|
|
8
8
|
* enforces exactly one populated. Idempotent: same bytes always produce
|
|
9
9
|
* the same hash, so `INSERT … ON CONFLICT DO NOTHING` is the put primitive.
|
|
10
|
-
* - `
|
|
10
|
+
* - `fact_ref` — declared dependency edges (source fact → target fact).
|
|
11
11
|
* `target_hash` is intentionally **not** a foreign key: in federation a
|
|
12
12
|
* reference may target a fact stored on another instance.
|
|
13
|
-
* - `
|
|
13
|
+
* - `memo` — `(fn_id, input_hash) → output_hash` for memoized computations.
|
|
14
14
|
*
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
17
|
import type { Migration, MigrationNamespace } from './migrate.js';
|
|
18
|
-
/** `
|
|
19
|
-
export declare const FACTS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS
|
|
18
|
+
/** `fact` table — content-addressed byte store. */
|
|
19
|
+
export declare const FACTS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS fact (\n\thash TEXT PRIMARY KEY,\n\tbytes BYTEA,\n\texternal_url TEXT,\n\tcontent_type TEXT,\n\tsize BIGINT NOT NULL,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tCONSTRAINT fact_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)\n)";
|
|
20
20
|
/**
|
|
21
|
-
* `
|
|
21
|
+
* `fact_ref` table — declared dependency edges between facts.
|
|
22
22
|
*
|
|
23
23
|
* `target_hash` is not a foreign key (federation: target may live remotely).
|
|
24
24
|
*/
|
|
25
|
-
export declare const FACT_REFS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS
|
|
25
|
+
export declare const FACT_REFS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS fact_ref (\n\tsource_hash TEXT NOT NULL REFERENCES fact(hash) ON DELETE CASCADE,\n\ttarget_hash TEXT NOT NULL,\n\tPRIMARY KEY (source_hash, target_hash)\n)";
|
|
26
26
|
/** Reverse lookup: which facts reference a given target? */
|
|
27
|
-
export declare const FACT_REFS_TARGET_INDEX = "\nCREATE INDEX IF NOT EXISTS
|
|
28
|
-
/** `
|
|
29
|
-
export declare const MEMOS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS
|
|
27
|
+
export declare const FACT_REFS_TARGET_INDEX = "\nCREATE INDEX IF NOT EXISTS idx_fact_ref_target ON fact_ref(target_hash)";
|
|
28
|
+
/** `memo` table — `(fn_id, input_hash) → output_hash` for memoized computations. */
|
|
29
|
+
export declare const MEMOS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS memo (\n\tfn_id TEXT NOT NULL,\n\tinput_hash TEXT NOT NULL,\n\toutput_hash TEXT NOT NULL,\n\tcreated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n\tPRIMARY KEY (fn_id, input_hash)\n)";
|
|
30
30
|
/** Tables created by `FACT_MIGRATION_NS`, in drop order (children first). */
|
|
31
|
-
export declare const FACT_DROP_TABLES: readonly ["
|
|
31
|
+
export declare const FACT_DROP_TABLES: readonly ["memo", "fact_ref", "fact"];
|
|
32
32
|
/** Fact + memo migrations. */
|
|
33
33
|
export declare const FACT_MIGRATIONS: Array<Migration>;
|
|
34
34
|
/** Namespace identifier for fact + memo migrations. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fact_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAC,SAAS,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhE,
|
|
1
|
+
{"version":3,"file":"fact_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAC,SAAS,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhE,mDAAmD;AACnD,eAAO,MAAM,YAAY,qSASvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6LAK3B,CAAC;AAEH,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,8EACqC,CAAC;AAEzE,oFAAoF;AACpF,eAAO,MAAM,YAAY,mNAOvB,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,uCAAwC,CAAC;AAEtE,8BAA8B;AAC9B,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAU5C,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,wBAAwB,cAAc,CAAC;AAEpD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
|
package/dist/db/fact_ddl.js
CHANGED
|
@@ -3,45 +3,45 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Three tables:
|
|
5
5
|
*
|
|
6
|
-
* - `
|
|
6
|
+
* - `fact` — content-addressed bytes. `hash = 'blake3:<hex64>'`. Either
|
|
7
7
|
* embedded (`bytes`) or referenced (`external_url`); the CHECK constraint
|
|
8
8
|
* enforces exactly one populated. Idempotent: same bytes always produce
|
|
9
9
|
* the same hash, so `INSERT … ON CONFLICT DO NOTHING` is the put primitive.
|
|
10
|
-
* - `
|
|
10
|
+
* - `fact_ref` — declared dependency edges (source fact → target fact).
|
|
11
11
|
* `target_hash` is intentionally **not** a foreign key: in federation a
|
|
12
12
|
* reference may target a fact stored on another instance.
|
|
13
|
-
* - `
|
|
13
|
+
* - `memo` — `(fn_id, input_hash) → output_hash` for memoized computations.
|
|
14
14
|
*
|
|
15
15
|
* @module
|
|
16
16
|
*/
|
|
17
|
-
/** `
|
|
17
|
+
/** `fact` table — content-addressed byte store. */
|
|
18
18
|
export const FACTS_SCHEMA = `
|
|
19
|
-
CREATE TABLE IF NOT EXISTS
|
|
19
|
+
CREATE TABLE IF NOT EXISTS fact (
|
|
20
20
|
hash TEXT PRIMARY KEY,
|
|
21
21
|
bytes BYTEA,
|
|
22
22
|
external_url TEXT,
|
|
23
23
|
content_type TEXT,
|
|
24
24
|
size BIGINT NOT NULL,
|
|
25
25
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
26
|
-
CONSTRAINT
|
|
26
|
+
CONSTRAINT fact_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)
|
|
27
27
|
)`;
|
|
28
28
|
/**
|
|
29
|
-
* `
|
|
29
|
+
* `fact_ref` table — declared dependency edges between facts.
|
|
30
30
|
*
|
|
31
31
|
* `target_hash` is not a foreign key (federation: target may live remotely).
|
|
32
32
|
*/
|
|
33
33
|
export const FACT_REFS_SCHEMA = `
|
|
34
|
-
CREATE TABLE IF NOT EXISTS
|
|
35
|
-
source_hash TEXT NOT NULL REFERENCES
|
|
34
|
+
CREATE TABLE IF NOT EXISTS fact_ref (
|
|
35
|
+
source_hash TEXT NOT NULL REFERENCES fact(hash) ON DELETE CASCADE,
|
|
36
36
|
target_hash TEXT NOT NULL,
|
|
37
37
|
PRIMARY KEY (source_hash, target_hash)
|
|
38
38
|
)`;
|
|
39
39
|
/** Reverse lookup: which facts reference a given target? */
|
|
40
40
|
export const FACT_REFS_TARGET_INDEX = `
|
|
41
|
-
CREATE INDEX IF NOT EXISTS
|
|
42
|
-
/** `
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_fact_ref_target ON fact_ref(target_hash)`;
|
|
42
|
+
/** `memo` table — `(fn_id, input_hash) → output_hash` for memoized computations. */
|
|
43
43
|
export const MEMOS_SCHEMA = `
|
|
44
|
-
CREATE TABLE IF NOT EXISTS
|
|
44
|
+
CREATE TABLE IF NOT EXISTS memo (
|
|
45
45
|
fn_id TEXT NOT NULL,
|
|
46
46
|
input_hash TEXT NOT NULL,
|
|
47
47
|
output_hash TEXT NOT NULL,
|
|
@@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS memos (
|
|
|
49
49
|
PRIMARY KEY (fn_id, input_hash)
|
|
50
50
|
)`;
|
|
51
51
|
/** Tables created by `FACT_MIGRATION_NS`, in drop order (children first). */
|
|
52
|
-
export const FACT_DROP_TABLES = ['
|
|
52
|
+
export const FACT_DROP_TABLES = ['memo', 'fact_ref', 'fact'];
|
|
53
53
|
/** Fact + memo migrations. */
|
|
54
54
|
export const FACT_MIGRATIONS = [
|
|
55
55
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Raw queries against the `
|
|
2
|
+
* Raw queries against the `fact` and `fact_ref` tables.
|
|
3
3
|
*
|
|
4
4
|
* Convention: `deps: QueryDeps` first, no audit side effects, mutations are
|
|
5
5
|
* idempotent (`ON CONFLICT DO NOTHING`) so the same hash can be written by
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { QueryDeps } from './query_deps.js';
|
|
15
15
|
import type { FactHash } from '@fuzdev/fuz_util/fact_hash.js';
|
|
16
|
-
/** Row shape for `SELECT … FROM
|
|
16
|
+
/** Row shape for `SELECT … FROM fact`. */
|
|
17
17
|
export interface FactRow {
|
|
18
18
|
hash: FactHash;
|
|
19
19
|
bytes: Uint8Array | null;
|
|
@@ -33,11 +33,11 @@ export interface FactMetaRow {
|
|
|
33
33
|
/**
|
|
34
34
|
* Idempotently insert a fact row.
|
|
35
35
|
*
|
|
36
|
-
* `bytes` xor `external_url` per the `
|
|
36
|
+
* `bytes` xor `external_url` per the `fact_storage_present` CHECK
|
|
37
37
|
* constraint; the caller is responsible for satisfying it (the queries
|
|
38
38
|
* layer does not second-guess). Returns `true` when a new row was
|
|
39
39
|
* inserted, `false` when a row already existed (caller can use this to
|
|
40
|
-
* decide whether to also write `
|
|
40
|
+
* decide whether to also write `fact_ref`).
|
|
41
41
|
*/
|
|
42
42
|
export declare const query_put_fact: (deps: QueryDeps, input: {
|
|
43
43
|
hash: FactHash;
|
|
@@ -62,7 +62,7 @@ export declare const query_get_fact: (deps: QueryDeps, hash: FactHash) => Promis
|
|
|
62
62
|
*/
|
|
63
63
|
export declare const query_get_fact_meta: (deps: QueryDeps, hash: FactHash) => Promise<FactMetaRow | null>;
|
|
64
64
|
/**
|
|
65
|
-
* Cheap existence check. Backed by the `
|
|
65
|
+
* Cheap existence check. Backed by the `fact` PK index.
|
|
66
66
|
*/
|
|
67
67
|
export declare const query_has_fact: (deps: QueryDeps, hash: FactHash) => Promise<boolean>;
|
|
68
68
|
/**
|
|
@@ -71,7 +71,7 @@ export declare const query_has_fact: (deps: QueryDeps, hash: FactHash) => Promis
|
|
|
71
71
|
*/
|
|
72
72
|
export declare const query_get_fact_refs: (deps: QueryDeps, source_hash: FactHash) => Promise<Array<FactHash>>;
|
|
73
73
|
/**
|
|
74
|
-
* Drop a fact row. Cascades `
|
|
74
|
+
* Drop a fact row. Cascades `fact_ref` rows via the `ON DELETE CASCADE`
|
|
75
75
|
* FK on `source_hash`. Returns the deleted row's `(size, external_url)`
|
|
76
76
|
* so the caller can unlink the disk file (if any) and tally freed bytes,
|
|
77
77
|
* or `null` when no row matched (idempotent: deleting an absent fact is
|
|
@@ -104,17 +104,17 @@ export interface OrphanFactsListResult {
|
|
|
104
104
|
}>;
|
|
105
105
|
}
|
|
106
106
|
/**
|
|
107
|
-
* Compute the "orphan facts" set: rows in `
|
|
107
|
+
* Compute the "orphan facts" set: rows in `fact` where no active
|
|
108
108
|
* (non-tombstone) `cell.refs` array contains the hash.
|
|
109
109
|
*
|
|
110
|
-
* The `cell` join is deliberately app-coupled — `
|
|
110
|
+
* The `cell` join is deliberately app-coupled — `fact` lives in the
|
|
111
111
|
* `fuz_facts` namespace and `cell.refs` lives in `fuz_cell`, but the
|
|
112
112
|
* orphan predicate only makes sense in apps that route content through
|
|
113
113
|
* cells. When a non-cell fact consumer ever appears (signed memo
|
|
114
114
|
* outputs? external fact mirrors?) the predicate moves to a generic
|
|
115
115
|
* `fact_consumers` registry; today the cell layer is the only consumer.
|
|
116
116
|
*
|
|
117
|
-
* The `older_than` filter applies to `
|
|
117
|
+
* The `older_than` filter applies to `fact.created_at`. Pass `null`
|
|
118
118
|
* to skip the filter (used by the list-summary preview); the delete
|
|
119
119
|
* handler always passes a non-null cutoff (default 0, meaning "any
|
|
120
120
|
* orphan").
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE/C,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,+BAA+B,CAAC;AAE5D,
|
|
1
|
+
{"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"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"}
|
package/dist/db/fact_queries.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Raw queries against the `
|
|
2
|
+
* Raw queries against the `fact` and `fact_ref` tables.
|
|
3
3
|
*
|
|
4
4
|
* Convention: `deps: QueryDeps` first, no audit side effects, mutations are
|
|
5
5
|
* idempotent (`ON CONFLICT DO NOTHING`) so the same hash can be written by
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
/**
|
|
15
15
|
* Idempotently insert a fact row.
|
|
16
16
|
*
|
|
17
|
-
* `bytes` xor `external_url` per the `
|
|
17
|
+
* `bytes` xor `external_url` per the `fact_storage_present` CHECK
|
|
18
18
|
* constraint; the caller is responsible for satisfying it (the queries
|
|
19
19
|
* layer does not second-guess). Returns `true` when a new row was
|
|
20
20
|
* inserted, `false` when a row already existed (caller can use this to
|
|
21
|
-
* decide whether to also write `
|
|
21
|
+
* decide whether to also write `fact_ref`).
|
|
22
22
|
*/
|
|
23
23
|
export const query_put_fact = async (deps, input) => {
|
|
24
|
-
const row = await deps.db.query_one(`INSERT INTO
|
|
24
|
+
const row = await deps.db.query_one(`INSERT INTO fact (hash, bytes, external_url, content_type, size)
|
|
25
25
|
VALUES ($1, $2, $3, $4, $5)
|
|
26
26
|
ON CONFLICT (hash) DO NOTHING
|
|
27
27
|
RETURNING hash`, [input.hash, input.bytes, input.external_url, input.content_type, input.size]);
|
|
@@ -35,7 +35,7 @@ export const query_put_fact = async (deps, input) => {
|
|
|
35
35
|
export const query_put_fact_refs = async (deps, source_hash, target_hashes) => {
|
|
36
36
|
if (target_hashes.length === 0)
|
|
37
37
|
return;
|
|
38
|
-
await deps.db.query(`INSERT INTO
|
|
38
|
+
await deps.db.query(`INSERT INTO fact_ref (source_hash, target_hash)
|
|
39
39
|
SELECT $1::text, unnest($2::text[])
|
|
40
40
|
ON CONFLICT (source_hash, target_hash) DO NOTHING`, [source_hash, target_hashes]);
|
|
41
41
|
};
|
|
@@ -45,7 +45,7 @@ export const query_put_fact_refs = async (deps, source_hash, target_hashes) => {
|
|
|
45
45
|
*/
|
|
46
46
|
export const query_get_fact = async (deps, hash) => {
|
|
47
47
|
const row = await deps.db.query_one(`SELECT hash, bytes, external_url, content_type, size, created_at
|
|
48
|
-
FROM
|
|
48
|
+
FROM fact WHERE hash = $1`, [hash]);
|
|
49
49
|
return row ?? null;
|
|
50
50
|
};
|
|
51
51
|
/**
|
|
@@ -53,14 +53,14 @@ export const query_get_fact = async (deps, hash) => {
|
|
|
53
53
|
*/
|
|
54
54
|
export const query_get_fact_meta = async (deps, hash) => {
|
|
55
55
|
const row = await deps.db.query_one(`SELECT hash, external_url, content_type, size, created_at
|
|
56
|
-
FROM
|
|
56
|
+
FROM fact WHERE hash = $1`, [hash]);
|
|
57
57
|
return row ?? null;
|
|
58
58
|
};
|
|
59
59
|
/**
|
|
60
|
-
* Cheap existence check. Backed by the `
|
|
60
|
+
* Cheap existence check. Backed by the `fact` PK index.
|
|
61
61
|
*/
|
|
62
62
|
export const query_has_fact = async (deps, hash) => {
|
|
63
|
-
const row = await deps.db.query_one(`SELECT EXISTS(SELECT 1 FROM
|
|
63
|
+
const row = await deps.db.query_one(`SELECT EXISTS(SELECT 1 FROM fact WHERE hash = $1) AS exists`, [hash]);
|
|
64
64
|
return row?.exists ?? false;
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
@@ -68,11 +68,11 @@ export const query_has_fact = async (deps, hash) => {
|
|
|
68
68
|
* that need stable ordering should sort.
|
|
69
69
|
*/
|
|
70
70
|
export const query_get_fact_refs = async (deps, source_hash) => {
|
|
71
|
-
const rows = await deps.db.query(`SELECT target_hash FROM
|
|
71
|
+
const rows = await deps.db.query(`SELECT target_hash FROM fact_ref WHERE source_hash = $1`, [source_hash]);
|
|
72
72
|
return rows.map((r) => r.target_hash);
|
|
73
73
|
};
|
|
74
74
|
/**
|
|
75
|
-
* Drop a fact row. Cascades `
|
|
75
|
+
* Drop a fact row. Cascades `fact_ref` rows via the `ON DELETE CASCADE`
|
|
76
76
|
* FK on `source_hash`. Returns the deleted row's `(size, external_url)`
|
|
77
77
|
* so the caller can unlink the disk file (if any) and tally freed bytes,
|
|
78
78
|
* or `null` when no row matched (idempotent: deleting an absent fact is
|
|
@@ -84,24 +84,24 @@ export const query_get_fact_refs = async (deps, source_hash) => {
|
|
|
84
84
|
* `PgFactStore.delete` handles the disk-file unlink.
|
|
85
85
|
*/
|
|
86
86
|
export const query_delete_fact = async (deps, hash) => {
|
|
87
|
-
const row = await deps.db.query_one(`DELETE FROM
|
|
87
|
+
const row = await deps.db.query_one(`DELETE FROM fact WHERE hash = $1
|
|
88
88
|
RETURNING size, external_url`, [hash]);
|
|
89
89
|
if (!row)
|
|
90
90
|
return null;
|
|
91
91
|
return { size: Number(row.size), external_url: row.external_url };
|
|
92
92
|
};
|
|
93
93
|
/**
|
|
94
|
-
* Compute the "orphan facts" set: rows in `
|
|
94
|
+
* Compute the "orphan facts" set: rows in `fact` where no active
|
|
95
95
|
* (non-tombstone) `cell.refs` array contains the hash.
|
|
96
96
|
*
|
|
97
|
-
* The `cell` join is deliberately app-coupled — `
|
|
97
|
+
* The `cell` join is deliberately app-coupled — `fact` lives in the
|
|
98
98
|
* `fuz_facts` namespace and `cell.refs` lives in `fuz_cell`, but the
|
|
99
99
|
* orphan predicate only makes sense in apps that route content through
|
|
100
100
|
* cells. When a non-cell fact consumer ever appears (signed memo
|
|
101
101
|
* outputs? external fact mirrors?) the predicate moves to a generic
|
|
102
102
|
* `fact_consumers` registry; today the cell layer is the only consumer.
|
|
103
103
|
*
|
|
104
|
-
* The `older_than` filter applies to `
|
|
104
|
+
* The `older_than` filter applies to `fact.created_at`. Pass `null`
|
|
105
105
|
* to skip the filter (used by the list-summary preview); the delete
|
|
106
106
|
* handler always passes a non-null cutoff (default 0, meaning "any
|
|
107
107
|
* orphan").
|
|
@@ -113,7 +113,7 @@ export const query_delete_fact = async (deps, hash) => {
|
|
|
113
113
|
*/
|
|
114
114
|
export const query_orphan_facts_list = async (deps, older_than, sample_limit) => {
|
|
115
115
|
const summary = await deps.db.query_one(`SELECT COUNT(*)::bigint AS count, COALESCE(SUM(size), 0)::bigint AS total
|
|
116
|
-
FROM
|
|
116
|
+
FROM fact f
|
|
117
117
|
WHERE NOT EXISTS (
|
|
118
118
|
SELECT 1 FROM cell c
|
|
119
119
|
WHERE c.refs @> ARRAY[f.hash]::text[]
|
|
@@ -121,7 +121,7 @@ export const query_orphan_facts_list = async (deps, older_than, sample_limit) =>
|
|
|
121
121
|
)
|
|
122
122
|
AND ($1::timestamptz IS NULL OR f.created_at < $1::timestamptz)`, [older_than]);
|
|
123
123
|
const sample_rows = await deps.db.query(`SELECT hash, size, created_at, external_url
|
|
124
|
-
FROM
|
|
124
|
+
FROM fact f
|
|
125
125
|
WHERE NOT EXISTS (
|
|
126
126
|
SELECT 1 FROM cell c
|
|
127
127
|
WHERE c.refs @> ARRAY[f.hash]::text[]
|
|
@@ -150,7 +150,7 @@ export const query_orphan_facts_list = async (deps, older_than, sample_limit) =>
|
|
|
150
150
|
*/
|
|
151
151
|
export const query_orphan_facts_select_for_delete = async (deps, older_than) => {
|
|
152
152
|
const rows = await deps.db.query(`SELECT hash, size, external_url
|
|
153
|
-
FROM
|
|
153
|
+
FROM fact f
|
|
154
154
|
WHERE NOT EXISTS (
|
|
155
155
|
SELECT 1 FROM cell c
|
|
156
156
|
WHERE c.refs @> ARRAY[f.hash]::text[]
|
package/dist/db/fact_store.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare const create_default_fetcher: () => FactExternalFetcher;
|
|
|
42
42
|
* Construction-time deps for `PgFactStore`.
|
|
43
43
|
*
|
|
44
44
|
* `embedded_threshold` (bytes) is the inline-vs-external cutoff: payloads
|
|
45
|
-
* at or under it store embedded in the `
|
|
45
|
+
* at or under it store embedded in the `fact` row, larger ones route to
|
|
46
46
|
* the external fetcher. Defaults to `FACT_EMBEDDED_THRESHOLD_DEFAULT`
|
|
47
47
|
* (1 MiB). Consumers tune it per workload — e.g. a much lower bound
|
|
48
48
|
* (~16 KiB) keeps only small JSON inline and routes image originals +
|
|
@@ -86,8 +86,8 @@ export declare class PgFactStore implements FactStore {
|
|
|
86
86
|
get_meta(hash: FactHash): Promise<FactMeta | null>;
|
|
87
87
|
get_refs(hash: FactHash): Promise<Array<FactHash>>;
|
|
88
88
|
/**
|
|
89
|
-
* Drop a fact row. `
|
|
90
|
-
* cascade via the FK; `
|
|
89
|
+
* Drop a fact row. `fact_ref` rows referencing this hash as a source
|
|
90
|
+
* cascade via the FK; `fact_ref` targeting this hash do **not** —
|
|
91
91
|
* they remain as dangling pointers, consistent with the federation
|
|
92
92
|
* model where `target_hash` is intentionally not a FK.
|
|
93
93
|
*
|
package/dist/db/fact_store.js
CHANGED
|
@@ -157,8 +157,8 @@ export class PgFactStore {
|
|
|
157
157
|
return query_get_fact_refs(this.#deps, hash);
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
|
-
* Drop a fact row. `
|
|
161
|
-
* cascade via the FK; `
|
|
160
|
+
* Drop a fact row. `fact_ref` rows referencing this hash as a source
|
|
161
|
+
* cascade via the FK; `fact_ref` targeting this hash do **not** —
|
|
162
162
|
* they remain as dangling pointers, consistent with the federation
|
|
163
163
|
* model where `target_hash` is intentionally not a FK.
|
|
164
164
|
*
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -209,15 +209,15 @@ test-only by construction.
|
|
|
209
209
|
|
|
210
210
|
### `schema_introspect.ts` — `query_schema_snapshot`
|
|
211
211
|
|
|
212
|
-
- `query_schema_snapshot(db, options?)` — introspects a live DB into a deterministic `SchemaSnapshot` via `pg_catalog` + `information_schema`. Captures tables, columns (with `udt_name` to distinguish int4/int8), indexes (`indexdef`), constraints (`pg_get_constraintdef`), sequences
|
|
213
|
-
- `SchemaSnapshot` —
|
|
212
|
+
- `query_schema_snapshot(db, options?)` — introspects a live DB into a deterministic `SchemaSnapshot` via `pg_catalog` + `information_schema`. Captures tables, columns (with `udt_name` to distinguish int4/int8), indexes (`indexdef`), constraints (`pg_get_constraintdef`), and sequences. The `schema_version` migration tracker is always excluded — it's framework bookkeeping, not domain schema, and impls organize migration namespaces differently.
|
|
213
|
+
- `SchemaSnapshot` — the Zod schema is canonical (co-located in `schema_introspect.ts`; the cross-impl `_testing_schema_snapshot` RPC action reuses it as its wire validator, and the type is `z.infer`'d from it). Fully JSON-serializable; every collection deterministically sorted on capture so structural equality is stable across runs.
|
|
214
214
|
|
|
215
215
|
### `schema_parity.ts` — `assert_schema_snapshots_equal`
|
|
216
216
|
|
|
217
217
|
- `diff_schema_snapshots(a, b)` — structured `Array<SchemaDiff>` between two snapshots; empty array means parity holds.
|
|
218
218
|
- `format_schema_diffs(diffs, labels?)` — human-readable multi-line rendering; labels name the impl on each side (e.g., `{a: 'deno', b: 'rust'}`).
|
|
219
219
|
- `assert_schema_snapshots_equal(a, b, labels?)` — throws on drift with a fully-formatted diff message.
|
|
220
|
-
- `SchemaDiff` — tagged-union per drift kind: `
|
|
220
|
+
- `SchemaDiff` — tagged-union per drift kind: `table_only_in`, `column_only_in`, `column_field_differs`, `index_only_in`, `index_definition_differs`, `constraint_only_in`, `constraint_differs`, `sequence_only_in`, `sequence_data_type_differs`.
|
|
221
221
|
|
|
222
222
|
**Cross-impl gate pattern** — a dual-impl consumer running two backends
|
|
223
223
|
(a TS Hono server and a Rust spine server) against a shared schema, plus
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Vitest `globalSetup` factory for cross-impl **schema-parity** projects.
|
|
4
|
+
*
|
|
5
|
+
* The per-backend `create_cross_backend_global_setup` spawns one backend
|
|
6
|
+
* (derived from the project name). A schema-parity gate instead needs
|
|
7
|
+
* *two* backends alive at once so the test process can capture each one's
|
|
8
|
+
* schema (via `capture_schema_snapshot`) and diff them with
|
|
9
|
+
* `assert_schema_snapshots_equal`. This factory spawns + bootstraps both
|
|
10
|
+
* configs and `provide`s both serialized handles.
|
|
11
|
+
*
|
|
12
|
+
* A consumer's parity `global_setup.ts` collapses to:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import {create_schema_parity_global_setup} from
|
|
16
|
+
* '@fuzdev/fuz_app/testing/cross_backend/create_schema_parity_global_setup.js';
|
|
17
|
+
* import {deno_backend_config, rust_backend_config} from './my_backend_config.js';
|
|
18
|
+
* import './cross_test_types.js'; // augments the two provide keys
|
|
19
|
+
*
|
|
20
|
+
* export default create_schema_parity_global_setup({
|
|
21
|
+
* configs: {a: deno_backend_config, b: rust_backend_config},
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* The parity `.cross.test.ts` `inject`s both keys, rebuilds each with
|
|
26
|
+
* `reconstruct_bootstrapped_handle`, and asserts. Run this project in a
|
|
27
|
+
* later `groupOrder` than the single-backend projects (or with distinct
|
|
28
|
+
* ports) — it reuses both configs' ports, so it must not run concurrently
|
|
29
|
+
* with the per-backend projects.
|
|
30
|
+
*
|
|
31
|
+
* @module
|
|
32
|
+
*/
|
|
33
|
+
import type { TestProject } from 'vitest/node';
|
|
34
|
+
import type { BackendConfig } from './backend_config.js';
|
|
35
|
+
export interface SchemaParityGlobalSetupOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The two backend config factories to spawn. `a` is spawned first; on
|
|
38
|
+
* its success `b` is spawned (with `a` torn down if `b` throws). Label
|
|
39
|
+
* them in the parity test via `assert_schema_snapshots_equal`'s labels.
|
|
40
|
+
*/
|
|
41
|
+
readonly configs: {
|
|
42
|
+
readonly a: () => BackendConfig;
|
|
43
|
+
readonly b: () => BackendConfig;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* `project.provide` keys for the two serialized handles (read by
|
|
47
|
+
* `inject` in the parity test). Default `parity_handle_a` /
|
|
48
|
+
* `parity_handle_b`. Augment vitest's `ProvidedContext` for them.
|
|
49
|
+
*/
|
|
50
|
+
readonly provide_keys?: {
|
|
51
|
+
readonly a: string;
|
|
52
|
+
readonly b: string;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Build a vitest `globalSetup` default export that spawns both backends.
|
|
57
|
+
*/
|
|
58
|
+
export declare const create_schema_parity_global_setup: ({ configs, provide_keys, }: SchemaParityGlobalSetupOptions) => ((project: TestProject) => Promise<() => Promise<void>>);
|
|
59
|
+
//# sourceMappingURL=create_schema_parity_global_setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_schema_parity_global_setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/create_schema_parity_global_setup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAIvD,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,aAAa,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,aAAa,CAAA;KAAC,CAAC;IACrF;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;CACjE;AAED;;GAEG;AACH,eAAO,MAAM,iCAAiC,GAAI,4BAG/C,8BAA8B,KAAG,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAmB1F,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { bootstrap_backend } from './bootstrap_backend.js';
|
|
3
|
+
import { serialize_bootstrapped_handle } from './setup.js';
|
|
4
|
+
/**
|
|
5
|
+
* Build a vitest `globalSetup` default export that spawns both backends.
|
|
6
|
+
*/
|
|
7
|
+
export const create_schema_parity_global_setup = ({ configs, provide_keys = { a: 'parity_handle_a', b: 'parity_handle_b' }, }) => {
|
|
8
|
+
return async (project) => {
|
|
9
|
+
const a = await bootstrap_backend(configs.a());
|
|
10
|
+
// Tear `a` down if `b` fails to spawn — no orphaned child process.
|
|
11
|
+
let b;
|
|
12
|
+
try {
|
|
13
|
+
b = await bootstrap_backend(configs.b());
|
|
14
|
+
}
|
|
15
|
+
catch (err) {
|
|
16
|
+
await a.teardown();
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
const provide = project.provide;
|
|
20
|
+
provide(provide_keys.a, serialize_bootstrapped_handle(a));
|
|
21
|
+
provide(provide_keys.b, serialize_bootstrapped_handle(b));
|
|
22
|
+
return async () => {
|
|
23
|
+
await b.teardown();
|
|
24
|
+
await a.teardown();
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -8,6 +8,7 @@ import { type RpcEndpointsSuiteOption } from '../rpc_helpers.js';
|
|
|
8
8
|
import { type BackendCapabilities } from './capabilities.js';
|
|
9
9
|
import type { AppSurfaceSpec } from '../../http/surface.js';
|
|
10
10
|
import { type FetchTransport } from '../transports/fetch_transport.js';
|
|
11
|
+
import type { SchemaSnapshot } from '../schema_introspect.js';
|
|
11
12
|
import type { BackendHandle } from './spawn_backend.js';
|
|
12
13
|
/**
|
|
13
14
|
* Options for `TestFixture.create_account` — mints an additional
|
|
@@ -321,6 +322,21 @@ export interface CrossProcessSetupOptions {
|
|
|
321
322
|
*/
|
|
322
323
|
readonly extra_accounts?: ReadonlyArray<ExtraAccountSpec>;
|
|
323
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Capture a backend's schema snapshot over the `_testing_schema_snapshot`
|
|
327
|
+
* RPC action (keeper daemon-token channel). The canonical way for a
|
|
328
|
+
* cross-impl parity gate to read each backend's live schema — pair two
|
|
329
|
+
* calls with `assert_schema_snapshots_equal` (`testing/schema_parity.js`).
|
|
330
|
+
*
|
|
331
|
+
* `exclude_tables` drops documented divergences from both sides before
|
|
332
|
+
* comparison (e.g. a cell-primary Rust backend lacks tables the TS schema
|
|
333
|
+
* has). Each impl answers from its own introspection — TS via
|
|
334
|
+
* `query_schema_snapshot`, Rust via `fuz_db::query_schema_snapshot` — and
|
|
335
|
+
* the snapshot shapes match by design.
|
|
336
|
+
*/
|
|
337
|
+
export declare const capture_schema_snapshot: (handle: ReconstructedBootstrappedBackendHandle, options?: {
|
|
338
|
+
exclude_tables?: ReadonlyArray<string>;
|
|
339
|
+
}) => Promise<SchemaSnapshot>;
|
|
324
340
|
/**
|
|
325
341
|
* Build a `SetupTest` against a spawned + bootstrapped backend.
|
|
326
342
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -233,6 +233,22 @@ const rpc_via_transport = async (transport, rpc_path, method, params, backend_na
|
|
|
233
233
|
}
|
|
234
234
|
return raw.result;
|
|
235
235
|
};
|
|
236
|
+
/**
|
|
237
|
+
* Capture a backend's schema snapshot over the `_testing_schema_snapshot`
|
|
238
|
+
* RPC action (keeper daemon-token channel). The canonical way for a
|
|
239
|
+
* cross-impl parity gate to read each backend's live schema — pair two
|
|
240
|
+
* calls with `assert_schema_snapshots_equal` (`testing/schema_parity.js`).
|
|
241
|
+
*
|
|
242
|
+
* `exclude_tables` drops documented divergences from both sides before
|
|
243
|
+
* comparison (e.g. a cell-primary Rust backend lacks tables the TS schema
|
|
244
|
+
* has). Each impl answers from its own introspection — TS via
|
|
245
|
+
* `query_schema_snapshot`, Rust via `fuz_db::query_schema_snapshot` — and
|
|
246
|
+
* the snapshot shapes match by design.
|
|
247
|
+
*/
|
|
248
|
+
export const capture_schema_snapshot = async (handle, options = {}) => {
|
|
249
|
+
const raw = await rpc_via_transport(handle.keeper_transport, handle.config.rpc_path, '_testing_schema_snapshot', { exclude_tables: [...(options.exclude_tables ?? [])] }, handle.config.name, { [DAEMON_TOKEN_HEADER]: handle.daemon_token });
|
|
250
|
+
return raw;
|
|
251
|
+
};
|
|
236
252
|
/**
|
|
237
253
|
* Backdating offset (seconds) the `mint_expired_session` seam passes to
|
|
238
254
|
* `mint_test_session` / `_testing_mint_session`. A minute in the past is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAOvD,uGAAuG;AACvG,eAAO,MAAM,kBAAkB,+BAA+B,CAAC;AAE/D,kGAAkG;AAClG,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,0FAA0F;AAC1F,eAAO,MAAM,+BAA+B,iDAAiD,CAAC;AAE9F,MAAM,WAAW,6BAA6B;IAC7C,yDAAyD;IACzD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GACrC,UAAS,6BAAkC,KACzC,aAsCF,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
|
-
import { make_default_rust_backend_config } from './default_backend_configs.js';
|
|
3
|
+
import { make_default_rust_backend_config, rust_default_capabilities, } from './default_backend_configs.js';
|
|
4
4
|
/** Env var naming the prebuilt `testing_spine_stub` binary. Required when `binary_path` is omitted. */
|
|
5
5
|
export const SPINE_STUB_BIN_ENV = 'FUZ_TESTING_SPINE_STUB_BIN';
|
|
6
6
|
/** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
|
|
@@ -35,6 +35,10 @@ export const spine_stub_backend_config = (options = {}) => {
|
|
|
35
35
|
// is the lower-precedence fallback — both carry the same value.
|
|
36
36
|
start_command: [binary_path, '--port', String(port)],
|
|
37
37
|
database_url,
|
|
38
|
+
// The stub now serves `GET /api/admin/audit/stream` (the spine
|
|
39
|
+
// `fuz_realtime::SseRegistry` + audit listener), so it advertises `sse`
|
|
40
|
+
// like the TS spines — the cross-process SSE suite's three cases run.
|
|
41
|
+
capabilities: { ...rust_default_capabilities, sse: true },
|
|
38
42
|
port_env_var: 'FUZ_SPINE_STUB_PORT',
|
|
39
43
|
rust_log: 'info,testing_spine_stub=info',
|
|
40
44
|
paths,
|
|
@@ -2,11 +2,12 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Test-binary RPC actions for cross-process integration tests.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Four daemon-token-authed actions, bundled by `create_testing_actions`:
|
|
6
6
|
* **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
|
|
7
|
-
* (audit barrier),
|
|
7
|
+
* (audit barrier), **`_testing_mint_session`** (forge an
|
|
8
8
|
* expired-by-construction server-side session for the expiry conformance
|
|
9
|
-
* cases)
|
|
9
|
+
* cases), and **`_testing_schema_snapshot`** (introspect the live schema for
|
|
10
|
+
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot).
|
|
10
11
|
*
|
|
11
12
|
* `_testing_reset` — full DB wipe + keeper re-seed + optional
|
|
12
13
|
* secondary-account seeding. The
|
|
@@ -213,6 +214,61 @@ export declare const testing_mint_session_action_spec: {
|
|
|
213
214
|
* `create_testing_actions`; in-process suites mount it directly).
|
|
214
215
|
*/
|
|
215
216
|
export declare const create_testing_drain_effects_action: () => RpcAction;
|
|
217
|
+
/**
|
|
218
|
+
* `_testing_schema_snapshot` — introspect the live database into a normalized
|
|
219
|
+
* `SchemaSnapshot` for cross-impl parity diffing. The cross-backend harness
|
|
220
|
+
* calls this on each backend, then `assert_schema_snapshots_equal`s the
|
|
221
|
+
* results (a Rust backend answers from `fuz_db::query_schema_snapshot`; the
|
|
222
|
+
* shapes match by design). Optional `exclude_tables` drops documented
|
|
223
|
+
* divergences from both sides before comparison.
|
|
224
|
+
*
|
|
225
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
226
|
+
*/
|
|
227
|
+
export declare const testing_schema_snapshot_action_spec: {
|
|
228
|
+
readonly method: "_testing_schema_snapshot";
|
|
229
|
+
readonly kind: "request_response";
|
|
230
|
+
readonly initiator: "frontend";
|
|
231
|
+
readonly auth: {
|
|
232
|
+
readonly account: "required";
|
|
233
|
+
readonly actor: "none";
|
|
234
|
+
readonly credential_types: readonly ["daemon_token"];
|
|
235
|
+
};
|
|
236
|
+
readonly side_effects: false;
|
|
237
|
+
readonly input: z.ZodObject<{
|
|
238
|
+
exclude_tables: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
239
|
+
}, z.core.$strict>;
|
|
240
|
+
readonly output: z.ZodObject<{
|
|
241
|
+
tables: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
242
|
+
columns: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
243
|
+
data_type: z.ZodString;
|
|
244
|
+
udt_name: z.ZodString;
|
|
245
|
+
is_nullable: z.ZodBoolean;
|
|
246
|
+
column_default: z.ZodNullable<z.ZodString>;
|
|
247
|
+
is_identity: z.ZodBoolean;
|
|
248
|
+
}, z.core.$strip>>;
|
|
249
|
+
indexes: z.ZodArray<z.ZodObject<{
|
|
250
|
+
name: z.ZodString;
|
|
251
|
+
definition: z.ZodString;
|
|
252
|
+
}, z.core.$strip>>;
|
|
253
|
+
constraints: z.ZodArray<z.ZodObject<{
|
|
254
|
+
name: z.ZodString;
|
|
255
|
+
type: z.ZodString;
|
|
256
|
+
definition: z.ZodString;
|
|
257
|
+
}, z.core.$strip>>;
|
|
258
|
+
}, z.core.$strip>>;
|
|
259
|
+
sequences: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
260
|
+
data_type: z.ZodString;
|
|
261
|
+
}, z.core.$strip>>;
|
|
262
|
+
}, z.core.$strip>;
|
|
263
|
+
readonly async: true;
|
|
264
|
+
readonly description: "Test-binary only — introspect the live schema into a normalized snapshot for cross-impl parity diffing.";
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Build the standalone `_testing_schema_snapshot` action. No deps —
|
|
268
|
+
* introspects `ctx.db` via `query_schema_snapshot`. Bundled by
|
|
269
|
+
* `create_testing_actions`; mount directly for in-process use.
|
|
270
|
+
*/
|
|
271
|
+
export declare const create_testing_schema_snapshot_action: () => RpcAction;
|
|
216
272
|
/** Options for `create_testing_actions`. */
|
|
217
273
|
export interface CreateTestingActionsOptions {
|
|
218
274
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B
|
|
1
|
+
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,gBAAgB,CAAC;AAkBvC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwBQ,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;CAWA,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;CAeC,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,QAAO,SACiB,CAAC;AAEzE;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWF,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,QAAO,SAGvD,CAAC;AAEH,4CAA4C;AAC5C,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,2BAA2B,KAClC,KAAK,CAAC,SAAS,CA2GjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
|
|
@@ -2,11 +2,12 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Test-binary RPC actions for cross-process integration tests.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Four daemon-token-authed actions, bundled by `create_testing_actions`:
|
|
6
6
|
* **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
|
|
7
|
-
* (audit barrier),
|
|
7
|
+
* (audit barrier), **`_testing_mint_session`** (forge an
|
|
8
8
|
* expired-by-construction server-side session for the expiry conformance
|
|
9
|
-
* cases)
|
|
9
|
+
* cases), and **`_testing_schema_snapshot`** (introspect the live schema for
|
|
10
|
+
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot).
|
|
10
11
|
*
|
|
11
12
|
* `_testing_reset` — full DB wipe + keeper re-seed + optional
|
|
12
13
|
* secondary-account seeding. The
|
|
@@ -62,6 +63,7 @@ import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
|
62
63
|
import { rpc_action } from '../../actions/action_rpc.js';
|
|
63
64
|
import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
|
|
64
65
|
import { auth_integration_truncate_tables } from '../db.js';
|
|
66
|
+
import { query_schema_snapshot, SchemaSnapshot } from '../schema_introspect.js';
|
|
65
67
|
import { create_test_account_with_credentials, mint_test_session, DEFAULT_TEST_PASSWORD, } from '../app_server.js';
|
|
66
68
|
/** Output shape for an individual seeded account (keeper or extra). */
|
|
67
69
|
const SeededAccountShape = z.strictObject({
|
|
@@ -188,6 +190,33 @@ export const testing_mint_session_action_spec = {
|
|
|
188
190
|
* `create_testing_actions`; in-process suites mount it directly).
|
|
189
191
|
*/
|
|
190
192
|
export const create_testing_drain_effects_action = () => rpc_action(testing_drain_effects_action_spec, async () => ({ ok: true }));
|
|
193
|
+
/**
|
|
194
|
+
* `_testing_schema_snapshot` — introspect the live database into a normalized
|
|
195
|
+
* `SchemaSnapshot` for cross-impl parity diffing. The cross-backend harness
|
|
196
|
+
* calls this on each backend, then `assert_schema_snapshots_equal`s the
|
|
197
|
+
* results (a Rust backend answers from `fuz_db::query_schema_snapshot`; the
|
|
198
|
+
* shapes match by design). Optional `exclude_tables` drops documented
|
|
199
|
+
* divergences from both sides before comparison.
|
|
200
|
+
*
|
|
201
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
202
|
+
*/
|
|
203
|
+
export const testing_schema_snapshot_action_spec = {
|
|
204
|
+
method: '_testing_schema_snapshot',
|
|
205
|
+
kind: 'request_response',
|
|
206
|
+
initiator: 'frontend',
|
|
207
|
+
auth: { account: 'required', actor: 'none', credential_types: ['daemon_token'] },
|
|
208
|
+
side_effects: false,
|
|
209
|
+
input: z.strictObject({ exclude_tables: z.array(z.string()).optional() }),
|
|
210
|
+
output: SchemaSnapshot,
|
|
211
|
+
async: true,
|
|
212
|
+
description: 'Test-binary only — introspect the live schema into a normalized snapshot for cross-impl parity diffing.',
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Build the standalone `_testing_schema_snapshot` action. No deps —
|
|
216
|
+
* introspects `ctx.db` via `query_schema_snapshot`. Bundled by
|
|
217
|
+
* `create_testing_actions`; mount directly for in-process use.
|
|
218
|
+
*/
|
|
219
|
+
export const create_testing_schema_snapshot_action = () => rpc_action(testing_schema_snapshot_action_spec, async (input, ctx) => query_schema_snapshot(ctx.db, { exclude_tables: input.exclude_tables }));
|
|
191
220
|
/**
|
|
192
221
|
* Build the testing RPC actions for a test binary's registry.
|
|
193
222
|
*
|
|
@@ -298,6 +327,7 @@ export const create_testing_actions = (deps, options) => {
|
|
|
298
327
|
return { session_cookie };
|
|
299
328
|
}),
|
|
300
329
|
create_testing_drain_effects_action(),
|
|
330
|
+
create_testing_schema_snapshot_action(),
|
|
301
331
|
];
|
|
302
332
|
};
|
|
303
333
|
/** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
|
|
@@ -5,8 +5,6 @@ import './assert_dev_env.js';
|
|
|
5
5
|
*
|
|
6
6
|
* The snapshot covers:
|
|
7
7
|
*
|
|
8
|
-
* - `schema_version` rows (`namespace`, `name`, `sequence`) — captures
|
|
9
|
-
* migration set state across impls
|
|
10
8
|
* - Tables with columns (data type, nullability, default, identity)
|
|
11
9
|
* - Indexes with canonical Postgres-rendered definitions
|
|
12
10
|
* - Constraints (CHECK, FOREIGN KEY, PRIMARY KEY, UNIQUE, EXCLUSION)
|
|
@@ -21,60 +19,80 @@ import './assert_dev_env.js';
|
|
|
21
19
|
*
|
|
22
20
|
* @module
|
|
23
21
|
*/
|
|
22
|
+
import { z } from 'zod';
|
|
24
23
|
import type { Db } from '../db/db.js';
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
24
|
+
/**
|
|
25
|
+
* Per-column structural metadata. The Zod schema is the canonical source
|
|
26
|
+
* for the column shape — `SchemaSnapshot` reuses it as the cross-impl
|
|
27
|
+
* `_testing_schema_snapshot` RPC action's wire validator, so the
|
|
28
|
+
* introspection type and the wire contract can't drift apart.
|
|
29
|
+
*/
|
|
30
|
+
export declare const ColumnSnapshot: z.ZodObject<{
|
|
31
|
+
data_type: z.ZodString;
|
|
32
|
+
udt_name: z.ZodString;
|
|
33
|
+
is_nullable: z.ZodBoolean;
|
|
34
|
+
column_default: z.ZodNullable<z.ZodString>;
|
|
35
|
+
is_identity: z.ZodBoolean;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
export type ColumnSnapshot = z.infer<typeof ColumnSnapshot>;
|
|
38
38
|
/** Per-table structural metadata. */
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
export declare const TableSnapshot: z.ZodObject<{
|
|
40
|
+
columns: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
41
|
+
data_type: z.ZodString;
|
|
42
|
+
udt_name: z.ZodString;
|
|
43
|
+
is_nullable: z.ZodBoolean;
|
|
44
|
+
column_default: z.ZodNullable<z.ZodString>;
|
|
45
|
+
is_identity: z.ZodBoolean;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
indexes: z.ZodArray<z.ZodObject<{
|
|
48
|
+
name: z.ZodString;
|
|
49
|
+
definition: z.ZodString;
|
|
50
|
+
}, z.core.$strip>>;
|
|
51
|
+
constraints: z.ZodArray<z.ZodObject<{
|
|
52
|
+
name: z.ZodString;
|
|
53
|
+
type: z.ZodString;
|
|
54
|
+
definition: z.ZodString;
|
|
55
|
+
}, z.core.$strip>>;
|
|
56
|
+
}, z.core.$strip>;
|
|
57
|
+
export type TableSnapshot = z.infer<typeof TableSnapshot>;
|
|
54
58
|
/** Sequence metadata — `data_type` is `bigint` (BIGSERIAL) or `integer` (SERIAL). */
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface SchemaVersionRow {
|
|
60
|
-
readonly namespace: string;
|
|
61
|
-
readonly name: string;
|
|
62
|
-
readonly sequence: number;
|
|
63
|
-
}
|
|
59
|
+
export declare const SequenceSnapshot: z.ZodObject<{
|
|
60
|
+
data_type: z.ZodString;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
export type SequenceSnapshot = z.infer<typeof SequenceSnapshot>;
|
|
64
63
|
/**
|
|
65
|
-
* Normalized database schema snapshot for parity comparison
|
|
64
|
+
* Normalized database schema snapshot for parity comparison — the single
|
|
65
|
+
* source of truth for the snapshot shape across the introspection query
|
|
66
|
+
* (`query_schema_snapshot`), the diff comparator (`schema_parity.ts`), and
|
|
67
|
+
* the cross-impl RPC action's wire validator (`testing_reset_actions.ts`).
|
|
66
68
|
*
|
|
67
69
|
* All fields are deterministically ordered on capture so structural equality
|
|
68
70
|
* via `JSON.stringify` or per-key comparison yields stable results.
|
|
69
71
|
*/
|
|
70
|
-
export
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
export declare const SchemaSnapshot: z.ZodObject<{
|
|
73
|
+
tables: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
74
|
+
columns: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
75
|
+
data_type: z.ZodString;
|
|
76
|
+
udt_name: z.ZodString;
|
|
77
|
+
is_nullable: z.ZodBoolean;
|
|
78
|
+
column_default: z.ZodNullable<z.ZodString>;
|
|
79
|
+
is_identity: z.ZodBoolean;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
indexes: z.ZodArray<z.ZodObject<{
|
|
82
|
+
name: z.ZodString;
|
|
83
|
+
definition: z.ZodString;
|
|
84
|
+
}, z.core.$strip>>;
|
|
85
|
+
constraints: z.ZodArray<z.ZodObject<{
|
|
86
|
+
name: z.ZodString;
|
|
87
|
+
type: z.ZodString;
|
|
88
|
+
definition: z.ZodString;
|
|
89
|
+
}, z.core.$strip>>;
|
|
90
|
+
}, z.core.$strip>>;
|
|
91
|
+
sequences: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
92
|
+
data_type: z.ZodString;
|
|
93
|
+
}, z.core.$strip>>;
|
|
94
|
+
}, z.core.$strip>;
|
|
95
|
+
export type SchemaSnapshot = z.infer<typeof SchemaSnapshot>;
|
|
78
96
|
/** Filter options for `query_schema_snapshot`. */
|
|
79
97
|
export interface QuerySchemaSnapshotOptions {
|
|
80
98
|
/**
|
|
@@ -83,8 +101,10 @@ export interface QuerySchemaSnapshotOptions {
|
|
|
83
101
|
*/
|
|
84
102
|
readonly schema?: string;
|
|
85
103
|
/**
|
|
86
|
-
* Tables to exclude from the snapshot. The `schema_version`
|
|
87
|
-
* is always excluded
|
|
104
|
+
* Tables to exclude from the snapshot. The `schema_version` migration
|
|
105
|
+
* tracker is always excluded — it's framework bookkeeping created by the
|
|
106
|
+
* migration runner, identical across impls, and not part of any
|
|
107
|
+
* consumer's domain schema.
|
|
88
108
|
*/
|
|
89
109
|
readonly exclude_tables?: ReadonlyArray<string>;
|
|
90
110
|
}
|
|
@@ -92,15 +112,11 @@ export interface QuerySchemaSnapshotOptions {
|
|
|
92
112
|
* Introspect a live database into a deterministic `SchemaSnapshot`.
|
|
93
113
|
*
|
|
94
114
|
* Reads `information_schema` and `pg_catalog` to capture tables, columns,
|
|
95
|
-
* indexes, constraints,
|
|
96
|
-
* rows. The `applied_at` timestamp is deliberately excluded — only the set
|
|
97
|
-
* of applied migrations matters for parity.
|
|
98
|
-
*
|
|
99
|
-
* The `schema_version` table itself never appears in the `tables` field;
|
|
100
|
-
* its structure is identical across consumers and would only add noise.
|
|
115
|
+
* indexes, constraints, and sequences.
|
|
101
116
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
117
|
+
* The `schema_version` migration tracker never appears in the `tables`
|
|
118
|
+
* field — it's framework bookkeeping created by the migration runner,
|
|
119
|
+
* identical across consumers, and would only add noise.
|
|
104
120
|
*/
|
|
105
121
|
export declare const query_schema_snapshot: (db: Db, options?: QuerySchemaSnapshotOptions) => Promise<SchemaSnapshot>;
|
|
106
122
|
//# sourceMappingURL=schema_introspect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_introspect.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_introspect.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B
|
|
1
|
+
{"version":3,"file":"schema_introspect.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_introspect.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;iBAWzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,qCAAqC;AACrC,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;iBAOxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,gBAAgB;;iBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;iBAKzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,kDAAkD;AAClD,MAAM,WAAW,0BAA0B;IAC1C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChD;AAyDD;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,IAAI,EAAE,EACN,UAAS,0BAA+B,KACtC,OAAO,CAAC,cAAc,CAuGxB,CAAC"}
|
|
@@ -1,4 +1,71 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* PostgreSQL schema introspection — produces a normalized, JSON-serializable
|
|
4
|
+
* snapshot of a database's structure for cross-impl parity checks.
|
|
5
|
+
*
|
|
6
|
+
* The snapshot covers:
|
|
7
|
+
*
|
|
8
|
+
* - Tables with columns (data type, nullability, default, identity)
|
|
9
|
+
* - Indexes with canonical Postgres-rendered definitions
|
|
10
|
+
* - Constraints (CHECK, FOREIGN KEY, PRIMARY KEY, UNIQUE, EXCLUSION)
|
|
11
|
+
* - Sequences with data type — distinguishes `int4` (SERIAL) from `int8`
|
|
12
|
+
* (BIGSERIAL)
|
|
13
|
+
*
|
|
14
|
+
* Designed for `pg_catalog` introspection — works against both PostgreSQL
|
|
15
|
+
* and PGlite. The snapshot is fully deterministic: every collection sorts by
|
|
16
|
+
* a stable key and excludes time-varying fields like `applied_at`.
|
|
17
|
+
*
|
|
18
|
+
* Paired with `schema_parity.ts` for comparison + assertion helpers.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
/**
|
|
24
|
+
* Per-column structural metadata. The Zod schema is the canonical source
|
|
25
|
+
* for the column shape — `SchemaSnapshot` reuses it as the cross-impl
|
|
26
|
+
* `_testing_schema_snapshot` RPC action's wire validator, so the
|
|
27
|
+
* introspection type and the wire contract can't drift apart.
|
|
28
|
+
*/
|
|
29
|
+
export const ColumnSnapshot = z.object({
|
|
30
|
+
/** SQL standard type name from `information_schema.columns.data_type`. */
|
|
31
|
+
data_type: z.string(),
|
|
32
|
+
/** Postgres-native type name from `information_schema.columns.udt_name`. */
|
|
33
|
+
udt_name: z.string(),
|
|
34
|
+
/** `true` when the column accepts NULL. */
|
|
35
|
+
is_nullable: z.boolean(),
|
|
36
|
+
/** Default-value expression as Postgres reports it, or `null` if none. */
|
|
37
|
+
column_default: z.string().nullable(),
|
|
38
|
+
/** `true` when the column was declared GENERATED ... AS IDENTITY. */
|
|
39
|
+
is_identity: z.boolean(),
|
|
40
|
+
});
|
|
41
|
+
/** Per-table structural metadata. */
|
|
42
|
+
export const TableSnapshot = z.object({
|
|
43
|
+
/** Column metadata keyed by column name (sorted on serialization). */
|
|
44
|
+
columns: z.record(z.string(), ColumnSnapshot),
|
|
45
|
+
/** Index definitions as Postgres renders them via `pg_indexes.indexdef`. */
|
|
46
|
+
indexes: z.array(z.object({ name: z.string(), definition: z.string() })),
|
|
47
|
+
/** Constraint definitions as Postgres renders them via `pg_get_constraintdef`. */
|
|
48
|
+
constraints: z.array(z.object({ name: z.string(), type: z.string(), definition: z.string() })),
|
|
49
|
+
});
|
|
50
|
+
/** Sequence metadata — `data_type` is `bigint` (BIGSERIAL) or `integer` (SERIAL). */
|
|
51
|
+
export const SequenceSnapshot = z.object({
|
|
52
|
+
data_type: z.string(),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Normalized database schema snapshot for parity comparison — the single
|
|
56
|
+
* source of truth for the snapshot shape across the introspection query
|
|
57
|
+
* (`query_schema_snapshot`), the diff comparator (`schema_parity.ts`), and
|
|
58
|
+
* the cross-impl RPC action's wire validator (`testing_reset_actions.ts`).
|
|
59
|
+
*
|
|
60
|
+
* All fields are deterministically ordered on capture so structural equality
|
|
61
|
+
* via `JSON.stringify` or per-key comparison yields stable results.
|
|
62
|
+
*/
|
|
63
|
+
export const SchemaSnapshot = z.object({
|
|
64
|
+
/** Tables keyed by name. */
|
|
65
|
+
tables: z.record(z.string(), TableSnapshot),
|
|
66
|
+
/** Sequences keyed by name. */
|
|
67
|
+
sequences: z.record(z.string(), SequenceSnapshot),
|
|
68
|
+
});
|
|
2
69
|
const sort_keys = (record) => {
|
|
3
70
|
const sorted = {};
|
|
4
71
|
for (const key of Object.keys(record).sort()) {
|
|
@@ -28,26 +95,16 @@ const contype_to_kind = (contype) => {
|
|
|
28
95
|
* Introspect a live database into a deterministic `SchemaSnapshot`.
|
|
29
96
|
*
|
|
30
97
|
* Reads `information_schema` and `pg_catalog` to capture tables, columns,
|
|
31
|
-
* indexes, constraints,
|
|
32
|
-
* rows. The `applied_at` timestamp is deliberately excluded — only the set
|
|
33
|
-
* of applied migrations matters for parity.
|
|
34
|
-
*
|
|
35
|
-
* The `schema_version` table itself never appears in the `tables` field;
|
|
36
|
-
* its structure is identical across consumers and would only add noise.
|
|
98
|
+
* indexes, constraints, and sequences.
|
|
37
99
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
100
|
+
* The `schema_version` migration tracker never appears in the `tables`
|
|
101
|
+
* field — it's framework bookkeeping created by the migration runner,
|
|
102
|
+
* identical across consumers, and would only add noise.
|
|
40
103
|
*/
|
|
41
104
|
export const query_schema_snapshot = async (db, options = {}) => {
|
|
42
105
|
const schema = options.schema ?? 'public';
|
|
43
106
|
const exclude_tables = new Set(options.exclude_tables ?? []);
|
|
44
107
|
exclude_tables.add('schema_version');
|
|
45
|
-
// schema_version rows — the migration tracker. Exclude `applied_at`
|
|
46
|
-
// because timestamps differ across bootstraps even when the migration
|
|
47
|
-
// set is identical.
|
|
48
|
-
const schema_version_rows = await db.query(`SELECT namespace, name, sequence
|
|
49
|
-
FROM schema_version
|
|
50
|
-
ORDER BY namespace ASC, sequence ASC`);
|
|
51
108
|
// All tables in the target schema, minus the excludes.
|
|
52
109
|
const table_rows = await db.query(`SELECT table_name
|
|
53
110
|
FROM information_schema.tables
|
|
@@ -68,6 +125,11 @@ export const query_schema_snapshot = async (db, options = {}) => {
|
|
|
68
125
|
WHERE schemaname = $1
|
|
69
126
|
ORDER BY tablename ASC, indexname ASC`, [schema]);
|
|
70
127
|
// Constraints — pg_get_constraintdef produces a canonical text rendering.
|
|
128
|
+
// Skip NOT NULL constraints (`contype = 'n'`): PG17+ catalogs them as
|
|
129
|
+
// named `pg_constraint` rows while PGlite / older PG don't, and
|
|
130
|
+
// nullability is already captured per-column by `is_nullable` — including
|
|
131
|
+
// them would report a pure engine-cataloging artifact as cross-backend
|
|
132
|
+
// drift between a PGlite and a real-Postgres backend.
|
|
71
133
|
const constraint_rows = await db.query(`SELECT c.conrelid::regclass::text AS table_name,
|
|
72
134
|
c.conname,
|
|
73
135
|
c.contype::text,
|
|
@@ -76,6 +138,7 @@ export const query_schema_snapshot = async (db, options = {}) => {
|
|
|
76
138
|
JOIN pg_namespace n ON n.oid = c.connamespace
|
|
77
139
|
WHERE n.nspname = $1
|
|
78
140
|
AND c.conrelid != 0
|
|
141
|
+
AND c.contype != 'n'
|
|
79
142
|
ORDER BY table_name ASC, conname ASC`, [schema]);
|
|
80
143
|
// Sequences — data_type distinguishes bigint (BIGSERIAL) from integer (SERIAL).
|
|
81
144
|
const sequence_rows = await db.query(`SELECT sequence_name, data_type
|
|
@@ -116,7 +179,6 @@ export const query_schema_snapshot = async (db, options = {}) => {
|
|
|
116
179
|
sequences[row.sequence_name] = { data_type: row.data_type };
|
|
117
180
|
}
|
|
118
181
|
return {
|
|
119
|
-
schema_version: schema_version_rows,
|
|
120
182
|
tables: sort_keys(tables),
|
|
121
183
|
sequences: sort_keys(sequences),
|
|
122
184
|
};
|
|
@@ -6,7 +6,7 @@ import './assert_dev_env.js';
|
|
|
6
6
|
* Two live impls (TS fuz_app vs Rust spine) are each other's parity
|
|
7
7
|
* reference. After both bootstrap, snapshot each, diff, fail loudly on
|
|
8
8
|
* drift. The diff entries name the specific divergence (column type,
|
|
9
|
-
* missing index,
|
|
9
|
+
* missing index, constraint absent on one side) so the error
|
|
10
10
|
* message points at the source.
|
|
11
11
|
*
|
|
12
12
|
* Consumer pattern (in zzz's integration runner or fuz_app's own
|
|
@@ -29,8 +29,8 @@ import './assert_dev_env.js';
|
|
|
29
29
|
* impls with the same columns in different declaration order compare
|
|
30
30
|
* equal (functional parity is preserved; `SELECT *` ordering is not)
|
|
31
31
|
* - `COMMENT ON ...`
|
|
32
|
-
* - the `schema_version`
|
|
33
|
-
*
|
|
32
|
+
* - the `schema_version` migration tracker (always excluded — framework
|
|
33
|
+
* bookkeeping, not domain schema)
|
|
34
34
|
* - permissions / `GRANT`s
|
|
35
35
|
*
|
|
36
36
|
* None of these are used by the current fuz_app auth schema. Extend
|
|
@@ -40,19 +40,9 @@ import './assert_dev_env.js';
|
|
|
40
40
|
*
|
|
41
41
|
* @module
|
|
42
42
|
*/
|
|
43
|
-
import type { ColumnSnapshot, SchemaSnapshot
|
|
43
|
+
import type { ColumnSnapshot, SchemaSnapshot } from './schema_introspect.js';
|
|
44
44
|
/** Structured drift entry. `where` is the named source impl ('a' or 'b'). */
|
|
45
45
|
export type SchemaDiff = {
|
|
46
|
-
readonly kind: 'schema_version_only_in';
|
|
47
|
-
readonly where: 'a' | 'b';
|
|
48
|
-
readonly row: SchemaVersionRow;
|
|
49
|
-
} | {
|
|
50
|
-
readonly kind: 'schema_version_sequence_differs';
|
|
51
|
-
readonly namespace: string;
|
|
52
|
-
readonly name: string;
|
|
53
|
-
readonly a: number;
|
|
54
|
-
readonly b: number;
|
|
55
|
-
} | {
|
|
56
46
|
readonly kind: 'table_only_in';
|
|
57
47
|
readonly where: 'a' | 'b';
|
|
58
48
|
readonly table: string;
|
|
@@ -109,9 +99,9 @@ export type SchemaDiff = {
|
|
|
109
99
|
/**
|
|
110
100
|
* Structural diff between two snapshots — empty array means parity holds.
|
|
111
101
|
*
|
|
112
|
-
* Order of diffs is deterministic:
|
|
113
|
-
*
|
|
114
|
-
*
|
|
102
|
+
* Order of diffs is deterministic: tables in sorted order (with
|
|
103
|
+
* column/index/constraint sub-diffs grouped per table), then sequences.
|
|
104
|
+
* Consumers can rely on this for stable diff output.
|
|
115
105
|
*/
|
|
116
106
|
export declare const diff_schema_snapshots: (a: SchemaSnapshot, b: SchemaSnapshot) => Array<SchemaDiff>;
|
|
117
107
|
/** Labels used in formatted output — defaults to `'a'` and `'b'`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema_parity.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_parity.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EACX,cAAc,EACd,cAAc,
|
|
1
|
+
{"version":3,"file":"schema_parity.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/schema_parity.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,13 +2,12 @@ import './assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Structural diff between two snapshots — empty array means parity holds.
|
|
4
4
|
*
|
|
5
|
-
* Order of diffs is deterministic:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Order of diffs is deterministic: tables in sorted order (with
|
|
6
|
+
* column/index/constraint sub-diffs grouped per table), then sequences.
|
|
7
|
+
* Consumers can rely on this for stable diff output.
|
|
8
8
|
*/
|
|
9
9
|
export const diff_schema_snapshots = (a, b) => {
|
|
10
10
|
const diffs = [];
|
|
11
|
-
diff_schema_version(a.schema_version, b.schema_version, diffs);
|
|
12
11
|
const all_tables = new Set([...Object.keys(a.tables), ...Object.keys(b.tables)]);
|
|
13
12
|
for (const table of [...all_tables].sort()) {
|
|
14
13
|
const ta = a.tables[table];
|
|
@@ -39,33 +38,6 @@ export const diff_schema_snapshots = (a, b) => {
|
|
|
39
38
|
}
|
|
40
39
|
return diffs;
|
|
41
40
|
};
|
|
42
|
-
const diff_schema_version = (a, b, out) => {
|
|
43
|
-
const key = (r) => `${r.namespace}\x00${r.name}`;
|
|
44
|
-
const a_by_key = new Map(a.map((r) => [key(r), r]));
|
|
45
|
-
const b_by_key = new Map(b.map((r) => [key(r), r]));
|
|
46
|
-
const keys = new Set([...a_by_key.keys(), ...b_by_key.keys()]);
|
|
47
|
-
for (const k of [...keys].sort()) {
|
|
48
|
-
const row_a = a_by_key.get(k);
|
|
49
|
-
const row_b = b_by_key.get(k);
|
|
50
|
-
if (!row_a) {
|
|
51
|
-
out.push({ kind: 'schema_version_only_in', where: 'b', row: row_b });
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (!row_b) {
|
|
55
|
-
out.push({ kind: 'schema_version_only_in', where: 'a', row: row_a });
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (row_a.sequence !== row_b.sequence) {
|
|
59
|
-
out.push({
|
|
60
|
-
kind: 'schema_version_sequence_differs',
|
|
61
|
-
namespace: row_a.namespace,
|
|
62
|
-
name: row_a.name,
|
|
63
|
-
a: row_a.sequence,
|
|
64
|
-
b: row_b.sequence,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
41
|
const COLUMN_FIELDS = [
|
|
70
42
|
'data_type',
|
|
71
43
|
'udt_name',
|
|
@@ -165,12 +137,6 @@ export const format_schema_diffs = (diffs, labels = {}) => {
|
|
|
165
137
|
const lines = [];
|
|
166
138
|
for (const d of diffs) {
|
|
167
139
|
switch (d.kind) {
|
|
168
|
-
case 'schema_version_only_in':
|
|
169
|
-
lines.push(` schema_version row only in ${where_label(d.where)}: ${d.row.namespace}/${d.row.name} (sequence ${d.row.sequence})`);
|
|
170
|
-
break;
|
|
171
|
-
case 'schema_version_sequence_differs':
|
|
172
|
-
lines.push(` schema_version sequence differs for ${d.namespace}/${d.name}: ${label_a}=${d.a}, ${label_b}=${d.b}`);
|
|
173
|
-
break;
|
|
174
140
|
case 'table_only_in':
|
|
175
141
|
lines.push(` table ${d.table} only in ${where_label(d.where)}`);
|
|
176
142
|
break;
|