Skip to content

refactor(web_core): resolve catalog definitions before building Zod - #28

Closed
louis-bompart wants to merge 6 commits into
claude/zod-scope/catalog-schema-compiler-v2from
claude/zod-scope/catalog-definition-resolution
Closed

louis-bompart wants to merge 6 commits into
claude/zod-scope/catalog-schema-compiler-v2from
claude/zod-scope/catalog-definition-resolution

Conversation

@louis-bompart

@louis-bompart louis-bompart commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR is Layer 2 of the loader work: the loader resolves a catalog entry's definition without Zod, then converts the result to Zod. It changes no public API.

Reference resolution (catalog_references.ts):

  • JSON Pointer traversal, peer-target envelope handling and local/peer $ref inlining moved here from schema_loader.ts.
  • resolveDefinition() resolves the references an entry schema reaches without flattening them. It returns the schema with its named references kept, every local or peer definition it reaches bundled under defs, and the names of the root catalog's own definitions:
    • A root $defs entry keeps its name and its #/$defs/... reference.
    • A definition in a peer document, including one reached through a peer's own local reference, is bundled as peer_<n>_<name>, and its reference is rewritten to point there.
    • A root component target is bundled as component_<name>. Bundled names never reuse a root definition name.
    • Common types stay external references, and recursion stays a reference cycle in defs.
    • Two options keep the loader's behavior. A component mixed in from the same catalog keeps its envelope. A reference outside the root catalog's own $defs whose definition name is a common type stays that common type.
  • inlineResolvedDefinition() inlines a resolved definition for the consumers that still need flat schemas, and inlinePeerAndLocalRefs now resolves, then inlines. Sibling keywords beside an inlined reference are merged: object keywords shallowly, with the sibling winning, and arrays as a union.

Object resolution (new catalog_definitions.ts):

  • resolveComponentDefinition(), resolveFunctionArgumentsDefinition() and resolveThemeDefinition() resolve a definition into the object it validates:
    • its pieces in override order: common-type mixins, then referenced definitions, then inline allOf members, then own properties;
    • the required names;
    • the omitted envelope fields;
    • whether the object is open.
  • The rule that adds metadata to ComponentCommon from v1.0 lives here, as do the closure rules for components, themes and function arguments.

Zod building (schema_loader.ts):

  • The loader now only translates that model into Zod, against the entry's bundle, so it no longer resolves peers.
  • Error messages are unchanged.

Layer 3 comes next: keep each loaded entry's resolved definition, and derive the binding views from it instead of from the compiled Zod schema.

Edge cases that can differ

None of these appears in any catalog in the repository:

  • Inside a peer catalog, an allOf mixin that points at the peer's own definition named after a common type now uses the common type.
  • A peer component mixed in twice through allOf contributes once. The result is the same.
  • An unresolvable cross-catalog reference inside if/then/else now fails at load. Before, the loader ignored it.

Validation

  • yarn build
  • node --test "dist/**/*.test.js": 1278 passing, 0 failing, 21 todo
  • node tests/conformance/conformance_test.mjs: all vectors passing
  • yarn lint: 0 errors
  • yarn format:check
  • yarn test in typescript/catalogs/{mcp,vegachart}, typescript/renderers/{lit,react} and typescript/agents/a2ui_agent. Angular passes apart from test:closure-compiler, which needs Java 21.
  • The 61 catalog documents in the repository were each loaded as declared, at v0.9, at v1.0, and linked to each other. Every component, function and theme converts to the same Zod schema as before, compared through its JSON Schema view (1,790 entries), with the same 30 load errors.
  • inlinePeerAndLocalRefs output for every entry of the basic v0.9 and v1.0 catalogs and vegachart v0.9 and v1.0, in both modes, is byte-identical to the output before this PR (132 outputs).
  • New tests:
    • every basic v0.9 and v1.0 catalog entry, resolved and inlined;
    • peer-local and root-local definitions that share a name;
    • same-named definitions of two peers, and a peer that references a second peer;
    • root component naming, and the sibling-merge rule;
    • piece order, the ComponentCommon version rule, and the closure rules;
    • peer component mixins, peer definitions named after common types, and function-argument gathering.

Part of a2ui-project#2160

louis-bompart and others added 6 commits October 27, 2026 23:40
Check that every basic v0.9 and v1.0 catalog entry resolves to itself with
its local definitions bundled, and that inlining the result matches the
loader's inlining. Cover same-named definitions of two peers, a peer that
references a second peer, and the sibling-keyword merge rule, which the
inliner's documentation now states.

Name a bundled root component target `component_<name>` without reusing a
root definition name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Move the loader's resolution of components, function arguments and themes
into the Zod-neutral catalog_definitions module. Each resolves its
definition with resolveDefinition, then into an object model: its pieces in
override order (common-type mixins, referenced definitions, inline allOf
members, own properties), the required names, the omitted envelope fields
and whether the object is open. The ComponentCommon version rule and the
component, theme and function-argument closure rules live there too.

The Zod builder now only translates that model, against the entry's bundle,
so it no longer resolves peers. Two resolveDefinition options keep the
loader's behavior: a component mixin from the same catalog keeps its
envelope, and a reference outside the root catalog's own $defs whose
definition name is a common type stays that common type.

Every entry of the 61 catalog documents in the repository, loaded alone, at
v0.9 and v1.0, and linked to each other, converts to the same Zod schema
as before, with the same load errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@louis-bompart louis-bompart changed the title refactor(web_core): move catalog reference inlining refactor(web_core): resolve catalog definitions before building Zod Oct 28, 2026
@louis-bompart

Copy link
Copy Markdown
Collaborator Author

Superseded by the regrouped stack on upstream main: this change is now part of #50 (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