fix(types): bind the ListView exportOptions mirror to the spec field; drop the ListView casts (#6956) - #7763
Merged
Conversation
… drop the ListView casts (#6956) The zod mirror of `ListViewSchema` restated a pre-objectstack#8010 shape for `exportOptions` — `'pdf'` accepted in both spellings, no `streaming`, a non-strict object — while the installed `@objectstack/spec@17.2.0` refuses `'pdf'` with an `os migrate meta --from 16` prescription, is strict on the object form and declares `streaming`. Because `ListViewInferred` is `z.input` of the mirror, the `ListViewSchema` type the ListView renderer is written against disagreed with its sibling `ObjectGridSchema['exportOptions']` and the renderer read `streaming` through `as any`. The member is now `SpecListViewSchema.shape.exportOptions` by reference: the spec's two-branch union (bare array lifted to `{ formats }` at parse, strict five-key object), with the spec's own description. `ListViewSchema['exportOptions']` is measured to be exactly `ListViewExportFormat[] | ListViewExportOptions`. ListView: the two `as any` `streaming` reads and the `'pdf'` in the fold's cast are deleted; they compile without annotation. The bare-array fold stays — nothing on the render path parses, so a stored array reaches the renderer un-lifted (objectui#4535 item 4). The parity pin is widened to the mirror: identity with the spec field, the four verdicts the card names (lift / refuse pdf / refuse sixth key / accept streaming with the value surviving), the TS face pins, and a neighbouring member as control. One fixture that authored `'pdf'` under the ListView type (`p1-spec-alignment.test.ts`) drops it — the test is about the object form, not the value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BAZFhALsQsGqxui8sNqM8s
…tview-export-options-spec
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
Generated by Claude Code |
os-justin
marked this pull request as ready for review
September 5, 2026 16:19
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6956
Tier
CONTRACT_REVIEW_TIER(Clause-② yes): this narrows a published validator's accept set ('pdf'refused in both spellings, the object branch strict) and declaresstreaming. Draft for thedomain:specseat's review; dispatched from sessionsession_01BAZFhALsQsGqxui8sNqM8s. Generic type arguments in this body are written in words — GitHub's body sanitizer eats short tag-shaped fragments — and pipes are kept out of table cells.Premise, re-verified on
83c77dc3(and again on the merged head216c7490)'pdf'in both branches, nostreaming, non-strictz.objectpackages/types/src/zod/objectql.zod.ts:514to:522, byte-identical on9587fc95,83c77dc3and3e377c93(no commit between the tips touched the file)ListView.tsx:1304a cast to an array of csv, xlsx, json, pdf;:1324and:2976(… as any)?.streaming !== false:1299to:1308{ formats }normalize-list-view.tsexportOptionshits; controluserActions6objectql.ts:572ListViewExportFormat= csv, xlsx, json;:607ListViewExportOptionsfive keys incl.streaming@objectstack/spec@17.2.0;packages/typesand the spec resolve the SAMEzod@4.4.3instance (node_modules/.pnpm/zod@4.4.3), so a by-reference member is a schema from the same class graphThe four spec readings, through the installed pin (not a working tree)
ListViewSchema.shape.exportOptionsfrom@objectstack/spec/uiat@objectstack/spec@17.2.0:['csv', 'xlsx']→ ACCEPT →{"formats":["csv","xlsx"]}(the lift)['csv', 'pdf']→ REFUSE, codeinvalid_union; the message names the removal in@objectstack/spec17.0.0 (objectstack#8010; PDF export declined as objectstack#1301 NOT_PLANNED), prescribes deleting the value, lists the surviving formats csv / xlsx / json, and points atos migrate meta --from 16{ formats: ['csv'], compression: 'gzip' }→ REFUSE, codeinvalid_union(strict; the object arm's catchall isnever){ formats: ['csv'], streaming: true }→ ACCEPT →{"formats":["csv"],"streaming":true}Structure read off the schema object: wrapper chain
optional → union; armspipe(array of the enum csv / xlsx / json, then the transform) andobject(five optional keys, no defaults, catchallnever). The same probe on{ formats: ['csv'], streaming: false }returns the value intact.What moved
packages/types/src/zod/objectql.zod.ts, theexportOptionsmember only):exportOptions: SpecListViewSchema.shape.exportOptions— the spec field by reference, under the file's existingSpecListViewSchemaimport alias. The spec field is already.optional()(re-wrapping would nest a second optional) and carries the spec's own description, so neither is re-applied. Measured: the import is usable at module load — the same alias already feeds thirty-odd fields by reference on this schema, the package builds and type-checks, and the parity test asserts identity (toBe) between the mirror's member and the spec's.ListViewInferred(z.inputof the mirror) now spells the member as the spec's INPUT type, measured by a type-levelEqualpin:NonNullableofListViewSchema['exportOptions']is exactlyListViewExportFormat[] | ListViewExportOptions— the array of csv / xlsx / json, or the strict five-key object withstreaming. Two prose sites that calledexportOptions"intentionally broader than spec's" are corrected (the schema's doc comment at:300andpackages/types/src/zod/README.md:22). Nothing else in the file moves;LIST_VIEW_LOCAL_OVERRIDESkeeps the key, as it does foruserActions,ariaand the per-view configs that are likewise redeclared below the by-reference extend.packages/plugin-list/src/ListView.tsx, exactly three lines)::1304return { formats: schema.exportOptions };(the cast is gone entirely — the fold's return type follows);:1324resolvedExportOptions?.streaming !== false;:2976exportConfig?.streaming !== false. PM mechanism assumption, measured: they compile with NO annotation —pnpm --filter @object-ui/plugin-list type-checkexits 0 against the rebuilt types dist, and the ablation below turns exactly those two lines red (TS2339) against the old mirror, so the green is the rebuilt.d.ts, not a cache. The fold at:1299to:1308STAYS.handleExport(:2943) still names'pdf'in its parameter type: measured, no live export path implements it —serverEligibleadmits csv / xlsx / json only, the client fallback branches on csv and json (a'pdf'call falls through both and does nothing), andexportableFormatsnever offers it (filtered bysupported). The parameter type is left alone as dispatched: a runtime signature, not the authoring contract.packages/types/src/__tests__/export-options-spec-parity.test.ts) widened to the mirror: identity with the spec field; the bare array lifts to{ formats }through the wholelist-viewnode while the TS face admits it (satisfies);'pdf'refused in both spellings on theexportOptionspath with the migration prescription, and refused by the TYPE in both spellings (@ts-expect-error); sixth key refused (strict) at both levels;streaming: true/falseaccepted with the value SURVIVING the parse; spec-vs-mirror parity verdict-for-verdict AND output-for-output on the four readings, with a non-vacuity assertion on the verdict vector[true, false, false, true]; type-level pins that the face carriesstreamingasboolean | undefinedand that the array arm's element is csv / xlsx / json; controls:conditionalFormatting(both shapes accept, a non-array is refused),allowExport, and the bare envelope. The objectui#4535 legs are unchanged.'pdf'under the ListView type —p1-spec-alignment.test.ts:240(should accept exportOptions as ObjectUI object format). Its subject is the object form, not the value, so the spelling changes (['csv', 'json']), not the test.plugin-list'sListView.test.tsx:2228and:2264author'pdf'throughas anyto pin that a stored dead format is dropped from the menu; they compile as-is and stay (no other line inplugin-listmoves).@object-ui/types: minor("Breaking for authored metadata", naming the spec authority and the four readings) and@object-ui/plugin-list: patch(cast removals, no behaviour change, the fold stays).Gates (exit captured before any pipe; verdict lines quoted from the gate)
HEAD for every row is the merged head
216c7490; identical readings were taken ona7e32588before the merge.turbo run build --filter='@object-ui/app-shell^...' --concurrency=2, under the verify lockTasks: 28 successful, 28 total;VERDICT command-exit 0; dist markerobjectql.zod.d.tspdf 0, streaming 2pnpm --filter @object-ui/types type-check(three tsc projects incl.tsconfig.test.json)error TSpnpm --filter @object-ui/plugin-list type-checkerror TSpnpm --filter @object-ui/app-shell type-check(forwardsexportOptionsatviews/ObjectView.tsx:2266)error TSpnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-list/, under the verify lockTest Files 187 passed (187),Tests 2906 passed (2906);VERDICT command-exit 0pnpm --filter @object-ui/types lint,pnpm --filter @object-ui/plugin-list lintcheck-changeset-presence·-fixed·-no-major·-overwritecheck-control-bytespnpm check:spec-symbolsCLAIM_DEBTledger is untouched and stays green under the derivationcheck:doc-types·check:doc-fences·check-doc-linkscheck:phantom-deps·check:unreferenced-sources·check:esm-specifierscheck-governed-merges.mjs --teston all 7 paths)Ablation — mirror reverted to the
9587fc95blob (HEAD committed first; restore trap on EXIT / INT / TERM)Mutation proven on disk:
git hash-objectof the file =70fec93c…=git rev-parse 9587fc95:…(HEAD blobb20e082b…); marker counts: new-member 0, old-enum 2. Types dist rebuilt and proven to have moved:objectql.zod.d.tspdf 0 → 4, streaming 2 → 0.Tests 6 failed | 8 passed (14)— exactly the six finding(types/plugin-list): the ListView leg of theexportOptionsreconciliation never landed — zod mirror still takes'pdf'and declares nostreaming, and bothas anyreads survive #6956 legs red (identity, lift, pdf refusal, sixth key, streaming survival, four-reading parity); the eight objectui#4535 legs and the controls green.tsc -p tsconfig.test.jsoninpackages/types: 6 errors, all in the new pins (twoType 'false' does not satisfy the constraint 'true', oneProperty 'streaming' does not exist, two unused@ts-expect-error, one excess-propertystreaming).plugin-listtsc --noEmit: 2 errors —ListView.tsx(1324)and(2976), TS2339streamingdoes not exist. Direction: turns red, as predicted.Restore proven: on-disk blob = HEAD blob
b20e082b…,git diff HEAD0 lines, dist markers back to pdf 0 / streaming 2, plugin-list tsc 0 errors, types test-project tsc 0 errors, parity file 14 / 14.The parity test imports the mirror from
../zod/objectql.zod.js(source, relative), so the vitest legs measure source; the two tsc legs measure the rebuilt dist.Out of scope
exportOptionsarray on a directly-authoredobject-gridnode passes the zod mirror unvalidated and silently degrades to the csv/json default #7762 — filed (unlabelled, unassigned, for triage): a bareexportOptionsarray on a directly-authoredobject-gridnode passes theobject-gridzod mirror unvalidated (that mirror declares noexportOptionsmember;BaseSchemais passthrough) andObjectGrid.tsx:2956degrades it silently to['csv', 'json']. The SpecBridge route is already covered by objectui#4585 (landed via PR fix(react): SpecBridge lifts a bare exportOptions array to the spec's object form (#4585) #4587); the saved-view route folds inListView.scripts/check-spec-symbol-derivation.mjsCLAIM_DEBTentry forListViewExportOptions— not touched (held by the When bumping @objectstack/spec past objectstack#14075: list-view spec-parity pins need the CalendarConfig titleField-optional update #7122 chain); the gate stays green with the derivation.Merged
origin/mainat83c77dc3and again at3e377c93(PRs #7737, #7736, #7739 — none touch these files). Session:session_01BAZFhALsQsGqxui8sNqM8s.Generated by Claude Code