Skip to content

feat(web_core): write the v1.0 basic catalog with Zod 4 - #34

Closed
louis-bompart wants to merge 1 commit into
claude/zod-scope/library-spec-annotationsfrom
claude/zod-scope/basic-v1-zod4
Closed

louis-bompart wants to merge 1 commit into
claude/zod-scope/library-spec-annotationsfrom
claude/zod-scope/basic-v1-zod4

Conversation

@louis-bompart

Copy link
Copy Markdown
Collaborator

Description

This PR completes the original proof, stacked on #33: the v1.0 basic catalog is now written with Zod 4. Its components and functions are generated as Zod 4 schemas annotated with specMeta(), on Zod 4 versions of the v1.0 common types. basicCatalog.toJson() and basicCatalog.validationSchema are byte-identical to before, so it still publishes the same catalog.json.

Generators.

  • The catalog generator (scripts/generate-catalog-schemas.mjs) takes a zod option. With 'v4' it:

    • imports zod/v4 and specMeta;
    • writes a reference to a common type as X.meta({$ref: '#/$defs/X', description}), instead of X.describe('REF:#/$defs/X|…');
    • writes an annotation as .meta(specMeta({...})) instead of spec(..., {...});
    • writes a component as z.strictObject, and a record with an explicit key schema.

    It rejects a catalog mixin model such as v0.9's CatalogComponentCommon, which metadata can only name. v1.0 has none. The rest of the generator, including how it predicts what the shaper derives, is shared with Zod 3. refactor(web_core): shape catalog documents from the JSON Schema view #32 and feat(web_core): annotate library schemas with specMeta #33 made the shaper read both the same way.

  • The v1.0 common-types generator also writes src/v1_0/zod4/common-types.ts, using a new toZod4Code in zod-generator-core.mjs. These are the same compiled types, except that they:

    • import zod/v4;
    • name their definitions with .meta({$ref}) instead of REF: markers, so markChildRef isn't needed, since views find child references by name;
    • give Child its own $ref instead of aliasing ComponentId.

    The module is internal for now. The v1.0 common types that @a2ui/web_core/v1_0 exports, and the protocol's own message schemas, stay Zod 3.

Two Zod 4 behaviours the generators handle.

  • Metadata is inherited. A reference with no description of its own sets description: undefined, to clear the common type's. Without that, Button.action and others would gain a description the document doesn't have.
  • Prettier needs two passes on the rewritten method chains. The generator formats zod4/common-types.ts to a fixed point, so the build's single Prettier pass leaves it unchanged and format:check passes after a clean build.

What changes for consumers (marked breaking in the changelog).

  • Exported basic v1.0 entries are Zod 4 schemas. Code that composes them with Zod 3 schemas, or calls Zod 3-only methods on them, must use Zod 4. Nothing in the repository does: renderers, samples and tools use them through A2UI's interfaces, .parse or .safeParse.
  • Zod 4 applies the default of an optional property, so a component's own schema now outputs those defaults. For example, Divider validated with {} yields {axis: 'horizontal'}. The core doesn't use a component's output. No basic function has an argument with a default, so what functions receive is unchanged.
  • Binding and validation messages are unchanged, because they come from the catalog's document, which is unchanged.

The fromZod3 parity test now wraps the v0.9 basic catalog, which is still written with Zod 3 and spec(), catalog mixins included.

Validation

  • yarn build from clean, including all generators
  • node --test "dist/**/*.test.js": 1430 passing, 0 failing, 42 todo (the known cases)
  • node tests/conformance/conformance_test.mjs: all vectors passing
  • yarn lint: 0 errors, no new warnings
  • yarn format:check after a clean build
  • yarn test in typescript/catalogs/{mcp,vegachart}, typescript/renderers/{lit,react,markdown-it} and typescript/agents/a2ui_agent. Angular passes apart from test:closure-compiler, which needs Java 21.
  • Documents. The existing test that basicCatalog.toJson() equals the published catalogs/basic/v1/catalog.json passes. Beyond that, the toJson() and validationSchema of the basic v0.8, v0.9 and v1.0 catalogs and the vegachart catalogs are byte-identical to before, key order included.
  • Unchanged across every catalog document in the repository:
    • the compiled Zod schemas (1,790 entries);
    • the binding views (5,625 results);
    • the validation results and messages (10,497 payloads), except the one Divider default described above.

Part of a2ui-project#2160

Pre-launch Checklist

One time:

For this PR:

  • I have updated the relevant CHANGELOG.md file.
  • I updated/added relevant documentation.
  • My code changes (if any) have tests.
  • If my branch is on a fork, I have verified that scripts/e2e_test.sh passes.

🤖 Generated with Claude Code

The v1.0 basic catalog's components and functions are now generated as
Zod 4 schemas annotated with specMeta(), on Zod 4 versions of the v1.0
common types. basicCatalog.toJson() and validationSchema are byte-identical
to before, so the catalog still publishes the same catalog.json.

The catalog generator takes a zod option. With 'v4' it imports zod/v4 and
specMeta, writes a reference to a common type as
X.meta({$ref, description}), clearing an inherited description when the
reference has none of its own, an annotation as .meta(specMeta(...)), a
component as a z.strictObject, and a record with an explicit key schema.
It rejects a catalog mixin model, which metadata cannot name.

The v1.0 common types generator also writes src/v1_0/zod4/common-types.ts
with toZod4Code: the same compiled types importing zod/v4, naming their
definitions with .meta({$ref}) instead of REF: markers, without
markChildRef, and with Child naming its own definition. It formats the
file to a fixed point, which Prettier reaches in two passes. The common
types @a2ui/web_core/v1_0 exports, and the protocol's schemas, stay Zod 3.

The fromZod3 parity test now wraps the v0.9 basic catalog, which is still
written with Zod 3 and spec().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@louis-bompart

Copy link
Copy Markdown
Collaborator Author

Superseded by the regrouped stack on upstream main: this change is now part of #52 (series #45 to #54). The branch is kept for reference.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant