Skip to content

finding(plugin-grid/types): a bare exportOptions array on a directly-authored object-grid node passes the zod mirror unvalidated and silently degrades to the csv/json default #7762

Description

@os-justin

Found while landing objectui#6956 (the ListView leg of the exportOptions reconciliation). Out of that card's surface — packages/plugin-grid/** was excluded by the dispatch — so it is filed with the measurement rather than fixed there. Refs #6956. Refs #4585 (closed by PR #4587, which covers the SpecBridge route only; this card is about what that fix does not reach).

Measured on origin/main = 83c77dc3 (2026-09-05), through the installed @objectstack/spec@17.2.0. Generic types below are written in words or with a space inside the brackets: the body sanitizer eats short tag-shaped fragments.

What happens

packages/plugin-grid/src/ObjectGrid.tsx:2956 reads the OBJECT form and only that:

const declared = schema.exportOptions?.formats || ['csv', 'json'];   // :2956
... && schema.exportOptions?.streaming !== false                      // :2960

A bare format array has no .formats, so the default wins. The useEffect at :2964 that warns about dropped formats reads .formats too and returns early — no error, no warning, no console line. !!schema.exportOptions (:4690) is truthy for a non-empty array, so the export button still shows, offering csv and json regardless of what the author declared. The failure is silent rather than absent.

Which routes still hand the renderer the array

Controls for the readings above: the list-view mirror on the same bare array lifts it to { formats: ['csv', 'xlsx'] } (same session, same installed spec), and 'bulkActions' in ObjectGridSchema.shape is true — the in reading distinguishes a declared key from an undeclared one.

Two candidate remedies (for triage, not a decision)

A. The same fold as ListView, at ObjectGrid's read siteArray.isArray(schema.exportOptions) ? { formats: schema.exportOptions } : schema.exportOptions. Cheap and local. Against it: #4585's own direction rejected a consumer-side fallback as "a second de-facto contract for one spec key" (the shape objectstack#8010 was filed against), and it leaves the zod mirror admitting 'pdf' and sixth keys on this node.

B. A named refusal on the object-grid zod mirror — declare exportOptions there as the spec's strict five-key object and refuse the bare array by name (message along the lines of: a bare format array is the list-view spelling; object-grid reads the object form — write { formats: [...] }). This keeps the TS face and the zod face agreeing (object only), turns the silent degrade into a loud refusal at validation, and closes the 'pdf' / sixth-key gap on this node in the same move. Note the spec's object branch is not an exported symbol (measured in export-options-spec-parity.test.ts), so the declaration either restates the five keys (a copy, pinned by the existing key-set test) or unwraps the object arm of ListViewSchema.shape.exportOptions at module load.

Either way: packages/plugin-grid/src/index.tsx:248, the designer description for exportOptions, lists four keys and omits streaming — worth correcting in the same PR.

Re-check commands

git grep -n "exportOptions" -- packages/plugin-grid/src/ObjectGrid.tsx | head -5
git grep -n "exportOptions" -- packages/types/src/zod/objectql.zod.ts      # no member between :111 and :151
# from packages/plugin-grid, after `pnpm --filter @object-ui/types build`:
node -e "import('@object-ui/types/zod').then(m => console.log(m.ObjectGridSchema.safeParse({ type: 'object-grid', objectName: 'a', exportOptions: ['csv','xlsx'] })))"

Filed unlabelled and unassigned for triage. Written by the dev seat of session_01BAZFhALsQsGqxui8sNqM8s's dispatch for objectui#6956.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanefindingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions