Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/zod-mirror-parity-population-5684.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
---

Test-infrastructure only, no shipped code touched: the derived anti-drift parity
construction that objectui#5680 built for `BaseSchema` now covers every hand-written
zod mirror in `@object-ui/types`'s `src/zod/` directory instead of that one schema.

A mirror restates a TypeScript declaration by hand, and when the declaration widens
and the mirror does not follow, the published validator refuses a spelling the
published types invite — `declared !== enforced` on a shipped surface. Two instances
were found independently before anything looked for them (objectui#4605, #5186);
nothing detected the class itself.

`packages/types/src/__tests__/zod-mirror-parity.test.ts` registers 164 mirror/declaration
pairs and applies one construction to all of them: it reads each mirror's own `.shape`
and compares every key against the declaration, so a widening that forgets a mirror
turns red with no key list to maintain. A runtime census reads the directory off disk
and fails when an exported const is in neither the registry nor the reasoned exclusion
list, so a mirror added later cannot join the population silently. 18 pairs carry
measured drift today and are pinned to their exact drifted key sets as a ratchet — new
drift fails, and so does a stale entry once the drift is fixed.

`base-schema-zod-mirror-parity.test.ts` keeps its objectui#4605 runtime pins and no
longer restates the type-level construction; `BaseSchema` is one registered row.
70 changes: 14 additions & 56 deletions packages/types/src/__tests__/base-schema-zod-mirror-parity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,69 +59,27 @@
*/

import { describe, it, expect } from 'vitest';
import type { z } from 'zod';
import { BaseSchema as Mirror } from '../zod/base.zod.js';
import type { BaseSchema } from '../base';

/* ── Type-level helpers ──────────────────────────────────────────────────── */

/** Invariant equality — `extends` both ways would accept a narrowing. */
type Equal< A, B > =
(< T >() => T extends A ? 1 : 2) extends (< T >() => T extends B ? 1 : 2) ? true : false;
type Expect< T extends true > = T;

/* ── The derived parity invariant ────────────────────────────────────────── */

/** The mirror's DECLARED keys, read from its own shape. */
type MirroredKeys = keyof typeof Mirror.shape & string;

/** What the mirror ACCEPTS for key `K` (input side, so `.optional()` shows). */
type Accepts< K extends MirroredKeys > = z.input< (typeof Mirror.shape)[K] >;

/**
* Every key whose DECLARED type the mirror would refuse. Must be `never`.
*
* The tuple wrappers keep the check non-distributive: `BaseSchema['visible']`
* is a union, and a bare `extends` would ask the question limb-by-limb and
* pass as long as ONE limb fit.
*/
type NarrowerThanDeclared = {
[K in MirroredKeys]: [BaseSchema[K]] extends [Accepts< K >] ? never : K
}[MirroredKeys];

/** The invariant this card exists to establish. */
export type assertionMirrorIsNotNarrower = Expect<
Equal< NarrowerThanDeclared, never >
>;
/* ── The derived parity invariant lives in the population guard ──────────── */

/**
* Non-vacuity guard for the pin above.
* The type-level pin this card introduced — read the mirror's OWN `.shape` and
* compare each key against the declaration, so the next widening that forgets
* this file turns red with no key list to maintain — now lives in
* `./zod-mirror-parity.test.ts` (objectui#5684), which applies that one
* construction to every registered mirror in `../zod/`.
*
* If `MirroredKeys` ever resolved to `never` — the `.passthrough()` failure
* mode, or a refactor that stops exposing `.shape` — the mapped type would be
* `never` and `assertionMirrorIsNotNarrower` would pass while enforcing
* nothing. This asserts the six keys are really reachable through `.shape`.
*/
export type assertionShapeKeysResolve = Expect<
Equal<
Exclude< 'type' | 'label' | 'description' | 'visible' | 'disabled' | 'ariaLabel', MirroredKeys >,
never
>
>;

/**
* The OTHER half of that guard — `MirroredKeys` must be a union of LITERALS.
* `BaseSchema` is a registered row there (`'base.zod.ts#BaseSchema'`), so the
* invariant is unchanged and still enforced by `tsc -p tsconfig.test.json`; it
* is simply not restated here. Its two non-vacuity guards travelled with it:
* `assertionBaseSchemaKeysResolve` keeps this card's six-key pin by name, and
* `assertionNoVacuousEntry` generalises the bare-`string` degeneration check
* across the whole population.
*
* The guard above catches `MirroredKeys` degenerating to `never`; it cannot
* catch it degenerating to bare `string`, because every literal is `Exclude`d
* by `string` and the guard would stay green. That is not a hypothetical
* shape: `keyof z.input<typeof Mirror>` IS `string` here, since
* `.passthrough()` puts an index signature on the inferred type. So this pins
* that a key the mirror does NOT declare stays outside the union.
* What stays here is what is specific to #4605: the spellings the OLD mirror
* really did refuse, and the two i18n vocabularies that must not cross.
*/
export type assertionShapeKeysAreLiteral = Expect<
Equal< Exclude< 'notAMirroredKey_4605', MirroredKeys >, 'notAMirroredKey_4605' >
>;

/* ── Runtime: the spellings the OLD mirror refused ───────────────────────── */

Expand Down
Loading
Loading