@fuzdev/fuz_app 0.98.1 → 0.99.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/CLAUDE.md +6 -4
- package/dist/db/cell_ddl.d.ts +3 -2
- package/dist/db/cell_ddl.d.ts.map +1 -1
- package/dist/db/cell_ddl.js +4 -3
- package/dist/db/cell_history_ddl.js +1 -1
- package/dist/db/fact_ddl.js +1 -1
- package/dist/db/migrate.d.ts +11 -0
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +11 -0
- package/dist/testing/CLAUDE.md +2 -0
- package/dist/testing/cross_backend/setup.d.ts +13 -1
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +30 -4
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +8 -4
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +48 -4
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +41 -5
- package/dist/testing/schema_introspect.d.ts +39 -0
- package/dist/testing/schema_introspect.d.ts.map +1 -1
- package/dist/testing/schema_introspect.js +47 -0
- package/dist/testing/schema_parity.d.ts +42 -1
- package/dist/testing/schema_parity.d.ts.map +1 -1
- package/dist/testing/schema_parity.js +91 -0
- package/package.json +1 -1
package/dist/db/CLAUDE.md
CHANGED
|
@@ -128,14 +128,16 @@ DO NOTHING`), `_put_fact_refs`, `_get_fact` / `_get_fact_meta` / `_has_fact`
|
|
|
128
128
|
|
|
129
129
|
FK dependency dictates order; consumers register via `migration_namespaces`
|
|
130
130
|
on `create_app_backend` (after the built-in `fuz_auth` namespace). The fact
|
|
131
|
-
namespace is independent
|
|
132
|
-
|
|
131
|
+
namespace is FK-independent so its slot is free; the spine binary and every
|
|
132
|
+
consumer place it last, keeping cell + its `cell_history` FK-child adjacent
|
|
133
|
+
(cross-namespace order is immaterial to the `schema_version` rows — `sequence`
|
|
134
|
+
is assigned per-namespace — so only the cell→cell_history FK constrains it):
|
|
133
135
|
|
|
134
136
|
```
|
|
135
137
|
auth_migration_ns (built-in, fuz_auth)
|
|
136
|
-
→ CELL_MIGRATION_NS
|
|
137
|
-
→ FACT_MIGRATION_NS (fuz_facts — no external FK; optional)
|
|
138
|
+
→ CELL_MIGRATION_NS (fuz_cell — FKs actor.id)
|
|
138
139
|
→ CELL_HISTORY_MIGRATION_NS (fuz_cell_history — FKs cell.id)
|
|
140
|
+
→ FACT_MIGRATION_NS (fuz_facts — no external FK; slot is free)
|
|
139
141
|
```
|
|
140
142
|
|
|
141
143
|
### Pre-stable migration policy
|
package/dist/db/cell_ddl.d.ts
CHANGED
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
* `_at`-everywhere convention used by `account`, `actor`, `audit_log`,
|
|
28
28
|
* `role_grant`, etc.
|
|
29
29
|
*
|
|
30
|
-
* **Single-migration shape**: `
|
|
30
|
+
* **Single-migration shape**: `full_cell_schema` creates the canonical
|
|
31
31
|
* cell + cell_grant + cell_field + cell_item layout in one shot from the
|
|
32
|
-
* live exported constants.
|
|
32
|
+
* live exported constants. The dormant `cell_history` table lives in the
|
|
33
|
+
* separate `fuz_cell_history` namespace (`cell_history_ddl.ts`).
|
|
33
34
|
*
|
|
34
35
|
* @module
|
|
35
36
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cell_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/cell_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EAAC,SAAS,EAAE,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,oBAAoB,oIAI1B,CAAC;AAER;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,4aAYtB,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,MAAM,CAYtC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,0iBAc5B,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAW5C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,+RAO5B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,MAAM,CAE5C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,qSAO3B,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,MAAM,CAG3C,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB,4DAA6D,CAAC;AAE3F,uBAAuB;AACvB,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,SAAS,CAuB5C,CAAC;AAEF,gDAAgD;AAChD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAEnD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,kBAG/B,CAAC"}
|
package/dist/db/cell_ddl.js
CHANGED
|
@@ -27,9 +27,10 @@
|
|
|
27
27
|
* `_at`-everywhere convention used by `account`, `actor`, `audit_log`,
|
|
28
28
|
* `role_grant`, etc.
|
|
29
29
|
*
|
|
30
|
-
* **Single-migration shape**: `
|
|
30
|
+
* **Single-migration shape**: `full_cell_schema` creates the canonical
|
|
31
31
|
* cell + cell_grant + cell_field + cell_item layout in one shot from the
|
|
32
|
-
* live exported constants.
|
|
32
|
+
* live exported constants. The dormant `cell_history` table lives in the
|
|
33
|
+
* separate `fuz_cell_history` namespace (`cell_history_ddl.ts`).
|
|
33
34
|
*
|
|
34
35
|
* @module
|
|
35
36
|
*/
|
|
@@ -216,7 +217,7 @@ export const CELL_DROP_TABLES = ['cell_field', 'cell_item', 'cell_grant', 'cell'
|
|
|
216
217
|
/** Cell migrations. */
|
|
217
218
|
export const CELL_MIGRATIONS = [
|
|
218
219
|
{
|
|
219
|
-
name: '
|
|
220
|
+
name: 'full_cell_schema',
|
|
220
221
|
up: async (db) => {
|
|
221
222
|
await db.query(CELL_VISIBILITY_TYPE);
|
|
222
223
|
await db.query(CELL_SCHEMA);
|
|
@@ -43,7 +43,7 @@ export const CELL_HISTORY_DROP_TABLES = ['cell_history'];
|
|
|
43
43
|
/** Cell-history migrations. */
|
|
44
44
|
export const CELL_HISTORY_MIGRATIONS = [
|
|
45
45
|
{
|
|
46
|
-
name: '
|
|
46
|
+
name: 'full_cell_history_schema',
|
|
47
47
|
up: async (db) => {
|
|
48
48
|
await db.query(CELL_HISTORY_SCHEMA);
|
|
49
49
|
for (const sql of CELL_HISTORY_INDEXES) {
|
package/dist/db/fact_ddl.js
CHANGED
|
@@ -53,7 +53,7 @@ export const FACT_DROP_TABLES = ['memo', 'fact_ref', 'fact'];
|
|
|
53
53
|
/** Fact + memo migrations. */
|
|
54
54
|
export const FACT_MIGRATIONS = [
|
|
55
55
|
{
|
|
56
|
-
name: '
|
|
56
|
+
name: 'full_fact_schema',
|
|
57
57
|
up: async (db) => {
|
|
58
58
|
await db.query(FACTS_SCHEMA);
|
|
59
59
|
await db.query(FACT_REFS_SCHEMA);
|
package/dist/db/migrate.d.ts
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
* `(namespace, name, sequence, applied_at)` — and the runner verifies the
|
|
7
7
|
* applied list is a name-prefix of the code's migration array at boot.
|
|
8
8
|
*
|
|
9
|
+
* **`name` is descriptive identity, not a version ordinal.** The runner orders
|
|
10
|
+
* by the `sequence` column and matches `name` by array position — it never
|
|
11
|
+
* parses the name — so `name` carries *what the migration is*
|
|
12
|
+
* (`full_cell_schema`, `role_grant_offer_and_scoped_role_grants`), and the
|
|
13
|
+
* redundant `_vN` ordinal is avoided (it would only duplicate `sequence`, and
|
|
14
|
+
* a `cell_v0` name tempts editing "v0" in place). `name` is half the
|
|
15
|
+
* `schema_version` PK (with `namespace`), so a rename is an identity change
|
|
16
|
+
* every already-migrated DB sees as `name-divergence-at-N`. This is also the
|
|
17
|
+
* cross-impl contract: the TS and Rust spines must record byte-identical
|
|
18
|
+
* `(namespace, name, sequence)` so a consumer can swap backends over one DB.
|
|
19
|
+
*
|
|
9
20
|
* **Schema is not stabilized yet — append-only is NOT the rule.** While
|
|
10
21
|
* fuz_app is pre-stable, migration bodies, names, and positions can change
|
|
11
22
|
* freely between versions; consumers upgrading across a schema change are
|
package/dist/db/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAIH,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAEhC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,2BAA2B,GAC3B,4BAA4B,GAC5B,sCAAsC,CAAC;AAE1C,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEnC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB;CAQtF;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,cAAc,GAC1B,IAAI,EAAE,EACN,YAAY,KAAK,CAAC,kBAAkB,CAAC,KACnC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAuFhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,QAAQ,GACpB,IAAI,EAAE,EACN,IAAI,kBAAkB,EACtB,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,OAAO,CAAC,IAAI,CA+Dd,CAAC"}
|
package/dist/db/migrate.js
CHANGED
|
@@ -6,6 +6,17 @@
|
|
|
6
6
|
* `(namespace, name, sequence, applied_at)` — and the runner verifies the
|
|
7
7
|
* applied list is a name-prefix of the code's migration array at boot.
|
|
8
8
|
*
|
|
9
|
+
* **`name` is descriptive identity, not a version ordinal.** The runner orders
|
|
10
|
+
* by the `sequence` column and matches `name` by array position — it never
|
|
11
|
+
* parses the name — so `name` carries *what the migration is*
|
|
12
|
+
* (`full_cell_schema`, `role_grant_offer_and_scoped_role_grants`), and the
|
|
13
|
+
* redundant `_vN` ordinal is avoided (it would only duplicate `sequence`, and
|
|
14
|
+
* a `cell_v0` name tempts editing "v0" in place). `name` is half the
|
|
15
|
+
* `schema_version` PK (with `namespace`), so a rename is an identity change
|
|
16
|
+
* every already-migrated DB sees as `name-divergence-at-N`. This is also the
|
|
17
|
+
* cross-impl contract: the TS and Rust spines must record byte-identical
|
|
18
|
+
* `(namespace, name, sequence)` so a consumer can swap backends over one DB.
|
|
19
|
+
*
|
|
9
20
|
* **Schema is not stabilized yet — append-only is NOT the rule.** While
|
|
10
21
|
* fuz_app is pre-stable, migration bodies, names, and positions can change
|
|
11
22
|
* freely between versions; consumers upgrading across a schema change are
|
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -214,6 +214,7 @@ test-only by construction.
|
|
|
214
214
|
|
|
215
215
|
- `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 enum types (`pg_enum` labels in declared `enumsortorder`, so a `cell_visibility` label-set/order drift is gated). The `schema_version` migration tracker is always excluded — it's framework bookkeeping, not domain schema, and impls organize migration namespaces differently. Twinned by `fuz_db::query_schema_snapshot` (Rust); the `_testing_schema_snapshot` RPC's wire validator is the shared `SchemaSnapshot`, so the enum field must serialize on both sides.
|
|
216
216
|
- `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.
|
|
217
|
+
- `query_migration_tracker(db)` / `MigrationTracker` — the migration-_identity_ twin of the snapshot: reads the `schema_version` tracker rows (`[{namespace, name, sequence}]`) the snapshot deliberately _excludes_. `MigrationTracker` is the `_testing_migration_tracker` RPC's wire validator; pairs with `assert_migration_trackers_equal` to gate that the two spines record byte-identical migration identity (the swap-freely invariant), catching a migration-name or namespace-partitioning drift the provenance-agnostic schema snapshot can't see.
|
|
217
218
|
|
|
218
219
|
### `schema_parity.ts` — `assert_schema_snapshots_equal`
|
|
219
220
|
|
|
@@ -221,6 +222,7 @@ test-only by construction.
|
|
|
221
222
|
- `format_schema_diffs(diffs, labels?)` — human-readable multi-line rendering; labels name the impl on each side (e.g., `{a: 'deno', b: 'rust'}`).
|
|
222
223
|
- `assert_schema_snapshots_equal(a, b, labels?)` — throws on drift with a fully-formatted diff message.
|
|
223
224
|
- `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`, `enum_only_in`, `enum_labels_differ` (enum labels compared positionally — declared order is significant).
|
|
225
|
+
- `diff_migration_trackers(a, b)` / `format_migration_tracker_diffs(diffs, labels?)` / `assert_migration_trackers_equal(a, b, labels?)` — the migration-identity twins of the snapshot diff/format/assert, over two `MigrationTracker`s. `MigrationTrackerDiff` is keyed on `(namespace, name)` (the `schema_version` PK): `tracker_row_only_in` (a migration rename or namespace re-partition) and `tracker_sequence_differs` (a re-order).
|
|
224
226
|
|
|
225
227
|
fuz_app's own spine gates this cross-process via the `cross_backend_parity` project (`schema_parity.cross.test.ts` + the dual-spawn `global_setup_schema_parity.ts`), diffing the TS spine ↔ `testing_spine_stub` full schema — `npm run test:cross:parity`. The forge has its own deno↔rust parity gate.
|
|
226
228
|
|
|
@@ -3,7 +3,7 @@ import { Uuid } from '@fuzdev/fuz_util/id.ts';
|
|
|
3
3
|
import type { TestAccount } from '../app_server.js';
|
|
4
4
|
import type { BackendCapabilities } from './capabilities.js';
|
|
5
5
|
import { type FetchTransport } from '../transports/fetch_transport.js';
|
|
6
|
-
import type
|
|
6
|
+
import { MigrationTracker, type SchemaSnapshot } from '../schema_introspect.js';
|
|
7
7
|
import { ActionManifest } from './action_manifest.js';
|
|
8
8
|
import type { BackendHandle } from './spawn_backend.js';
|
|
9
9
|
/**
|
|
@@ -406,6 +406,18 @@ export declare const capture_schema_snapshot: (handle: ReconstructedBootstrapped
|
|
|
406
406
|
* `fuz_testing` mirror); the normalized shapes match by design.
|
|
407
407
|
*/
|
|
408
408
|
export declare const capture_action_manifest: (handle: ReconstructedBootstrappedBackendHandle) => Promise<ActionManifest>;
|
|
409
|
+
/**
|
|
410
|
+
* Capture a backend's `schema_version` migration tracker over the
|
|
411
|
+
* `_testing_migration_tracker` RPC action (keeper daemon-token channel). The
|
|
412
|
+
* provenance twin of `capture_schema_snapshot` — where that reads the
|
|
413
|
+
* resulting schema (and excludes the tracker), this reads the tracker rows
|
|
414
|
+
* themselves. Pair two calls with `assert_migration_trackers_equal`
|
|
415
|
+
* (`testing/schema_parity.ts`) to gate that the TS spine and the Rust
|
|
416
|
+
* `testing_spine_stub` record byte-identical migration identity
|
|
417
|
+
* (`namespace` + `name` + `sequence`) — the invariant the schema-snapshot
|
|
418
|
+
* gate is blind to.
|
|
419
|
+
*/
|
|
420
|
+
export declare const capture_migration_tracker: (handle: ReconstructedBootstrappedBackendHandle) => Promise<MigrationTracker>;
|
|
409
421
|
/**
|
|
410
422
|
* Backdating offset (seconds) the `mint_expired_session` seam passes to
|
|
411
423
|
* `mint_test_session` / `_testing_mint_session`. A minute in the past is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0B9B,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAK5C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAyB,KAAK,cAAc,EAAC,MAAM,kCAAkC,CAAC;AAC7F,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,6 +27,7 @@ import { DAEMON_TOKEN_HEADER } from '../../auth/daemon_token.js';
|
|
|
27
27
|
import { USERNAME_LENGTH_MAX } from '../../primitive_schemas.js';
|
|
28
28
|
import { DEFAULT_TEST_PASSWORD } from '../test_credentials.js';
|
|
29
29
|
import { create_fetch_transport } from '../transports/fetch_transport.js';
|
|
30
|
+
import { MigrationTracker } from '../schema_introspect.js';
|
|
30
31
|
import { ActionManifest } from './action_manifest.js';
|
|
31
32
|
/**
|
|
32
33
|
* Build an `ExtraAccountFixture` from a seeded `{account, actor,
|
|
@@ -179,6 +180,22 @@ export const capture_schema_snapshot = async (handle, options = {}) => {
|
|
|
179
180
|
// reconcile first, so it's left as a follow-up.
|
|
180
181
|
return raw;
|
|
181
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* Capture a no-arg daemon-token introspection RPC and validate the wire
|
|
185
|
+
* response against `schema`. The shared shape behind the parse-based
|
|
186
|
+
* `capture_*` cross-impl parity probes: call a `_testing_*` introspection
|
|
187
|
+
* action over the keeper's daemon-token channel, then `parse` so a shape
|
|
188
|
+
* drift between the TS and Rust answers fails loud at the boundary.
|
|
189
|
+
*
|
|
190
|
+
* (`capture_schema_snapshot` deliberately stays separate: it *casts* rather
|
|
191
|
+
* than parses — narrowing a large snapshot to the Zod model would silently
|
|
192
|
+
* drop unmodeled fields the parity diff should still see — and it takes an
|
|
193
|
+
* `exclude_tables` param.)
|
|
194
|
+
*/
|
|
195
|
+
const capture_via_daemon = async (handle, method, schema) => {
|
|
196
|
+
const raw = await rpc_via_transport(handle.keeper_daemon_transport, handle.config.rpc_path, method, {}, handle.config.name, { [DAEMON_TOKEN_HEADER]: handle.daemon_token });
|
|
197
|
+
return schema.parse(raw);
|
|
198
|
+
};
|
|
182
199
|
/**
|
|
183
200
|
* Capture a backend's live RPC action manifest over the
|
|
184
201
|
* `_testing_action_manifest` RPC action (keeper daemon-token channel). The
|
|
@@ -189,10 +206,19 @@ export const capture_schema_snapshot = async (handle, options = {}) => {
|
|
|
189
206
|
* own live registry (TS via `build_action_manifest`, Rust via the
|
|
190
207
|
* `fuz_testing` mirror); the normalized shapes match by design.
|
|
191
208
|
*/
|
|
192
|
-
export const capture_action_manifest = async (handle) =>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
export const capture_action_manifest = async (handle) => capture_via_daemon(handle, '_testing_action_manifest', ActionManifest);
|
|
210
|
+
/**
|
|
211
|
+
* Capture a backend's `schema_version` migration tracker over the
|
|
212
|
+
* `_testing_migration_tracker` RPC action (keeper daemon-token channel). The
|
|
213
|
+
* provenance twin of `capture_schema_snapshot` — where that reads the
|
|
214
|
+
* resulting schema (and excludes the tracker), this reads the tracker rows
|
|
215
|
+
* themselves. Pair two calls with `assert_migration_trackers_equal`
|
|
216
|
+
* (`testing/schema_parity.ts`) to gate that the TS spine and the Rust
|
|
217
|
+
* `testing_spine_stub` record byte-identical migration identity
|
|
218
|
+
* (`namespace` + `name` + `sequence`) — the invariant the schema-snapshot
|
|
219
|
+
* gate is blind to.
|
|
220
|
+
*/
|
|
221
|
+
export const capture_migration_tracker = async (handle) => capture_via_daemon(handle, '_testing_migration_tracker', MigrationTracker);
|
|
196
222
|
/**
|
|
197
223
|
* Backdating offset (seconds) the `mint_expired_session` seam passes to
|
|
198
224
|
* `mint_test_session` / `_testing_mint_session`. A minute in the past is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_backdoor.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_backdoor.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"testing_backdoor.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_backdoor.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA2D9B,OAAO,KAAK,EAAC,wBAAwB,EAAc,MAAM,YAAY,CAAC;AAqEtE,kEAAkE;AAClE,MAAM,MAAM,+BAA+B,GAAG,wBAAwB,CAAC;AAEvE,eAAO,MAAM,qCAAqC,GACjD,SAAS,+BAA+B,KACtC,IAiCF,CAAC"}
|
|
@@ -4,10 +4,11 @@ import '../assert_dev_env.js';
|
|
|
4
4
|
* actions.
|
|
5
5
|
*
|
|
6
6
|
* `_testing_reset` / `_testing_mint_session` / `_testing_put_fact` /
|
|
7
|
-
* `_testing_schema_snapshot` / `
|
|
8
|
-
* test-binary actions the production
|
|
9
|
-
* writes (full auth wipe, forged session
|
|
10
|
-
* introspection reads (the live schema
|
|
7
|
+
* `_testing_schema_snapshot` / `_testing_migration_tracker` /
|
|
8
|
+
* `_testing_action_manifest` are privileged test-binary actions the production
|
|
9
|
+
* wire never exposes — three direct DB writes (full auth wipe, forged session
|
|
10
|
+
* row, raw fact insert) plus three introspection reads (the live schema, the
|
|
11
|
+
* `schema_version` migration tracker, and the live RPC registry — the highest
|
|
11
12
|
* info-leak of the set were the gate to break). Their only structural fence
|
|
12
13
|
* is the **daemon-token** credential gate on
|
|
13
14
|
* each spec's `auth` axis. A test binary live-mounts them on its RPC
|
|
@@ -67,6 +68,9 @@ const backdoor_methods = [
|
|
|
67
68
|
// The schema-dump read — `exclude_tables` is optional, so `{}` is valid
|
|
68
69
|
// and clears the 400 phase like the writes above.
|
|
69
70
|
{ method: '_testing_schema_snapshot', params: {} },
|
|
71
|
+
// The migration-tracker-dump read — input is an empty strict object, so `{}`
|
|
72
|
+
// is valid and clears the 400 phase, reaching the credential gate like the rest.
|
|
73
|
+
{ method: '_testing_migration_tracker', params: {} },
|
|
70
74
|
// The RPC-registry-dump read — input is an empty strict object, so `{}` is
|
|
71
75
|
// valid and clears the 400 phase, reaching the credential gate like the rest.
|
|
72
76
|
{ method: '_testing_action_manifest', params: {} },
|
|
@@ -2,15 +2,18 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Test-binary RPC actions for cross-process integration tests.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Six daemon-token-authed actions, bundled by `create_testing_actions`:
|
|
6
6
|
* **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
|
|
7
7
|
* (audit barrier), **`_testing_mint_session`** (forge an
|
|
8
8
|
* expired-by-construction server-side session for the expiry conformance
|
|
9
9
|
* cases), **`_testing_put_fact`** (seed an embedded fact for the fact-serving
|
|
10
|
-
* suite),
|
|
11
|
-
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot)
|
|
10
|
+
* suite), **`_testing_schema_snapshot`** (introspect the live schema for
|
|
11
|
+
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot), and
|
|
12
|
+
* **`_testing_migration_tracker`** (dump the `schema_version` tracker rows for
|
|
13
|
+
* cross-impl migration-identity parity — the provenance half the snapshot gate
|
|
14
|
+
* excludes by design).
|
|
12
15
|
*
|
|
13
|
-
* A
|
|
16
|
+
* A further daemon-token action, **`_testing_action_manifest`** (dump the live
|
|
14
17
|
* RPC registry for cross-impl method-set + auth-shape parity), lives here too
|
|
15
18
|
* but is **not** bundled by `create_testing_actions` — it must enumerate
|
|
16
19
|
* *every* mounted method, so it's appended at the full-mount layer
|
|
@@ -336,6 +339,47 @@ export declare const testing_schema_snapshot_action_spec: {
|
|
|
336
339
|
* `create_testing_actions`; mount directly for in-process use.
|
|
337
340
|
*/
|
|
338
341
|
export declare const create_testing_schema_snapshot_action: () => RpcAction;
|
|
342
|
+
/**
|
|
343
|
+
* `_testing_migration_tracker` — dump the `schema_version` tracker rows as a
|
|
344
|
+
* normalized `MigrationTracker` (`[{namespace, name, sequence}]`) for
|
|
345
|
+
* cross-impl migration-identity diffing. The provenance half of
|
|
346
|
+
* `_testing_schema_snapshot`: where that captures the resulting *schema*
|
|
347
|
+
* (and excludes the tracker by design), this captures the tracker *itself*,
|
|
348
|
+
* so the cross-backend harness can assert the two spines record byte-identical
|
|
349
|
+
* migration identity. This closes the gap that let the cell/fact
|
|
350
|
+
* migration-name divergence reach the visiones cutover undetected
|
|
351
|
+
* (`name-divergence-at-N` — same schema, divergent recorded names).
|
|
352
|
+
*
|
|
353
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`. The
|
|
354
|
+
* Rust mirror is `fuz_testing::create_testing_migration_tracker_action_spec`.
|
|
355
|
+
*/
|
|
356
|
+
export declare const testing_migration_tracker_action_spec: {
|
|
357
|
+
readonly method: "_testing_migration_tracker";
|
|
358
|
+
readonly kind: "request_response";
|
|
359
|
+
readonly initiator: "frontend";
|
|
360
|
+
readonly auth: {
|
|
361
|
+
readonly account: "required";
|
|
362
|
+
readonly actor: "none";
|
|
363
|
+
readonly credential_types: readonly ["daemon_token"];
|
|
364
|
+
};
|
|
365
|
+
readonly side_effects: false;
|
|
366
|
+
readonly input: z.ZodObject<{}, z.core.$strict>;
|
|
367
|
+
readonly output: z.ZodObject<{
|
|
368
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
369
|
+
namespace: z.ZodString;
|
|
370
|
+
name: z.ZodString;
|
|
371
|
+
sequence: z.ZodNumber;
|
|
372
|
+
}, z.core.$strip>>;
|
|
373
|
+
}, z.core.$strip>;
|
|
374
|
+
readonly async: true;
|
|
375
|
+
readonly description: string;
|
|
376
|
+
};
|
|
377
|
+
/**
|
|
378
|
+
* Build the standalone `_testing_migration_tracker` action. No deps — reads
|
|
379
|
+
* `ctx.db`'s `schema_version` via `query_migration_tracker`. Bundled by
|
|
380
|
+
* `create_testing_actions`; mount directly for in-process use.
|
|
381
|
+
*/
|
|
382
|
+
export declare const create_testing_migration_tracker_action: () => RpcAction;
|
|
339
383
|
/**
|
|
340
384
|
* `_testing_action_manifest` — dump the backend's live RPC method set as a
|
|
341
385
|
* normalized `ActionManifest` (one entry per method: `{method, side_effects,
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAItB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAKvE,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;AAqCvC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;QAiBpC;;;;;;;;WAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWyC,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;CAWA,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;CAwBC,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,QAAO,SACiB,CAAC;AAEzE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;CAeK,CAAC;AAE/C;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWF,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,QAAO,SAGvD,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qCAAqC;;;;;;;;;;;;;;;;;;;;CAYJ,CAAC;AAE/C;;;;GAIG;AACH,eAAO,MAAM,uCAAuC,QAAO,SAGzD,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAYF,CAAC;AAyB/C,eAAO,MAAM,qCAAqC,GACjD,SAAS,aAAa,CAAC,SAAS,CAAC,KAC/B,SAMF,CAAC;AAEF,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,CAsIjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
|
|
@@ -2,15 +2,18 @@ import '../assert_dev_env.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Test-binary RPC actions for cross-process integration tests.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Six daemon-token-authed actions, bundled by `create_testing_actions`:
|
|
6
6
|
* **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
|
|
7
7
|
* (audit barrier), **`_testing_mint_session`** (forge an
|
|
8
8
|
* expired-by-construction server-side session for the expiry conformance
|
|
9
9
|
* cases), **`_testing_put_fact`** (seed an embedded fact for the fact-serving
|
|
10
|
-
* suite),
|
|
11
|
-
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot)
|
|
10
|
+
* suite), **`_testing_schema_snapshot`** (introspect the live schema for
|
|
11
|
+
* cross-impl parity diffing against a Rust backend's `fuz_db` snapshot), and
|
|
12
|
+
* **`_testing_migration_tracker`** (dump the `schema_version` tracker rows for
|
|
13
|
+
* cross-impl migration-identity parity — the provenance half the snapshot gate
|
|
14
|
+
* excludes by design).
|
|
12
15
|
*
|
|
13
|
-
* A
|
|
16
|
+
* A further daemon-token action, **`_testing_action_manifest`** (dump the live
|
|
14
17
|
* RPC registry for cross-impl method-set + auth-shape parity), lives here too
|
|
15
18
|
* but is **not** bundled by `create_testing_actions` — it must enumerate
|
|
16
19
|
* *every* mounted method, so it's appended at the full-mount layer
|
|
@@ -73,7 +76,7 @@ import { protocol_action_specs } from '../../actions/protocol.js';
|
|
|
73
76
|
import { query_put_fact } from '../../db/fact_queries.js';
|
|
74
77
|
import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
|
|
75
78
|
import { auth_integration_truncate_tables } from '../db.js';
|
|
76
|
-
import { query_schema_snapshot, SchemaSnapshot } from '../schema_introspect.js';
|
|
79
|
+
import { query_schema_snapshot, SchemaSnapshot, query_migration_tracker, MigrationTracker, } from '../schema_introspect.js';
|
|
77
80
|
import { ActionManifest, build_action_manifest } from './action_manifest.js';
|
|
78
81
|
import { query_create_actor } from '../../auth/account_queries.js';
|
|
79
82
|
import { create_test_account_with_credentials, mint_test_session } from '../app_server.js';
|
|
@@ -302,6 +305,38 @@ export const testing_schema_snapshot_action_spec = {
|
|
|
302
305
|
* `create_testing_actions`; mount directly for in-process use.
|
|
303
306
|
*/
|
|
304
307
|
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 }));
|
|
308
|
+
/**
|
|
309
|
+
* `_testing_migration_tracker` — dump the `schema_version` tracker rows as a
|
|
310
|
+
* normalized `MigrationTracker` (`[{namespace, name, sequence}]`) for
|
|
311
|
+
* cross-impl migration-identity diffing. The provenance half of
|
|
312
|
+
* `_testing_schema_snapshot`: where that captures the resulting *schema*
|
|
313
|
+
* (and excludes the tracker by design), this captures the tracker *itself*,
|
|
314
|
+
* so the cross-backend harness can assert the two spines record byte-identical
|
|
315
|
+
* migration identity. This closes the gap that let the cell/fact
|
|
316
|
+
* migration-name divergence reach the visiones cutover undetected
|
|
317
|
+
* (`name-divergence-at-N` — same schema, divergent recorded names).
|
|
318
|
+
*
|
|
319
|
+
* `auth` gates on the daemon-token credential, matching `_testing_reset`. The
|
|
320
|
+
* Rust mirror is `fuz_testing::create_testing_migration_tracker_action_spec`.
|
|
321
|
+
*/
|
|
322
|
+
export const testing_migration_tracker_action_spec = {
|
|
323
|
+
method: '_testing_migration_tracker',
|
|
324
|
+
kind: 'request_response',
|
|
325
|
+
initiator: 'frontend',
|
|
326
|
+
auth: TESTING_ACTION_AUTH,
|
|
327
|
+
side_effects: false,
|
|
328
|
+
input: z.strictObject({}),
|
|
329
|
+
output: MigrationTracker,
|
|
330
|
+
async: true,
|
|
331
|
+
description: 'Test-binary only — dump the schema_version tracker rows as a normalized {namespace, name, ' +
|
|
332
|
+
'sequence} list for cross-impl migration-identity parity diffing.',
|
|
333
|
+
};
|
|
334
|
+
/**
|
|
335
|
+
* Build the standalone `_testing_migration_tracker` action. No deps — reads
|
|
336
|
+
* `ctx.db`'s `schema_version` via `query_migration_tracker`. Bundled by
|
|
337
|
+
* `create_testing_actions`; mount directly for in-process use.
|
|
338
|
+
*/
|
|
339
|
+
export const create_testing_migration_tracker_action = () => rpc_action(testing_migration_tracker_action_spec, async (_input, ctx) => query_migration_tracker(ctx.db));
|
|
305
340
|
/**
|
|
306
341
|
* `_testing_action_manifest` — dump the backend's live RPC method set as a
|
|
307
342
|
* normalized `ActionManifest` (one entry per method: `{method, side_effects,
|
|
@@ -495,6 +530,7 @@ export const create_testing_actions = (deps, options) => {
|
|
|
495
530
|
}),
|
|
496
531
|
create_testing_drain_effects_action(),
|
|
497
532
|
create_testing_schema_snapshot_action(),
|
|
533
|
+
create_testing_migration_tracker_action(),
|
|
498
534
|
];
|
|
499
535
|
};
|
|
500
536
|
/** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
|
|
@@ -109,6 +109,45 @@ export declare const SchemaSnapshot: z.ZodObject<{
|
|
|
109
109
|
}, z.core.$strip>>;
|
|
110
110
|
}, z.core.$strip>;
|
|
111
111
|
export type SchemaSnapshot = z.infer<typeof SchemaSnapshot>;
|
|
112
|
+
/**
|
|
113
|
+
* A single `schema_version` tracker row — the migration-identity primitive.
|
|
114
|
+
*
|
|
115
|
+
* Where `SchemaSnapshot` is provenance-agnostic (it captures the resulting
|
|
116
|
+
* tables and deliberately *excludes* the tracker), this **is** the tracker:
|
|
117
|
+
* the `(namespace, name, sequence)` the migration runner records per applied
|
|
118
|
+
* migration. `sequence` carries order; `name` carries identity (the PK is
|
|
119
|
+
* `(namespace, name)`). The cross-impl gate diffs these between the two
|
|
120
|
+
* bootstrapped spines so a migration-name or partitioning drift — invisible
|
|
121
|
+
* to the schema snapshot — is a gated fact, not a latent interop break.
|
|
122
|
+
*/
|
|
123
|
+
export declare const MigrationTrackerEntry: z.ZodObject<{
|
|
124
|
+
namespace: z.ZodString;
|
|
125
|
+
name: z.ZodString;
|
|
126
|
+
sequence: z.ZodNumber;
|
|
127
|
+
}, z.core.$strip>;
|
|
128
|
+
export type MigrationTrackerEntry = z.infer<typeof MigrationTrackerEntry>;
|
|
129
|
+
/**
|
|
130
|
+
* The full `schema_version` tracker as a deterministically-ordered list,
|
|
131
|
+
* wrapped in an object so it round-trips cleanly as a JSON-RPC result.
|
|
132
|
+
* Sorted by `(namespace, sequence)` on capture.
|
|
133
|
+
*/
|
|
134
|
+
export declare const MigrationTracker: z.ZodObject<{
|
|
135
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
136
|
+
namespace: z.ZodString;
|
|
137
|
+
name: z.ZodString;
|
|
138
|
+
sequence: z.ZodNumber;
|
|
139
|
+
}, z.core.$strip>>;
|
|
140
|
+
}, z.core.$strip>;
|
|
141
|
+
export type MigrationTracker = z.infer<typeof MigrationTracker>;
|
|
142
|
+
/**
|
|
143
|
+
* Read every `schema_version` row into a deterministic `MigrationTracker`.
|
|
144
|
+
*
|
|
145
|
+
* The migration-identity twin of `query_schema_snapshot`: that captures the
|
|
146
|
+
* resulting schema (and excludes this tracker); this captures the tracker
|
|
147
|
+
* rows themselves, so the cross-impl gate can assert the two spines record
|
|
148
|
+
* byte-identical migration identity.
|
|
149
|
+
*/
|
|
150
|
+
export declare const query_migration_tracker: (db: Db) => Promise<MigrationTracker>;
|
|
112
151
|
/** Filter options for `query_schema_snapshot`. */
|
|
113
152
|
export interface QuerySchemaSnapshotOptions {
|
|
114
153
|
/**
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;GAsBG;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;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;iBAG3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;iBAOzB,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;AA8DD;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,IAAI,EAAE,EACN,UAAS,0BAA+B,KACtC,OAAO,CAAC,cAAc,CAyHxB,CAAC"}
|
|
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;;;;;;;;;;;;;;;;;;;;;;GAsBG;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;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;iBAG3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;iBAOzB,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB;;;;iBAOhC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;iBAE3B,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,gBAAgB,CAa9E,CAAC;AAEF,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;AA8DD;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,IAAI,EAAE,EACN,UAAS,0BAA+B,KACtC,OAAO,CAAC,cAAc,CAyHxB,CAAC"}
|
|
@@ -81,6 +81,53 @@ export const SchemaSnapshot = z.object({
|
|
|
81
81
|
/** Enum types (`CREATE TYPE ... AS ENUM`) keyed by name; labels in declared order. */
|
|
82
82
|
enums: z.record(z.string(), EnumTypeSnapshot),
|
|
83
83
|
});
|
|
84
|
+
/**
|
|
85
|
+
* A single `schema_version` tracker row — the migration-identity primitive.
|
|
86
|
+
*
|
|
87
|
+
* Where `SchemaSnapshot` is provenance-agnostic (it captures the resulting
|
|
88
|
+
* tables and deliberately *excludes* the tracker), this **is** the tracker:
|
|
89
|
+
* the `(namespace, name, sequence)` the migration runner records per applied
|
|
90
|
+
* migration. `sequence` carries order; `name` carries identity (the PK is
|
|
91
|
+
* `(namespace, name)`). The cross-impl gate diffs these between the two
|
|
92
|
+
* bootstrapped spines so a migration-name or partitioning drift — invisible
|
|
93
|
+
* to the schema snapshot — is a gated fact, not a latent interop break.
|
|
94
|
+
*/
|
|
95
|
+
export const MigrationTrackerEntry = z.object({
|
|
96
|
+
/** Migration namespace (e.g. `fuz_cell`, `fuz_cell_history`). */
|
|
97
|
+
namespace: z.string(),
|
|
98
|
+
/** Migration name within the namespace (e.g. `full_cell_schema`). */
|
|
99
|
+
name: z.string(),
|
|
100
|
+
/** Per-namespace apply order, starting at 0. */
|
|
101
|
+
sequence: z.number().int(),
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* The full `schema_version` tracker as a deterministically-ordered list,
|
|
105
|
+
* wrapped in an object so it round-trips cleanly as a JSON-RPC result.
|
|
106
|
+
* Sorted by `(namespace, sequence)` on capture.
|
|
107
|
+
*/
|
|
108
|
+
export const MigrationTracker = z.object({
|
|
109
|
+
entries: z.array(MigrationTrackerEntry),
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* Read every `schema_version` row into a deterministic `MigrationTracker`.
|
|
113
|
+
*
|
|
114
|
+
* The migration-identity twin of `query_schema_snapshot`: that captures the
|
|
115
|
+
* resulting schema (and excludes this tracker); this captures the tracker
|
|
116
|
+
* rows themselves, so the cross-impl gate can assert the two spines record
|
|
117
|
+
* byte-identical migration identity.
|
|
118
|
+
*/
|
|
119
|
+
export const query_migration_tracker = async (db) => {
|
|
120
|
+
const rows = await db.query(`SELECT namespace, name, sequence
|
|
121
|
+
FROM schema_version
|
|
122
|
+
ORDER BY namespace ASC, sequence ASC`);
|
|
123
|
+
return {
|
|
124
|
+
entries: rows.map((r) => ({
|
|
125
|
+
namespace: r.namespace,
|
|
126
|
+
name: r.name,
|
|
127
|
+
sequence: r.sequence,
|
|
128
|
+
})),
|
|
129
|
+
};
|
|
130
|
+
};
|
|
84
131
|
const sort_keys = (record) => {
|
|
85
132
|
const sorted = {};
|
|
86
133
|
for (const key of Object.keys(record).sort()) {
|
|
@@ -39,7 +39,7 @@ import './assert_dev_env.js';
|
|
|
39
39
|
*
|
|
40
40
|
* @module
|
|
41
41
|
*/
|
|
42
|
-
import type { ColumnSnapshot, SchemaSnapshot } from './schema_introspect.js';
|
|
42
|
+
import type { ColumnSnapshot, MigrationTracker, SchemaSnapshot } from './schema_introspect.js';
|
|
43
43
|
/** Structured drift entry. `where` is the named source impl ('a' or 'b'). */
|
|
44
44
|
export type SchemaDiff = {
|
|
45
45
|
readonly kind: 'table_only_in';
|
|
@@ -139,4 +139,45 @@ export declare const format_schema_diffs: (diffs: ReadonlyArray<SchemaDiff>, lab
|
|
|
139
139
|
* ```
|
|
140
140
|
*/
|
|
141
141
|
export declare const assert_schema_snapshots_equal: (a: SchemaSnapshot, b: SchemaSnapshot, labels?: SchemaDiffLabels) => void;
|
|
142
|
+
/**
|
|
143
|
+
* Structured migration-identity drift entry. Keyed on `(namespace, name)` —
|
|
144
|
+
* the `schema_version` PK — so a name rename and a partitioning change both
|
|
145
|
+
* surface as `tracker_row_only_in`, and a re-order surfaces as
|
|
146
|
+
* `tracker_sequence_differs`.
|
|
147
|
+
*/
|
|
148
|
+
export type MigrationTrackerDiff = {
|
|
149
|
+
readonly kind: 'tracker_row_only_in';
|
|
150
|
+
readonly where: 'a' | 'b';
|
|
151
|
+
readonly namespace: string;
|
|
152
|
+
readonly name: string;
|
|
153
|
+
} | {
|
|
154
|
+
readonly kind: 'tracker_sequence_differs';
|
|
155
|
+
readonly namespace: string;
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly a: number;
|
|
158
|
+
readonly b: number;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Structural diff between two migration trackers — empty array means the two
|
|
162
|
+
* spines recorded byte-identical migration identity. Keyed on
|
|
163
|
+
* `(namespace, name)`; sequence mismatches on a shared key are reported too.
|
|
164
|
+
* Deterministic order: shared/missing rows in sorted `(namespace, name)` order.
|
|
165
|
+
*/
|
|
166
|
+
export declare const diff_migration_trackers: (a: MigrationTracker, b: MigrationTracker) => Array<MigrationTrackerDiff>;
|
|
167
|
+
/**
|
|
168
|
+
* Render migration-tracker diffs as a human-readable multi-line string.
|
|
169
|
+
* Empty diffs produce an empty string.
|
|
170
|
+
*/
|
|
171
|
+
export declare const format_migration_tracker_diffs: (diffs: ReadonlyArray<MigrationTrackerDiff>, labels?: SchemaDiffLabels) => string;
|
|
172
|
+
/**
|
|
173
|
+
* Throw if the two spines' `schema_version` trackers disagree — the gate for
|
|
174
|
+
* the swap-freely invariant (any consumer can swap TS↔Rust over one DB without
|
|
175
|
+
* re-bootstrapping). This catches what `assert_schema_snapshots_equal` is blind
|
|
176
|
+
* to by design: the snapshot excludes the tracker, so a migration-name or
|
|
177
|
+
* partitioning divergence that yields an identical *schema* (e.g. `cell_v0` vs
|
|
178
|
+
* `full_cell_schema`, or `cell_history` bundled vs isolated) passes schema
|
|
179
|
+
* parity but breaks the runner's positional name-prefix check at boot
|
|
180
|
+
* (`name-divergence-at-N`). The error names the impls and lists every diff.
|
|
181
|
+
*/
|
|
182
|
+
export declare const assert_migration_trackers_equal: (a: MigrationTracker, b: MigrationTracker, labels?: SchemaDiffLabels) => void;
|
|
142
183
|
//# sourceMappingURL=schema_parity.d.ts.map
|
|
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EACX,cAAc,EAEd,cAAc,EAGd,MAAM,wBAAwB,CAAC;AAEhC,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GACnB;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAC,GACnF;IACA,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACvB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,cAAc,CAAC;IACrC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC;CACnB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACtB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CAClB,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC3B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC;IAC/C,QAAQ,CAAC,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC;CAC9C,GACD;IAAC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,GACzF;IACA,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CAClB,GACD;IAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAC,GACtF;IACA,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,QAAQ,CAAC,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACjC,CAAC;AAEL;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,GAAG,cAAc,EAAE,GAAG,cAAc,KAAG,KAAK,CAAC,UAAU,CAiD5F,CAAC;AAqHF,qEAAqE;AACrE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC/B,OAAO,aAAa,CAAC,UAAU,CAAC,EAChC,SAAQ,gBAAqB,KAC3B,MAoEF,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,6BAA6B,GACzC,GAAG,cAAc,EACjB,GAAG,cAAc,EACjB,SAAQ,gBAAqB,KAC3B,IAUF,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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"}
|
|
@@ -225,3 +225,94 @@ export const assert_schema_snapshots_equal = (a, b, labels = {}) => {
|
|
|
225
225
|
const label_b = labels.b ?? 'b';
|
|
226
226
|
throw new Error(`Schema parity failed: ${diffs.length} diff(s) between ${label_a} and ${label_b}\n${format_schema_diffs(diffs, labels)}`);
|
|
227
227
|
};
|
|
228
|
+
const tracker_key = (namespace, name) => `${namespace}${name}`;
|
|
229
|
+
/**
|
|
230
|
+
* Structural diff between two migration trackers — empty array means the two
|
|
231
|
+
* spines recorded byte-identical migration identity. Keyed on
|
|
232
|
+
* `(namespace, name)`; sequence mismatches on a shared key are reported too.
|
|
233
|
+
* Deterministic order: shared/missing rows in sorted `(namespace, name)` order.
|
|
234
|
+
*/
|
|
235
|
+
export const diff_migration_trackers = (a, b) => {
|
|
236
|
+
const diffs = [];
|
|
237
|
+
const a_by_key = new Map(a.entries.map((e) => [tracker_key(e.namespace, e.name), e]));
|
|
238
|
+
const b_by_key = new Map(b.entries.map((e) => [tracker_key(e.namespace, e.name), e]));
|
|
239
|
+
// Union keyed by `(namespace, name)`; the value is a reference entry (from
|
|
240
|
+
// whichever side has it) used to recover namespace/name for the diff — so a
|
|
241
|
+
// row present on only one side needs no non-null assertion.
|
|
242
|
+
const by_key = new Map([...a_by_key, ...b_by_key]);
|
|
243
|
+
for (const [key, ref] of [...by_key].sort((x, y) => (x[0] < y[0] ? -1 : x[0] > y[0] ? 1 : 0))) {
|
|
244
|
+
const ea = a_by_key.get(key);
|
|
245
|
+
const eb = b_by_key.get(key);
|
|
246
|
+
if (!ea) {
|
|
247
|
+
diffs.push({
|
|
248
|
+
kind: 'tracker_row_only_in',
|
|
249
|
+
where: 'b',
|
|
250
|
+
namespace: ref.namespace,
|
|
251
|
+
name: ref.name,
|
|
252
|
+
});
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (!eb) {
|
|
256
|
+
diffs.push({
|
|
257
|
+
kind: 'tracker_row_only_in',
|
|
258
|
+
where: 'a',
|
|
259
|
+
namespace: ref.namespace,
|
|
260
|
+
name: ref.name,
|
|
261
|
+
});
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (ea.sequence !== eb.sequence) {
|
|
265
|
+
diffs.push({
|
|
266
|
+
kind: 'tracker_sequence_differs',
|
|
267
|
+
namespace: ea.namespace,
|
|
268
|
+
name: ea.name,
|
|
269
|
+
a: ea.sequence,
|
|
270
|
+
b: eb.sequence,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return diffs;
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Render migration-tracker diffs as a human-readable multi-line string.
|
|
278
|
+
* Empty diffs produce an empty string.
|
|
279
|
+
*/
|
|
280
|
+
export const format_migration_tracker_diffs = (diffs, labels = {}) => {
|
|
281
|
+
if (diffs.length === 0)
|
|
282
|
+
return '';
|
|
283
|
+
const label_a = labels.a ?? 'a';
|
|
284
|
+
const label_b = labels.b ?? 'b';
|
|
285
|
+
const lines = [];
|
|
286
|
+
for (const d of diffs) {
|
|
287
|
+
switch (d.kind) {
|
|
288
|
+
case 'tracker_row_only_in':
|
|
289
|
+
lines.push(` migration ${d.namespace}/${d.name} only in ${d.where === 'a' ? label_a : label_b}`);
|
|
290
|
+
break;
|
|
291
|
+
case 'tracker_sequence_differs':
|
|
292
|
+
lines.push(` migration ${d.namespace}/${d.name} sequence differs: ${label_a}=${d.a}, ${label_b}=${d.b}`);
|
|
293
|
+
break;
|
|
294
|
+
default:
|
|
295
|
+
d;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return lines.join('\n');
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Throw if the two spines' `schema_version` trackers disagree — the gate for
|
|
303
|
+
* the swap-freely invariant (any consumer can swap TS↔Rust over one DB without
|
|
304
|
+
* re-bootstrapping). This catches what `assert_schema_snapshots_equal` is blind
|
|
305
|
+
* to by design: the snapshot excludes the tracker, so a migration-name or
|
|
306
|
+
* partitioning divergence that yields an identical *schema* (e.g. `cell_v0` vs
|
|
307
|
+
* `full_cell_schema`, or `cell_history` bundled vs isolated) passes schema
|
|
308
|
+
* parity but breaks the runner's positional name-prefix check at boot
|
|
309
|
+
* (`name-divergence-at-N`). The error names the impls and lists every diff.
|
|
310
|
+
*/
|
|
311
|
+
export const assert_migration_trackers_equal = (a, b, labels = {}) => {
|
|
312
|
+
const diffs = diff_migration_trackers(a, b);
|
|
313
|
+
if (diffs.length === 0)
|
|
314
|
+
return;
|
|
315
|
+
const label_a = labels.a ?? 'a';
|
|
316
|
+
const label_b = labels.b ?? 'b';
|
|
317
|
+
throw new Error(`Migration-identity parity failed: ${diffs.length} diff(s) between ${label_a} and ${label_b}\n${format_migration_tracker_diffs(diffs, labels)}`);
|
|
318
|
+
};
|