@fuzdev/fuz_app 0.69.0 → 0.71.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.
Files changed (42) hide show
  1. package/dist/db/CLAUDE.md +3 -3
  2. package/dist/db/cell_history_ddl.d.ts +1 -1
  3. package/dist/db/cell_history_ddl.js +1 -1
  4. package/dist/db/fact_ddl.d.ts +11 -11
  5. package/dist/db/fact_ddl.d.ts.map +1 -1
  6. package/dist/db/fact_ddl.js +13 -13
  7. package/dist/db/fact_queries.d.ts +9 -9
  8. package/dist/db/fact_queries.d.ts.map +1 -1
  9. package/dist/db/fact_queries.js +18 -18
  10. package/dist/db/fact_store.d.ts +3 -3
  11. package/dist/db/fact_store.js +2 -2
  12. package/dist/testing/CLAUDE.md +16 -11
  13. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +57 -0
  14. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -0
  15. package/dist/testing/cross_backend/create_cross_backend_global_setup.js +31 -0
  16. package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts +59 -0
  17. package/dist/testing/cross_backend/create_schema_parity_global_setup.d.ts.map +1 -0
  18. package/dist/testing/cross_backend/create_schema_parity_global_setup.js +27 -0
  19. package/dist/testing/cross_backend/default_backend_configs.d.ts +13 -0
  20. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  21. package/dist/testing/cross_backend/default_backend_configs.js +4 -4
  22. package/dist/testing/cross_backend/make_cross_backend_project.d.ts +72 -0
  23. package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -0
  24. package/dist/testing/cross_backend/make_cross_backend_project.js +51 -0
  25. package/dist/testing/cross_backend/setup.d.ts +16 -0
  26. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  27. package/dist/testing/cross_backend/setup.js +16 -0
  28. package/dist/testing/cross_backend/standard.d.ts +8 -0
  29. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  30. package/dist/testing/cross_backend/standard.js +1 -0
  31. package/dist/testing/cross_backend/testing_reset_actions.d.ts +71 -11
  32. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  33. package/dist/testing/cross_backend/testing_reset_actions.js +38 -5
  34. package/dist/testing/integration.d.ts.map +1 -1
  35. package/dist/testing/integration.js +20 -3
  36. package/dist/testing/schema_introspect.d.ts +74 -58
  37. package/dist/testing/schema_introspect.d.ts.map +1 -1
  38. package/dist/testing/schema_introspect.js +77 -15
  39. package/dist/testing/schema_parity.d.ts +7 -17
  40. package/dist/testing/schema_parity.d.ts.map +1 -1
  41. package/dist/testing/schema_parity.js +3 -37
  42. 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`** — `facts` (content-addressed bytes: embedded `bytes` xor
84
- `external_url`, CHECK enforces exactly one) + `fact_refs` (declared
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
- `memos` (`(fn_id, input_hash) → output_hash`). `FACT_MIGRATION_NS`, namespace
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 `facts(hash)` —
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 `facts(hash)` —
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
  *
@@ -3,32 +3,32 @@
3
3
  *
4
4
  * Three tables:
5
5
  *
6
- * - `facts` — content-addressed bytes. `hash = 'blake3:<hex64>'`. Either
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
- * - `fact_refs` — declared dependency edges (source fact → target fact).
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
- * - `memos` — `(fn_id, input_hash) → output_hash` for memoized computations.
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
- /** `facts` table — content-addressed byte store. */
19
- export declare const FACTS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS facts (\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 facts_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)\n)";
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
- * `fact_refs` table — declared dependency edges between facts.
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 fact_refs (\n\tsource_hash TEXT NOT NULL REFERENCES facts(hash) ON DELETE CASCADE,\n\ttarget_hash TEXT NOT NULL,\n\tPRIMARY KEY (source_hash, target_hash)\n)";
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 idx_fact_refs_target ON fact_refs(target_hash)";
28
- /** `memos` table — `(fn_id, input_hash) → output_hash` for memoized computations. */
29
- export declare const MEMOS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS memos (\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)";
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 ["memos", "fact_refs", "facts"];
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,oDAAoD;AACpD,eAAO,MAAM,YAAY,uSASvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,+LAK3B,CAAC;AAEH,4DAA4D;AAC5D,eAAO,MAAM,sBAAsB,gFACuC,CAAC;AAE3E,qFAAqF;AACrF,eAAO,MAAM,YAAY,oNAOvB,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,0CAA2C,CAAC;AAEzE,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"}
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"}
@@ -3,45 +3,45 @@
3
3
  *
4
4
  * Three tables:
5
5
  *
6
- * - `facts` — content-addressed bytes. `hash = 'blake3:<hex64>'`. Either
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
- * - `fact_refs` — declared dependency edges (source fact → target fact).
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
- * - `memos` — `(fn_id, input_hash) → output_hash` for memoized computations.
13
+ * - `memo` — `(fn_id, input_hash) → output_hash` for memoized computations.
14
14
  *
15
15
  * @module
16
16
  */
17
- /** `facts` table — content-addressed byte store. */
17
+ /** `fact` table — content-addressed byte store. */
18
18
  export const FACTS_SCHEMA = `
19
- CREATE TABLE IF NOT EXISTS facts (
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 facts_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)
26
+ CONSTRAINT fact_storage_present CHECK (bytes IS NOT NULL OR external_url IS NOT NULL)
27
27
  )`;
28
28
  /**
29
- * `fact_refs` table — declared dependency edges between facts.
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 fact_refs (
35
- source_hash TEXT NOT NULL REFERENCES facts(hash) ON DELETE CASCADE,
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 idx_fact_refs_target ON fact_refs(target_hash)`;
42
- /** `memos` table — `(fn_id, input_hash) → output_hash` for memoized computations. */
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 memos (
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 = ['memos', 'fact_refs', 'facts'];
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 `facts` and `fact_refs` tables.
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 facts`. */
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 `facts_storage_present` CHECK
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 `fact_refs`).
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 `facts` PK index.
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 `fact_refs` rows via the `ON DELETE CASCADE`
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 `facts` where no active
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 — `facts` lives in the
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 `facts.created_at`. Pass `null`
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":"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"}
1
+ {"version":3,"file":"fact_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/fact_queries.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Raw queries against the `facts` and `fact_refs` tables.
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 `facts_storage_present` CHECK
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 `fact_refs`).
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 facts (hash, bytes, external_url, content_type, size)
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 fact_refs (source_hash, target_hash)
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 facts WHERE hash = $1`, [hash]);
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 facts WHERE hash = $1`, [hash]);
56
+ FROM fact WHERE hash = $1`, [hash]);
57
57
  return row ?? null;
58
58
  };
59
59
  /**
60
- * Cheap existence check. Backed by the `facts` PK index.
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 facts WHERE hash = $1) AS exists`, [hash]);
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 fact_refs WHERE source_hash = $1`, [source_hash]);
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 `fact_refs` rows via the `ON DELETE CASCADE`
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 facts WHERE hash = $1
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 `facts` where no active
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 — `facts` lives in the
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 `facts.created_at`. Pass `null`
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 facts f
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 facts f
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 facts f
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[]
@@ -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 `facts` row, larger ones route to
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. `fact_refs` rows referencing this hash as a source
90
- * cascade via the FK; `fact_refs` targeting this hash do **not** —
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
  *
@@ -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. `fact_refs` rows referencing this hash as a source
161
- * cascade via the FK; `fact_refs` targeting this hash do **not** —
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
  *
@@ -209,25 +209,26 @@ 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, and `schema_version` rows.
213
- - `SchemaSnapshot` — fully JSON-serializable; every collection deterministically sorted on capture so structural equality is stable across runs. `applied_at` is excluded from `schema_version` rows so timestamps don't drift the snapshot.
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: `schema_version_only_in`, `schema_version_sequence_differs`, `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`.
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
- **Cross-impl gate pattern** — consumers running two backends against a
223
- shared schema (zzz's `--backend=both`, fuz_app's cross-backend suite)
224
- bootstrap each impl against an isolated DB, snapshot, then compare:
222
+ **Cross-impl gate pattern** — a dual-impl consumer running two backends
223
+ (a TS Hono server and a Rust spine server) against a shared schema, plus
224
+ fuz_app's own cross-backend suite, bootstrap each impl against an isolated
225
+ DB, snapshot, then compare:
225
226
 
226
227
  ```ts
227
- await drop_recreate_db('zzz_test');
228
+ await drop_recreate_db('app_test');
228
229
  await spawn_backend(deno_config);
229
230
  const snapshot_deno = await query_schema_snapshot(db);
230
- await drop_recreate_db('zzz_test');
231
+ await drop_recreate_db('app_test');
231
232
  await spawn_backend(rust_config);
232
233
  const snapshot_rust = await query_schema_snapshot(db);
233
234
  assert_schema_snapshots_equal(snapshot_deno, snapshot_rust, {a: 'deno', b: 'rust'});
@@ -789,8 +790,8 @@ points:
789
790
  The standard test suites take a unified
790
791
  `{setup_test, surface_source, capabilities}` shape so the same suite bodies
791
792
  run against an in-process Hono harness today and against a spawned backend
792
- over real HTTP — either the Rust spine (`zzz_server`, `fuz_forge_server`, or
793
- the non-domain `testing_spine_stub`) or a **TS** spine binary built on the
793
+ over real HTTP — either the Rust spine (`zzz_server`, another consumer's
794
+ spine server, or the non-domain `testing_spine_stub`) or a **TS** spine binary built on the
794
795
  test-server core below (fuz_app's own domain-free `testing_spine_server`, run
795
796
  on Node + Deno + Bun). In-process is the fast feedback path; cross-process is the
796
797
  source of truth for wire-shape conformance.
@@ -1094,7 +1095,11 @@ in-process legs (plain `gro test`) are `src/test/auth/cell_crud_parity.db.test.t
1094
1095
  same bootstrap-equivalent step (the only path for roles like
1095
1096
  `ROLE_KEEPER` whose `grant_paths` is bootstrap-only), refreshes
1096
1097
  `DaemonTokenState.keeper_account_id` to the new row, then fires the
1097
- consumer-supplied `reset_state` callback for domain-state reset. Auth
1098
+ consumer-supplied `reset_state(db)` callback for domain-state reset
1099
+ passed the **transactional** `Db` the auth wipe ran on, so DB-domain
1100
+ consumers (e.g. fuz_forge truncating its cell / fact / file tables) reset
1101
+ on the same connection rather than a separately-pooled one that would
1102
+ deadlock against this open transaction under PGlite. Auth
1098
1103
  gates on `credential_types: ['daemon_token']` — effectively keeper-only
1099
1104
  without forcing the `actor: 'required'` ⟺ `acting?: ActingActor`
1100
1105
  biconditional. No free-form runtime grant action exists — see the
@@ -0,0 +1,57 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Generic vitest `globalSetup` factory for cross-backend integration suites.
4
+ *
5
+ * Pairs with `make_cross_backend_project`: each cross-backend vitest project
6
+ * sets its own `test.name`, and this factory derives the backend name from
7
+ * that project name (vitest 4 passes the `TestProject` to globalSetup), picks
8
+ * the matching `BackendConfig`, spawns + bootstraps it via `bootstrap_backend`,
9
+ * and `provide`s a serializable handle that `*.cross.test.ts` files
10
+ * `inject` and rebuild with `reconstruct_bootstrapped_handle`.
11
+ *
12
+ * A consumer's `global_setup.ts` collapses to:
13
+ *
14
+ * ```ts
15
+ * import {create_cross_backend_global_setup} from
16
+ * '@fuzdev/fuz_app/testing/cross_backend/create_cross_backend_global_setup.js';
17
+ * import {deno_backend_config, rust_backend_config} from './my_backend_config.js';
18
+ * import './cross_test_types.js'; // augments inject('backend_handle')
19
+ *
20
+ * export default create_cross_backend_global_setup({
21
+ * configs: {deno: deno_backend_config, rust: rust_backend_config},
22
+ * });
23
+ * ```
24
+ *
25
+ * vitest 4's `provide` hard-rejects non-serializable values, so the live
26
+ * `child` / `teardown` / `keeper_transport` are stripped via
27
+ * `serialize_bootstrapped_handle`; the teardown closure stays in the
28
+ * globalSetup process and is returned for vitest to fire after the suite.
29
+ *
30
+ * @module
31
+ */
32
+ import type { TestProject } from 'vitest/node';
33
+ import type { BackendConfig } from './backend_config.js';
34
+ export interface CrossBackendGlobalSetupOptions {
35
+ /**
36
+ * Map of derived backend name → `BackendConfig` factory. The derived
37
+ * name (see `derive_name`) selects the factory; unknown names throw with
38
+ * the full supported list so a misnamed project surfaces clearly.
39
+ */
40
+ readonly configs: Readonly<Record<string, () => BackendConfig>>;
41
+ /**
42
+ * Derive the backend name from the vitest project name. Default strips
43
+ * `cross_backend_(ts_)?`.
44
+ */
45
+ readonly derive_name?: (project_name: string) => string;
46
+ /**
47
+ * Key passed to `project.provide` (and read by `inject` in test files).
48
+ * Default `'backend_handle'`. Augment vitest's `ProvidedContext` for it.
49
+ */
50
+ readonly provide_key?: string;
51
+ }
52
+ /**
53
+ * Build a vitest `globalSetup` default export. Returns the
54
+ * `(project) => teardown` function vitest 4 expects.
55
+ */
56
+ export declare const create_cross_backend_global_setup: ({ configs, derive_name, provide_key, }: CrossBackendGlobalSetupOptions) => ((project: TestProject) => Promise<() => Promise<void>>);
57
+ //# sourceMappingURL=create_cross_backend_global_setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create_cross_backend_global_setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/create_cross_backend_global_setup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAYvD,MAAM,WAAW,8BAA8B;IAC9C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,aAAa,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,GAAI,wCAI/C,8BAA8B,KAAG,CAAC,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAqB1F,CAAC"}
@@ -0,0 +1,31 @@
1
+ import '../assert_dev_env.js';
2
+ import { bootstrap_backend } from './bootstrap_backend.js';
3
+ import { serialize_bootstrapped_handle } from './setup.js';
4
+ /**
5
+ * Default project-name → backend-name reduction: strips the
6
+ * `cross_backend_` prefix plus an optional `ts_` discriminator (the TS
7
+ * canonical backends carry it to distinguish JS runtimes). So
8
+ * `cross_backend_ts_deno` → `deno` and `cross_backend_rust` → `rust`.
9
+ */
10
+ const DEFAULT_PROJECT_NAME_PREFIX = /^cross_backend_(?:ts_)?/;
11
+ /**
12
+ * Build a vitest `globalSetup` default export. Returns the
13
+ * `(project) => teardown` function vitest 4 expects.
14
+ */
15
+ export const create_cross_backend_global_setup = ({ configs, derive_name = (project_name) => project_name.replace(DEFAULT_PROJECT_NAME_PREFIX, ''), provide_key = 'backend_handle', }) => {
16
+ return async (project) => {
17
+ const name = derive_name(project.name);
18
+ const factory = configs[name];
19
+ if (!factory) {
20
+ throw new Error(`Could not derive backend config from vitest project '${project.name}' ` +
21
+ `(derived name '${name}') — expected one of: ${Object.keys(configs).join(', ')}`);
22
+ }
23
+ const bootstrapped = await bootstrap_backend(factory());
24
+ // vitest's `provide` is keyed on the consumer-augmented `ProvidedContext`;
25
+ // the generic key is a plain string, so cast past the keyof constraint.
26
+ project.provide(provide_key, serialize_bootstrapped_handle(bootstrapped));
27
+ return async () => {
28
+ await bootstrapped.teardown();
29
+ };
30
+ };
31
+ };
@@ -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"}