Skip to content

feat(web_core)!: resolve catalogId overrides through per-surface catalogs - #2715

Open
gspencergoog wants to merge 3 commits into
v1_0from
v1_0_ts_multicatalog_parity
Open

gspencergoog wants to merge 3 commits into
v1_0from
v1_0_ts_multicatalog_parity

Conversation

@gspencergoog

Copy link
Copy Markdown
Collaborator

Summary

Aligns the TypeScript SDK (web_core) with Python's multi-catalog model by introducing per-surface catalog scoping and routing catalogId overrides for function calls and components. It also fixes two child-reference resolution defects where structured array sub-properties were either over-extracted as dangling references or omitted from catalog schemas entirely.

Changes

  • Multi-catalog routing: Added availableCatalogs: ReadonlyMap<string, Catalog> to SurfaceModel, populated during createSurface by filtering registered catalogs to those with compatible protocol versions. Component and function-call lookups now resolve through this map rather than assuming a single global catalog.
  • Function call catalog scoping: Plumbed catalogId through DataContext.resolveFunctionCatalog and evaluateFunctionReactive. When a function call specifies a catalogId, its arguments are validated against that specific catalog and executed using that catalog's invoker. Calls specifying an unresolved catalog raise A2uiCatalogError.
  • Action payload dispatch: Added catalogId to ActionPayload in SurfaceModel.dispatchAction when the triggering event payload names a catalog explicitly.
  • Error model: Added A2uiCatalogError (inheriting from A2uiError, default code 'CATALOG_ERROR') in src/errors.ts to represent catalog loading, registration, and resolution failures.
  • Catalog constructor: Reordered parameters to new Catalog(id, protocolVersion, components, functions, ...) to make protocolVersion required and positional, matching Python's constructor order. Updated all call sites and test suites across the repository.
  • Catalog schema loading: Updated loadCatalogFromSchema and Catalog.fromSchema to accept an optional protocolVersion parameter for pre-v1.0 catalogs that lack an inline version field.
  • Structured array child-reference extraction (bug fix): Added nestedRefs?: Readonly<Record<string, ReadonlySet<string>>> to ComponentRefMap and updated inspectShapeField / extractPointers. In structured array items (e.g., Tabs.tabs: [{title, child}]), only sub-keys declared by the catalog as child references are extracted; sibling string values (such as tab titles) are no longer treated as component IDs.
  • Inline object schema loading (bug fix): Fixed convertPropertyToZod in schema_loader.ts to preserve properties for inline type: object definitions rather than collapsing them to z.record(z.unknown()).
  • Superset schema generation: Updated scripts/generate-superset-common-types.mjs to inline allOf references so that catalogId from v1.0 FunctionCall is included in the generated superset FunctionCallSchema.
  • Conformance tests: Added two cross-SDK conformance tests:
    • test_topology_structured_array_child_ref_ignores_sibling_strings in conformance/core/message_processor_v0_9.yaml
    • test_multi_catalog_function_call_catalog_id_override in conformance/core/multi_catalog.yaml

Impact & Risks

  • Breaking change (Catalog constructor): Catalog now requires protocolVersion as its second argument (Catalog(id, protocolVersion, ...)). External custom catalogs must pass their protocol version explicitly.
  • Breaking change (SurfaceModel catalog access): SurfaceModel.catalog was renamed to defaultCatalog to reflect that surfaces support multiple available catalogs. A deprecated getter catalog is retained for backwards compatibility and will be removed in a subsequent release.
  • Breaking change (SurfaceModel constructor): SurfaceModel now accepts availableCatalogs as its third parameter, shifting optional parameters theme, sendDataModel, and dataModel. All in-tree callers and test mocks have been updated.

Testing

  1. Run unit tests in web_core:
    yarn workspace @a2ui/web_core test
  2. Run conformance tests across both v0.9 and v1.0 vectors:
    node typescript/web_core/tests/conformance/conformance_test.mjs
  3. Run builds and tests across all TypeScript workspaces:
    yarn workspaces foreach -A -t run build
    yarn workspaces foreach -A -p -j 8 run test
  4. Verify linting and license headers:
    yarn workspaces foreach -A -p -j 8 run lint
    ./scripts/fix_licenses.py --check

…logs

TypeScript accepted a `catalogId` on a component but had nowhere to put one
on a function call, and had no notion of which catalogs a given surface may
address. This brings it in line with Python's multi-catalog model.

`SurfaceModel` now carries `availableCatalogs`, the subset of registered
catalogs whose protocol version is compatible with the surface's own.
`MessageProcessor` populates it on `createSurface`, and component and
function lookups resolve through it. A function call naming a catalog the
surface cannot reach raises `A2uiCatalogError`, and its arguments are
validated against the catalog that will run it rather than against the
surface default.

Two reference-resolution bugs fall out of the same work:

- The integrity checker treated every string inside a structured array item
  as a component reference, so `Tabs.tabs: [{title, child}]` failed with
  `Dangling reference 'Overview'` where Python passed. `ComponentRefMap` now
  records which item sub-keys the catalog declares as children, mirroring
  Python's `nested_refs`.
- `schema_loader` collapsed any inline `type: object` into
  `z.record(z.unknown())`, discarding its properties, so a child reference
  nested inside an array item was invisible to the reference map for every
  JSON-loaded catalog.

Also adds `A2uiCatalogError` and puts `catalogId` on the superset
`FunctionCall` schema. The field was already in the v1.0 schema; the superset
generator missed it because v1.0's `FunctionCall` has no direct `properties`,
only an `allOf`, which the generator now inlines.

Two conformance cases bind the other SDKs to both behaviours: a `catalogId`
override on a function call, and a structured-array child reference beside a
plain string sibling.

BREAKING-CHANGE: `Catalog`'s constructor takes `protocolVersion` as its
second argument and requires it, matching Python's ordering.
`Catalog.fromSchema` and `loadCatalogFromSchema` take an optional
`protocolVersion` for catalog schemas published before v1.0, which omit the
field.

BREAKING-CHANGE: `SurfaceModel.catalog` is renamed to `defaultCatalog`, and
`availableCatalogs` is inserted as the third constructor argument. `catalog`
remains as a deprecated getter and will be removed in a future release.
gemini-code-assist[bot]

This comment was marked as resolved.

- Defensively fall back to surface.catalog in DataContext constructor and
  resolveFunctionCatalog when surface.defaultCatalog is undefined, and use
  optional chaining for surface.availableCatalogs.
- Support both single child references and child lists within structured
  array items in inspectRawProperties.
- Pass a cloned seen set down each recursive branch in inlineAllOfRefs to
  support diamond dependencies while guarding recursion cycles.
- Forward available_catalogs through component validation in Python
  MessageProcessor and PayloadValidator, resolving nested function calls
  with catalogId overrides.
- Revert samples/community modifications to maintain compatibility with the
  published @a2ui/web_core packages used by standalone community samples.
- Add regression tests in web_core and a2ui_core.
- In TypeScript reference-map.ts, call addNestedRef when subRes.isChildList matches in both inspectShapeField and inspectRawProperties.
- In integrity-checker.ts, recursively delegate to extractPointers for nestedKeys sub-keys rather than assuming single string child references.
- In Python reference_map.py, include child lists in nested_refs derivation and extract_child_references traversal to preserve cross-SDK parity.
- Add regression tests in web_core and a2ui_core.
@gspencergoog
gspencergoog changed the base branch from v1_0 to main September 21, 2026 23:51
@gspencergoog
gspencergoog added this pull request to stack #2719 September 21, 2026 23:51
@gspencergoog
gspencergoog removed this pull request from stack #2719 September 21, 2026 23:52
@gspencergoog
gspencergoog changed the base branch from main to v1_0 September 21, 2026 23:52
@gspencergoog
gspencergoog added this pull request to stack #2720 September 21, 2026 23:52

This branch has not been deployed

No deployments
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