Skip to content

finding(types): 28 zod-mirror keys are declared z.function(), which NO JSON document can satisfy — 14 in-repo corpus files author them and are silently invalid #6124

Description

@yinlianghui

Filed unassigned by the domain:ui execution seat while measuring objectui#5392 (session session_01CSoz9uGhaaSgiq3hshtN7L). Out of scope for that card, which is a measurement of zod→JSON Schema fidelity; this is a separate defect its instrument surfaced.

Dedupe search run before filing: #5250 (BaseSchema.passthrough() accepts undeclared keys) is the opposite direction and does not cover this; #5927 / #5853 / #6058 are the mirror-vs-TypeScript-declaration drift family, which is a different axis — this is mirror-vs-JSON-authorability. No existing card names z.function().

What was measured

@object-ui/types is the protocol layer for a JSON-authored UI vocabulary. Its zod mirrors declare 28 distinct keys as z.function():

cell custom onCancel onCardAdd onCardClick onCardMove onChange onClick
onCollapsedChange onColumnAdd onColumnsReorder onComplete onConfirm onDismiss
onError onExpandChange onOpenChange onPageChange onRowDelete onRowEdit
onSelectChange onSelectionChange onSendMessage onSlideChange onSubmit
onValueChange onViewChange validate

Command (run in packages/types/src/zod/):

grep -hoE "^\s*[A-Za-z]+: z\.function\(" *.ts | sed 's/[[:space:]]*//;s/: z.function($//' | sort -u

60 call sites across 9 files (grep -oF 'z.function(' *.ts | wc -l → 60; grep -lF → base, complex, data-display, disclosure, feedback, form, layout, navigation, overlay).

No JSON value satisfies z.function() — measured, with a positive control in the same run:

null -> REJECT {} -> REJECT
1 -> REJECT [] -> REJECT
"x" -> REJECT true -> REJECT
a real function -> accept <- control: the instrument can say "accept"

Why it matters: 14 in-repo corpus files author these keys and fail

Validating every root-type JSON file in the repo against AnyComponentSchema.safeParse finds 14 files that are rejected today, all for this reason. Example, examples/schema-catalog/src/schemas/components-feedback-toast/destructive.json:

{"type":"button","label":"Destructive Toast","variant":"destructive",
"onClick":{"action":"toast","variant":"destructive","title":"Error","description":"Something went wrong."}}
ButtonSchema.safeParse -> false
issue: { code: "invalid_type", expected: "function", path: ["onClick"],
message: "Invalid input: expected function, received object" }

The file is authoring onClick as a declarative action object, which is the only thing JSON can express. The mirror asks for a JS function. The affected files are the components-feedback-toast/ and components-feedback-sonner/ catalogs.

And they are invisible

All 14 sit in objectui#5127's structure-key-less set — they carry no body/children/className/… at the root, so objectui check never judges them. They are invalid and unreported, which is why this has stood.

The question this needs ruled

Two directions, and this is a contract question rather than a mechanical fix:

  • Narrow the declaration — if a JSON author can never supply a function, z.function() is declared != enforceable on a JSON surface and the keys should be removed or demoted to a runtime-only (non-authorable) face under ADR-0049 enforce-or-remove.
  • Widen the mirror to the declarative form — if onClick: {action: …} is the intended authored spelling (which the corpus and the renderer's own action plumbing suggest), the mirror should declare that shape, and the 14 corpus files become valid.

⛔ Not fixed here — picking one changes a published contract. Filed for triage.

Related: #5250, #5927, #5853, #6058, #5127, #5392.

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:queue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions