Skip to content

Comments

add a script to bundle catalogs into a standard file#634

Merged
wrenj merged 10 commits intomainfrom
catalog-bundler
Feb 23, 2026
Merged

add a script to bundle catalogs into a standard file#634
wrenj merged 10 commits intomainfrom
catalog-bundler

Conversation

@wrenj
Copy link
Collaborator

@wrenj wrenj commented Feb 17, 2026

tested

uv run scripts/build_catalog.py specification/v0_9/json/standard_catalog.json
📦 Bundling: specification/v0_9/json/standard_catalog.json
✅ Created: specification/v0_9/json/dist/standard_catalog.json

outputs specification/v0_9/json/dist/standard_catalog.json

{
  "$defs": {
    "CatalogComponentCommon": {
      "type": "object",
      "properties": {
        "weight": {
          "type": "number",
          "description": "The relative weight of this component within a Row or Column. This is similar to the CSS 'flex-grow' property. Note: this may ONLY be set when the component is a direct descendant of a Row or Column."
        }
      }
    },
    "theme": {
      "type": "object",
      "properties": {
        "primaryColor": {
          "type": "string",
          "description": "The primary brand color used for highlights (e.g., primary buttons, active borders). Renderers may generate variants of this color for different contexts. Format: Hexadecimal code (e.g., '#00BFFF').",
          "pattern": "^#[0-9a-fA-F]{6}$"
        },
        "iconUrl": {
          "type": "string",
          "format": "uri",
          "description": "A URL for an image that identifies the agent or tool associated with the surface."
        },
        "agentDisplayName": {
          "type": "string",
          "description": "Text to be displayed next to the surface to identify the agent or tool that created it."
        }
      },
      "additionalProperties": true
    },
    "anyComponent": {
      "oneOf": [
        {
          "$ref": "#/components/Text"
        },
        {
          "$ref": "#/components/Image"
        },
        {
          "$ref": "#/components/Icon"
        },
        {
          "$ref": "#/components/Video"
        },
        {
          "$ref": "#/components/AudioPlayer"
        },
        {
          "$ref": "#/components/Row"
        },
        {
          "$ref": "#/components/Column"
        },
        {
          "$ref": "#/components/List"
        },
        {
          "$ref": "#/components/Card"
        },
        {
          "$ref": "#/components/Tabs"
        },
        {
          "$ref": "#/components/Modal"
        },
        {
          "$ref": "#/components/Divider"
        },
        {
          "$ref": "#/components/Button"
        },
        {
          "$ref": "#/components/TextField"
        },
        {
          "$ref": "#/components/CheckBox"
        },
        {
          "$ref": "#/components/ChoicePicker"
        },
        {
          "$ref": "#/components/Slider"
        },
        {
          "$ref": "#/components/DateTimeInput"
        }
      ],
      "discriminator": {
        "propertyName": "component"
      }
    },
    "anyFunction": {
      "oneOf": [
        {
          "$ref": "#/functions/required"
        },
        {
          "$ref": "#/functions/regex"
        },
        {
          "$ref": "#/functions/length"
        },
        {
          "$ref": "#/functions/numeric"
        },
        {
          "$ref": "#/functions/email"
        },
        {
          "$ref": "#/functions/formatString"
        },
        {
          "$ref": "#/functions/formatNumber"
        },
        {
          "$ref": "#/functions/formatCurrency"
        },
        {
          "$ref": "#/functions/formatDate"
        },
        {
          "$ref": "#/functions/pluralize"
        },
        {
          "$ref": "#/functions/openUrl"
        },
        {
          "$ref": "#/functions/and"
        },
        {
          "$ref": "#/functions/or"
        },
        {
          "$ref": "#/functions/not"
        }
      ]
    },
    "common_types_$defs_ComponentCommon": {
      "type": "object",
      "properties": {
        "id": {
          "$ref": "#/$defs/ComponentId"
        },
        "accessibility": {
          "$ref": "#/$defs/AccessibilityAttributes"
        }
      },
      "required": [
        "id"
      ]
    },
    "common_types_$defs_DynamicString": {
      "description": "Represents a string",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "$ref": "#/$defs/DataBinding"
        },
        {
          "allOf": [
            {
              "$ref": "#/$defs/FunctionCall"
            },
            {
              "properties": {
                "returnType": {
                  "const": "string"
                }
              }
            }
          ]
        }
      ]
    },
    "common_types_$defs_ChildList": {
      "oneOf": [
        {
          "type": "array",
          "items": {
            "$ref": "#/$defs/ComponentId"
          },
          "description": "A static list of child component IDs."
        },
        {
          "type": "object",
          "description": "A template for generating a dynamic list of children from a data model list. The `componentId` is the component to use as a template.",
          "properties": {
            "componentId": {
              "$ref": "#/$defs/ComponentId"
            },
            "path": {
              "type": "string",
              "description": "The path to the list of component property objects in the data model."
            }
          },
          "required": [
            "componentId",
            "path"
          ],
          "additionalProperties": false
        }
      ]
    },
    "common_types_$defs_ComponentId": {
      "type": "string",
      "description": "The unique identifier for a component, used for both definitions and references within the same surface."
    },
    "common_types_$defs_Checkable": {
      "description": "Properties for components that support client-side checks.",
      "type": "object",
      "properties": {
        "checks": {
          "type": "array",
          "description": "A list of checks to perform. These are function calls that must return a boolean indicating validity.",
          "items": {
            "$ref": "#/$defs/CheckRule"
          }
        }
      }
    },
    "common_types_$defs_Action": {
      "description": "Defines an interaction handler that can either trigger a server-side event or execute a local client-side function.",
      "oneOf": [
        {
          "type": "object",
          "description": "Triggers a server-side event.",
          "properties": {
            "event": {
              "type": "object",
              "description": "The event to dispatch to the server.",
              "properties": {
                "name": {
                  "type": "string",
                  "description": "The name of the action to be dispatched to the server."
                },
                "context": {
                  "type": "object",
                  "description": "A JSON object containing the key-value pairs for the action context. Values can be literals or paths. Use literal values unless the value must be dynamically bound to the data model. Do NOT use paths for static IDs.",
                  "additionalProperties": {
                    "$ref": "#/$defs/DynamicValue"
                  }
                }
              },
              "required": [
                "name"
              ],
              "additionalProperties": false
            }
          },
          "required": [
            "event"
          ],
          "additionalProperties": false
        },
        {
          "type": "object",
          "description": "Executes a local client-side function.",
          "properties": {
            "functionCall": {
              "$ref": "#/$defs/FunctionCall"
            }
          },
          "required": [
            "functionCall"
          ],
          "additionalProperties": false
        }
      ]
    },
    "common_types_$defs_DynamicBoolean": {
      "description": "A boolean value that can be a literal, a path, or a function call returning a boolean.",
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "$ref": "#/$defs/DataBinding"
        },
        {
          "allOf": [
            {
              "$ref": "#/$defs/FunctionCall"
            },
            {
              "properties": {
                "returnType": {
                  "const": "boolean"
                }
              }
            }
          ]
        }
      ]
    },
    "common_types_$defs_DynamicStringList": {
      "description": "Represents a value that can be either a literal array of strings, a path to a string array in the data model, or a function call returning a string array.",
      "oneOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "$ref": "#/$defs/DataBinding"
        },
        {
          "allOf": [
            {
              "$ref": "#/$defs/FunctionCall"
            },
            {
              "properties": {
                "returnType": {
                  "const": "array"
                }
              }
            }
          ]
        }
      ]
    },
    "common_types_$defs_DynamicNumber": {
      "description": "Represents a value that can be either a literal number, a path to a number in the data model, or a function call returning a number.",
      "oneOf": [
        {
          "type": "number"
        },
        {
          "$ref": "#/$defs/DataBinding"
        },
        {
          "allOf": [
            {
              "$ref": "#/$defs/FunctionCall"
            },
            {
              "properties": {
                "returnType": {
                  "const": "number"
                }
              }
            }
          ]
        }
      ]
    },
    "common_types_$defs_DynamicValue": {
      "description": "A value that can be a literal, a path, or a function call returning any type.",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "number"
        },
        {
          "type": "boolean"
        },
        {
          "type": "array"
        },
        {
          "$ref": "#/$defs/DataBinding"
        },
        {
          "$ref": "#/$defs/FunctionCall"
        }
      ]
    }
  },
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://a2ui.org/specification/v0_9/standard_catalog.json",
  "title": "A2UI Standard Catalog",
  "description": "Unified catalog of standard A2UI components and functions.",
  "catalogId": "https://a2ui.org/specification/v0_9/standard_catalog.json",
  "components": {
    "Text": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Text"
            },
            "text": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The text content to display. While simple Markdown formatting is supported (i.e. without HTML, images, or links), utilizing dedicated UI components is generally preferred for a richer and more structured presentation."
            },
            "variant": {
              "type": "string",
              "description": "A hint for the base text style.",
              "enum": [
                "h1",
                "h2",
                "h3",
                "h4",
                "h5",
                "caption",
                "body"
              ]
            }
          },
          "required": [
            "component",
            "text"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Image": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Image"
            },
            "url": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The URL of the image to display."
            },
            "fit": {
              "type": "string",
              "description": "Specifies how the image should be resized to fit its container. This corresponds to the CSS 'object-fit' property.",
              "enum": [
                "contain",
                "cover",
                "fill",
                "none",
                "scale-down"
              ]
            },
            "variant": {
              "type": "string",
              "description": "A hint for the image size and style.",
              "enum": [
                "icon",
                "avatar",
                "smallFeature",
                "mediumFeature",
                "largeFeature",
                "header"
              ]
            }
          },
          "required": [
            "component",
            "url"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Icon": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Icon"
            },
            "name": {
              "description": "The name of the icon to display.",
              "oneOf": [
                {
                  "type": "string",
                  "enum": [
                    "accountCircle",
                    "add",
                    "arrowBack",
                    "arrowForward",
                    "attachFile",
                    "calendarToday",
                    "call",
                    "camera",
                    "check",
                    "close",
                    "delete",
                    "download",
                    "edit",
                    "event",
                    "error",
                    "fastForward",
                    "favorite",
                    "favoriteOff",
                    "folder",
                    "help",
                    "home",
                    "info",
                    "locationOn",
                    "lock",
                    "lockOpen",
                    "mail",
                    "menu",
                    "moreVert",
                    "moreHoriz",
                    "notificationsOff",
                    "notifications",
                    "pause",
                    "payment",
                    "person",
                    "phone",
                    "photo",
                    "play",
                    "print",
                    "refresh",
                    "rewind",
                    "search",
                    "send",
                    "settings",
                    "share",
                    "shoppingCart",
                    "skipNext",
                    "skipPrevious",
                    "star",
                    "starHalf",
                    "starOff",
                    "stop",
                    "upload",
                    "visibility",
                    "visibilityOff",
                    "volumeDown",
                    "volumeMute",
                    "volumeOff",
                    "volumeUp",
                    "warning"
                  ]
                },
                {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "path"
                  ],
                  "additionalProperties": false
                }
              ]
            }
          },
          "required": [
            "component",
            "name"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Video": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Video"
            },
            "url": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The URL of the video to display."
            }
          },
          "required": [
            "component",
            "url"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "AudioPlayer": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "AudioPlayer"
            },
            "url": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The URL of the audio to be played."
            },
            "description": {
              "description": "A description of the audio, such as a title or summary.",
              "$ref": "#/$defs/common_types_$defs_DynamicString"
            }
          },
          "required": [
            "component",
            "url"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Row": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "description": "A layout component that arranges its children horizontally. To create a grid layout, nest Columns within this Row.",
          "properties": {
            "component": {
              "const": "Row"
            },
            "children": {
              "description": "Defines the children. Use an array of strings for a fixed set of children, or a template object to generate children from a data list. Children cannot be defined inline, they must be referred to by ID.",
              "$ref": "#/$defs/common_types_$defs_ChildList"
            },
            "justify": {
              "type": "string",
              "description": "Defines the arrangement of children along the main axis (horizontally). Use 'spaceBetween' to push items to the edges, or 'start'/'end'/'center' to pack them together.",
              "enum": [
                "center",
                "end",
                "spaceAround",
                "spaceBetween",
                "spaceEvenly",
                "start",
                "stretch"
              ]
            },
            "align": {
              "type": "string",
              "description": "Defines the alignment of children along the cross axis (vertically). This is similar to the CSS 'align-items' property, but uses camelCase values (e.g., 'start').",
              "enum": [
                "start",
                "center",
                "end",
                "stretch"
              ]
            }
          },
          "required": [
            "component",
            "children"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Column": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "description": "A layout component that arranges its children vertically. To create a grid layout, nest Rows within this Column.",
          "properties": {
            "component": {
              "const": "Column"
            },
            "children": {
              "description": "Defines the children. Use an array of strings for a fixed set of children, or a template object to generate children from a data list. Children cannot be defined inline, they must be referred to by ID.",
              "$ref": "#/$defs/common_types_$defs_ChildList"
            },
            "justify": {
              "type": "string",
              "description": "Defines the arrangement of children along the main axis (vertically). Use 'spaceBetween' to push items to the edges (e.g. header at top, footer at bottom), or 'start'/'end'/'center' to pack them together.",
              "enum": [
                "start",
                "center",
                "end",
                "spaceBetween",
                "spaceAround",
                "spaceEvenly",
                "stretch"
              ]
            },
            "align": {
              "type": "string",
              "description": "Defines the alignment of children along the cross axis (horizontally). This is similar to the CSS 'align-items' property.",
              "enum": [
                "center",
                "end",
                "start",
                "stretch"
              ]
            }
          },
          "required": [
            "component",
            "children"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "List": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "List"
            },
            "children": {
              "description": "Defines the children. Use an array of strings for a fixed set of children, or a template object to generate children from a data list.",
              "$ref": "#/$defs/common_types_$defs_ChildList"
            },
            "direction": {
              "type": "string",
              "description": "The direction in which the list items are laid out.",
              "enum": [
                "vertical",
                "horizontal"
              ]
            },
            "align": {
              "type": "string",
              "description": "Defines the alignment of children along the cross axis.",
              "enum": [
                "start",
                "center",
                "end",
                "stretch"
              ]
            }
          },
          "required": [
            "component",
            "children"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Card": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Card"
            },
            "child": {
              "$ref": "#/$defs/common_types_$defs_ComponentId",
              "description": "The ID of the single child component to be rendered inside the card. To display multiple elements, you MUST wrap them in a layout component (like Column or Row) and pass that container's ID here. Do NOT pass multiple IDs or a non-existent ID. Do NOT define the child component inline."
            }
          },
          "required": [
            "component",
            "child"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Tabs": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Tabs"
            },
            "tabs": {
              "type": "array",
              "description": "An array of objects, where each object defines a tab with a title and a child component.",
              "items": {
                "type": "object",
                "properties": {
                  "title": {
                    "description": "The tab title.",
                    "$ref": "#/$defs/common_types_$defs_DynamicString"
                  },
                  "child": {
                    "$ref": "#/$defs/common_types_$defs_ComponentId",
                    "description": "The ID of the child component. Do NOT define the component inline."
                  }
                },
                "required": [
                  "title",
                  "child"
                ],
                "additionalProperties": false
              }
            }
          },
          "required": [
            "component",
            "tabs"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Modal": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Modal"
            },
            "trigger": {
              "$ref": "#/$defs/common_types_$defs_ComponentId",
              "description": "The ID of the component that opens the modal when interacted with (e.g., a button). Do NOT define the component inline."
            },
            "content": {
              "$ref": "#/$defs/common_types_$defs_ComponentId",
              "description": "The ID of the component to be displayed inside the modal. Do NOT define the component inline."
            }
          },
          "required": [
            "component",
            "trigger",
            "content"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Divider": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Divider"
            },
            "axis": {
              "type": "string",
              "description": "The orientation of the divider.",
              "enum": [
                "horizontal",
                "vertical"
              ],
              "default": "horizontal"
            }
          },
          "required": [
            "component"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Button": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Button"
            },
            "child": {
              "$ref": "#/$defs/common_types_$defs_ComponentId",
              "description": "The ID of the child component. Use a 'Text' component for a labeled button. Only use an 'Icon' if the requirements explicitly ask for an icon-only button. Do NOT define the child component inline."
            },
            "variant": {
              "type": "string",
              "description": "A hint for the button style. If omitted, a default button style is used. 'primary' indicates this is the main call-to-action button. 'borderless' means the button has no visual border or background, making its child content appear like a clickable link.",
              "enum": [
                "primary",
                "borderless"
              ]
            },
            "action": {
              "$ref": "#/$defs/common_types_$defs_Action"
            }
          },
          "required": [
            "component",
            "child",
            "action"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "TextField": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "TextField"
            },
            "label": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The text label for the input field."
            },
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The value of the text field."
            },
            "variant": {
              "type": "string",
              "description": "The type of input field to display.",
              "enum": [
                "longText",
                "number",
                "shortText",
                "obscured"
              ]
            },
            "validationRegexp": {
              "type": "string",
              "description": "A regular expression used for client-side validation of the input."
            }
          },
          "required": [
            "component",
            "label"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "CheckBox": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "CheckBox"
            },
            "label": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The text to display next to the checkbox."
            },
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicBoolean",
              "description": "The current state of the checkbox (true for checked, false for unchecked)."
            }
          },
          "required": [
            "component",
            "label",
            "value"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "ChoicePicker": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "description": "A component that allows selecting one or more options from a list.",
          "properties": {
            "component": {
              "const": "ChoicePicker"
            },
            "label": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The label for the group of options."
            },
            "variant": {
              "type": "string",
              "description": "A hint for how the choice picker should be displayed and behave.",
              "enum": [
                "multipleSelection",
                "mutuallyExclusive"
              ]
            },
            "options": {
              "type": "array",
              "description": "The list of available options to choose from.",
              "items": {
                "type": "object",
                "properties": {
                  "label": {
                    "description": "The text to display for this option.",
                    "$ref": "#/$defs/common_types_$defs_DynamicString"
                  },
                  "value": {
                    "type": "string",
                    "description": "The stable value associated with this option."
                  }
                },
                "required": [
                  "label",
                  "value"
                ],
                "additionalProperties": false
              }
            },
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicStringList",
              "description": "The list of currently selected values. This should be bound to a string array in the data model."
            },
            "displayStyle": {
              "type": "string",
              "description": "The display style of the component.",
              "enum": [
                "checkbox",
                "chips"
              ]
            },
            "filterable": {
              "type": "boolean",
              "description": "If true, displays a search input to filter the options."
            }
          },
          "required": [
            "component",
            "options",
            "value"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "Slider": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "Slider"
            },
            "label": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The label for the slider."
            },
            "min": {
              "type": "number",
              "description": "The minimum value of the slider."
            },
            "max": {
              "type": "number",
              "description": "The maximum value of the slider."
            },
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "The current value of the slider."
            }
          },
          "required": [
            "component",
            "value",
            "min",
            "max"
          ]
        }
      ],
      "unevaluatedProperties": false
    },
    "DateTimeInput": {
      "type": "object",
      "allOf": [
        {
          "$ref": "#/$defs/common_types_$defs_ComponentCommon"
        },
        {
          "$ref": "#/$defs/CatalogComponentCommon"
        },
        {
          "$ref": "#/$defs/common_types_$defs_Checkable"
        },
        {
          "type": "object",
          "properties": {
            "component": {
              "const": "DateTimeInput"
            },
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The selected date and/or time value in ISO 8601 format. If not yet set, initialize with an empty string."
            },
            "enableDate": {
              "type": "boolean",
              "description": "If true, allows the user to select a date."
            },
            "enableTime": {
              "type": "boolean",
              "description": "If true, allows the user to select a time."
            },
            "min": {
              "allOf": [
                {
                  "$ref": "#/$defs/common_types_$defs_DynamicString"
                },
                {
                  "if": {
                    "type": "string"
                  },
                  "then": {
                    "oneOf": [
                      {
                        "format": "date"
                      },
                      {
                        "format": "time"
                      },
                      {
                        "format": "date-time"
                      }
                    ]
                  }
                }
              ],
              "description": "The minimum allowed date/time in ISO 8601 format."
            },
            "max": {
              "allOf": [
                {
                  "$ref": "#/$defs/common_types_$defs_DynamicString"
                },
                {
                  "if": {
                    "type": "string"
                  },
                  "then": {
                    "oneOf": [
                      {
                        "format": "date"
                      },
                      {
                        "format": "time"
                      },
                      {
                        "format": "date-time"
                      }
                    ]
                  }
                }
              ],
              "description": "The maximum allowed date/time in ISO 8601 format."
            },
            "label": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The text label for the input field."
            }
          },
          "required": [
            "component",
            "value"
          ]
        }
      ],
      "unevaluatedProperties": false
    }
  },
  "functions": {
    "required": {
      "type": "object",
      "description": "Checks that the value is not null, undefined, or empty.",
      "properties": {
        "call": {
          "const": "required"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "description": "The value to check."
            }
          },
          "required": [
            "value"
          ],
          "additionalProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "regex": {
      "type": "object",
      "description": "Checks that the value matches a regular expression string.",
      "properties": {
        "call": {
          "const": "regex"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString"
            },
            "pattern": {
              "type": "string",
              "description": "The regex pattern to match against."
            }
          },
          "required": [
            "value",
            "pattern"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "length": {
      "type": "object",
      "description": "Checks string length constraints.",
      "properties": {
        "call": {
          "const": "length"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString"
            },
            "min": {
              "type": "integer",
              "minimum": 0,
              "description": "The minimum allowed length."
            },
            "max": {
              "type": "integer",
              "minimum": 0,
              "description": "The maximum allowed length."
            }
          },
          "required": [
            "value"
          ],
          "anyOf": [
            {
              "required": [
                "min"
              ]
            },
            {
              "required": [
                "max"
              ]
            }
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "numeric": {
      "type": "object",
      "description": "Checks numeric range constraints.",
      "properties": {
        "call": {
          "const": "numeric"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber"
            },
            "min": {
              "type": "number",
              "description": "The minimum allowed value."
            },
            "max": {
              "type": "number",
              "description": "The maximum allowed value."
            }
          },
          "required": [
            "value"
          ],
          "anyOf": [
            {
              "required": [
                "min"
              ]
            },
            {
              "required": [
                "max"
              ]
            }
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "email": {
      "type": "object",
      "description": "Checks that the value is a valid email address.",
      "properties": {
        "call": {
          "const": "email"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString"
            }
          },
          "required": [
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "formatString": {
      "type": "object",
      "description": "Performs string interpolation of data model values and other functions in the catalog functions list and returns the resulting string. The value string can contain interpolated expressions in the `${expression}` format. Supported expression types include: JSON Pointer paths to the data model (e.g., `${/absolute/path}` or `${relative/path}`), and client-side function calls (e.g., `${now()}`). Function arguments must be named (e.g., `${formatDate(value:${/currentDate}, format:'MM-dd')}`). To include a literal `${` sequence, escape it as `\\${`.",
      "properties": {
        "call": {
          "const": "formatString"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicString"
            }
          },
          "required": [
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "string"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "formatNumber": {
      "type": "object",
      "description": "Formats a number with the specified grouping and decimal precision.",
      "properties": {
        "call": {
          "const": "formatNumber"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "The number to format."
            },
            "decimals": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "Optional. The number of decimal places to show. Defaults to 0 or 2 depending on locale."
            },
            "grouping": {
              "$ref": "#/$defs/common_types_$defs_DynamicBoolean",
              "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true."
            }
          },
          "required": [
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "string"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "formatCurrency": {
      "type": "object",
      "description": "Formats a number as a currency string.",
      "properties": {
        "call": {
          "const": "formatCurrency"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "The monetary amount."
            },
            "currency": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The ISO 4217 currency code (e.g., 'USD', 'EUR')."
            },
            "decimals": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "Optional. The number of decimal places to show. Defaults to 0 or 2 depending on locale."
            },
            "grouping": {
              "$ref": "#/$defs/common_types_$defs_DynamicBoolean",
              "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true."
            }
          },
          "required": [
            "currency",
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "string"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "formatDate": {
      "type": "object",
      "description": "Formats a timestamp into a string using a pattern.",
      "properties": {
        "call": {
          "const": "formatDate"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicValue",
              "description": "The date to format."
            },
            "format": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "A Unicode TR35 date pattern string.\n\nToken Reference:\n- Year: 'yy' (26), 'yyyy' (2026)\n- Month: 'M' (1), 'MM' (01), 'MMM' (Jan), 'MMMM' (January)\n- Day: 'd' (1), 'dd' (01), 'E' (Tue), 'EEEE' (Tuesday)\n- Hour (12h): 'h' (1-12), 'hh' (01-12) - requires 'a' for AM/PM\n- Hour (24h): 'H' (0-23), 'HH' (00-23) - Military Time\n- Minute: 'mm' (00-59)\n- Second: 'ss' (00-59)\n- Period: 'a' (AM/PM)\n\nExamples:\n- 'MMM dd, yyyy' -> 'Jan 16, 2026'\n- 'HH:mm' -> '14:30' (Military)\n- 'h:mm a' -> '2:30 PM'\n- 'EEEE, d MMMM' -> 'Friday, 16 January'"
            }
          },
          "required": [
            "format",
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "string"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "pluralize": {
      "type": "object",
      "description": "Returns a localized string based on the Common Locale Data Repository (CLDR) plural category of the count (zero, one, two, few, many, other). Requires an 'other' fallback. For English, just use 'one' and 'other'.",
      "properties": {
        "call": {
          "const": "pluralize"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicNumber",
              "description": "The numeric value used to determine the plural category."
            },
            "zero": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "String for the 'zero' category (e.g., 0 items)."
            },
            "one": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "String for the 'one' category (e.g., 1 item)."
            },
            "two": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "String for the 'two' category (used in Arabic, Welsh, etc.)."
            },
            "few": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "String for the 'few' category (e.g., small groups in Slavic languages)."
            },
            "many": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "String for the 'many' category (e.g., large groups in various languages)."
            },
            "other": {
              "$ref": "#/$defs/common_types_$defs_DynamicString",
              "description": "The default/fallback string (used for general plural cases)."
            }
          },
          "required": [
            "value",
            "other"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "string"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "openUrl": {
      "type": "object",
      "description": "Opens the specified URL in a browser or handler. This function has no return value.",
      "properties": {
        "call": {
          "const": "openUrl"
        },
        "args": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string",
              "format": "uri",
              "description": "The URL to open."
            }
          },
          "required": [
            "url"
          ],
          "additionalProperties": false
        },
        "returnType": {
          "const": "void"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "and": {
      "type": "object",
      "description": "Performs a logical AND operation on a list of boolean values.",
      "properties": {
        "call": {
          "const": "and"
        },
        "args": {
          "type": "object",
          "properties": {
            "values": {
              "type": "array",
              "description": "The list of boolean values to evaluate.",
              "items": {
                "$ref": "#/$defs/common_types_$defs_DynamicBoolean"
              },
              "minItems": 2
            }
          },
          "required": [
            "values"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "or": {
      "type": "object",
      "description": "Performs a logical OR operation on a list of boolean values.",
      "properties": {
        "call": {
          "const": "or"
        },
        "args": {
          "type": "object",
          "properties": {
            "values": {
              "type": "array",
              "description": "The list of boolean values to evaluate.",
              "items": {
                "$ref": "#/$defs/common_types_$defs_DynamicBoolean"
              },
              "minItems": 2
            }
          },
          "required": [
            "values"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    },
    "not": {
      "type": "object",
      "description": "Performs a logical NOT operation on a boolean value.",
      "properties": {
        "call": {
          "const": "not"
        },
        "args": {
          "type": "object",
          "properties": {
            "value": {
              "$ref": "#/$defs/common_types_$defs_DynamicBoolean",
              "description": "The boolean value to negate."
            }
          },
          "required": [
            "value"
          ],
          "unevaluatedProperties": false
        },
        "returnType": {
          "const": "boolean"
        }
      },
      "required": [
        "call",
        "args"
      ],
      "unevaluatedProperties": false
    }
  }
}

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a Python script for bundling JSON schemas, along with its dependency management (pyproject.toml, uv.lock) and tests. The script effectively processes external $refs and integrates them into a single $defs section. However, there are a few areas for improvement. Specifically, the method for generating $defs keys carries a risk of collisions, which could lead to incorrect schema bundling. Additionally, ensuring deterministic output for generated schema keys would improve consistency, and there's an unused import that can be removed for cleaner code.

@wrenj wrenj requested a review from paullewis as a code owner February 17, 2026 22:14
@wrenj
Copy link
Collaborator Author

wrenj commented Feb 17, 2026

addressed comments ptal

Copy link
Collaborator

@jacobsimionato jacobsimionato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some documentation related to this, to give it context? Right now, if you weren't in our chat thread, I think anyone would struggle to understand what this is for.

E.g.

  • Update v0.9 specification to explain that catalog documents should aways be independent and not refer to other catalogs, other than common_types.json
  • Add a readme for this tool explaining what it does and why, and with an example of when and how you'd use it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be removed now? And uv.lock?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@wrenj
Copy link
Collaborator Author

wrenj commented Feb 18, 2026

Can you add some documentation related to this, to give it context? Right now, if you weren't in our chat thread, I think anyone would struggle to understand what this is for.

E.g.

  • Update v0.9 specification to explain that catalog documents should aways be independent and not refer to other catalogs, other than common_types.json
  • Add a readme for this tool explaining what it does and why, and with an example of when and how you'd use it

added a readme

the catalog.md explains the freestanding catalogs requirement in another PR

@jacobsimionato
Copy link
Collaborator

Can you add some documentation related to this, to give it context? Right now, if you weren't in our chat thread, I think anyone would struggle to understand what this is for.
E.g.

  • Update v0.9 specification to explain that catalog documents should aways be independent and not refer to other catalogs, other than common_types.json
  • Add a readme for this tool explaining what it does and why, and with an example of when and how you'd use it

added a readme

the catalog.md explains the freestanding catalogs requirement in another PR

Hey I don't see any docs in the PR - can you add the .md? Or am I missing something?

@wrenj wrenj requested a review from nan-yu as a code owner February 23, 2026 14:37
@wrenj
Copy link
Collaborator Author

wrenj commented Feb 23, 2026

Can you add some documentation related to this, to give it context? Right now, if you weren't in our chat thread, I think anyone would struggle to understand what this is for.
E.g.

  • Update v0.9 specification to explain that catalog documents should aways be independent and not refer to other catalogs, other than common_types.json
  • Add a readme for this tool explaining what it does and why, and with an example of when and how you'd use it

added a readme
the catalog.md explains the freestanding catalogs requirement in another PR

Hey I don't see any docs in the PR - can you add the .md? Or am I missing something?

try now, not sure why it didn't upload before

@wrenj wrenj enabled auto-merge (squash) February 23, 2026 14:39
@nan-yu
Copy link
Collaborator

nan-yu commented Feb 23, 2026

What is the intent to bundle catalogs into a standard file?

While developing A2uiSchemaManager, I considered a similar bundling approach for the core schemas (server_to_client.json, common_types.json, standard_catalog.json, and custom catalog schema). However, I found that using a referencing registry (as seen in my validator implementation) allows us to perform validation across multiple files without the overhead of physical bundling.

This registry-based approach also simplifies testing with custom catalogs (see example test). Do you think we could leverage the registry here instead of a build-time bundling script?

@wrenj
Copy link
Collaborator Author

wrenj commented Feb 23, 2026

What is the intent to bundle catalogs into a standard file?

While developing A2uiSchemaManager, I considered a similar bundling approach for the core schemas (server_to_client.json, common_types.json, standard_catalog.json, and custom catalog schema). However, I found that using a referencing registry (as seen in my validator implementation) allows us to perform validation across multiple files without the overhead of physical bundling.

This registry-based approach also simplifies testing with custom catalogs (see example test). Do you think we could leverage the registry here instead of a build-time bundling script?

from @jacobsimionato

I think Catalogs should be freestanding because:

Catalogs documents are used by LLMs to do inference. Inference needs to format the catalog as simply as possible to get best results. Having a layer of indirection adds complexity. With the "cherry-picking" example, its especially bad, because you pull an entire catalog into context, just to cherry-pick a couple of Components from it. The other components are unnecessarily going to confuse the LLM. We could write inference utilities which analyse a set of catalogs and references and merge and consolidate them to remove any unreachable definitions. But I think this adds unnecessary complexity and so we should instead just require that each catalog is completely independent.

Catalogs documents are used by humans to integrate catalog support into renderers and agents. Having catalogs refer to each other, and thus have dependencies between each other, makes this much more complex because now if I'm publishing a catalog, I need to make sure that consumers of it also gather up all its dependencies. There is no reliable way to do this, because catalogs are identified by a string ID which is not necessarily a URI. If vercel publish a catalog which depends on the material catalog, and Doordash want to use Vercel's catalog, Doordash now need to go and find both catalogs and install them correctly.

@nan-yu
Copy link
Collaborator

nan-yu commented Feb 23, 2026

With the "cherry-picking" example, its especially bad, because you pull an entire catalog into context, just to cherry-pick a couple of Components from it. The other components are unnecessarily going to confuse the LLM. We could write inference utilities which analyse a set of catalogs and references and merge and consolidate them to remove any unreachable definitions.

I am not aware of the cherry-picking example, but the current SDK implementation does allow cherry-picking a set of components from the basic ones. There're two mechanisms that guarantee that unreferenced components are not loaded into the LLM context when generating system prompt:

Having catalogs refer to each other, and thus have dependencies between each other, makes this much more complex because now if I'm publishing a catalog, I need to make sure that consumers of it also gather up all its dependencies.

I agree that having catalogs refer to each other is complex, but having custom catalogs refer to the basic one seems reasonable.

@wrenj wrenj merged commit a9f5419 into main Feb 23, 2026
6 checks passed
@wrenj wrenj deleted the catalog-bundler branch February 23, 2026 22:28
@github-project-automation github-project-automation bot moved this from Todo to Done in A2UI Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants