Repository navigation
fix(types,core,plugin-list,plugin-view,app-shell): derive ViewType from @objectstack/spec instead of re-declaring it - #8372
Merged
Conversation
…om the spec instead of re-declaring it `ViewType` (packages/types/src/views.ts) and its zod face `ViewTypeSchema` were hand-written eleven-arm copies of `@objectstack/spec`'s list-view type vocabulary. `@objectstack/spec@17.3.0` added `page`; neither copy followed, so every structure keyed on them was total over the COPY and compiled green while being incomplete — including `LIST_VIEW_KINDS`, whose own doc comment promises that "a kind added to the union fails the build HERE". Measured on 5505aec: `@object-ui/types`' published validator ACCEPTS `viewType: 'page'` (its `ViewKindEnum` was already derived) while `normalizeListViewSchema` resolved `page` to exactly what it resolved a typo to — `viewType: 'grid'`, silently. Both faces now derive from `@objectstack/spec/ui` `ListView['type']`, and the renderer-side structures derive from that in turn. `@object-ui/core` gains `ListViewVisualization` / `isListViewVisualization` for the kinds `ListView` actually draws, which exclude `page` for the same reason the spec's own `VisualizationType` does. A spec-valid but undrawable kind still falls back to a grid, but says so once instead of degrading identically to a typo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QtGhnU3WnnWyiWeYQhw2aX
Building them from a stem — t(`console.objectView.viewType${stem}`) — type-checks
fine and silently downgrades ten locale packs' worth of coverage to a prefix
check. `check:check-i18n-call-site-keys` caught it as
`[undeclared-dynamic-family]`: it can only see a member missing from every pack
when the key is a literal argument.
That is the same "a guard that stopped guarding" shape objectui#8127 exists to
remove, so it would have been a poor trade to introduce here. Totality still
comes from the `Record` over the derived visualization union; checkability comes
from the literals inside it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QtGhnU3WnnWyiWeYQhw2aX
…wtype-derive-from-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
|
This was referenced Sep 7, 2026
os-zhuang
marked this pull request as ready for review
September 7, 2026 22:48
os-justin
pushed a commit
that referenced
this pull request
Sep 8, 2026
`page` is simultaneously a list-view type `@objectstack/spec`'s own `ListViewSchema` accepts, a member of both published `@object-ui/types` faces, and absent from the set `ListView` draws -- so a spec-valid `type: 'page'` view normalises to `viewType: 'grid'` with its `pageName` mount target left unread in the metadata. PR #8372 closed the DECLARATION half of objectui#8127 by deriving `ViewType` from the spec. This states the renderer half that derivation made visible rather than removed, mechanically instead of in prose. Disposition-free by construction: it asserts today's behaviour, so it reddens if a page renderer is grown (the residual set empties and the normalised output stops being `grid`) and equally if `ViewType` is narrowed back (the published faces stop accepting `page`). Neither open ruling can land silently now. Every set is asserted at its exact size before use -- a census over an empty set passes -- and the `grid` reading carries a discrimination control so a normalizer that answered `grid` for everything would fail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S
akarma-synetal
pushed a commit
to akarma-synetal/objectui
that referenced
this pull request
Sep 9, 2026
…ck-ai#8684) `page` is simultaneously a list-view type `@objectstack/spec`'s own `ListViewSchema` accepts, a member of both published `@object-ui/types` faces, and absent from the set `ListView` draws -- so a spec-valid `type: 'page'` view normalises to `viewType: 'grid'` with its `pageName` mount target left unread in the metadata. PR objectstack-ai#8372 closed the DECLARATION half of objectui#8127 by deriving `ViewType` from the spec. This states the renderer half that derivation made visible rather than removed, mechanically instead of in prose. Disposition-free by construction: it asserts today's behaviour, so it reddens if a page renderer is grown (the residual set empties and the normalised output stops being `grid`) and equally if `ViewType` is narrowed back (the published faces stop accepting `page`). Neither open ruling can land silently now. Every set is asserted at its exact size before use -- a census over an empty set passes -- and the `grid` reading carries a discrimination control so a normalizer that answered `grid` for everything would fail. Claude-Session: https://claude.ai/code/session_01YBWFb5YgMU5dw8p2VKj16S Co-authored-by: Claude <noreply@anthropic.com>
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 #8127
What this changes
ViewTypeand its zod faceViewTypeSchemastop being hand-written copies of@objectstack/spec's list-view type vocabulary and become derivations of it. Everystructure keyed on them follows from that, so the exhaustiveness guarantee they were
supposed to provide is real again rather than total-over-a-copy.
Per the triage ruling on the card, this is the derivation route —
pageis nothand-added to an eleven-arm literal.
The measurement that decided the design
The spec models two unions here, and conflating them is what makes this look like
"add
pagein twelve places" when it is not:ListViewSchema.shape.typepageVisualizationTypeSchemapageAppearanceConfig.allowedVisualizationsis typed onThe spec's own docblock on the expanded list-view schema says why: it carries
"the
type: 'page'⇄pageNamebinding check". Atype: 'page'view mounts apublished page in place of rows; it is not a visualization that draws records.
⇒
ViewTypemust gainpage. Structures that enumerate what a renderer drawsmust not — and now cannot, because they derive from the subtraction rather than
restate a list.
The sharpest statement of the bug, measured on
5505aec@object-ui/types' published validator already acceptedviewType: 'page'— itsViewKindEnum(zod/objectql.zod.ts) was derived from the spec all along:while the renderer resolved it to exactly what it resolved a typo to:
That is
declared !== enforcedon a published surface: the validator says yes, therenderer silently draws something else, and a spec-valid view is byte-indistinguishable
from a misspelling. The derivation mechanism already existed in this repo — it had just
never been applied to the two faces the card names.
The promise is true again — ablation
LIST_VIEW_KINDS' docblock promises "a kind added to the union fails the build HERE".Adding a synthetic member to the derived union, rebuilding
@object-ui/typesso itreaches
dist, then type-checking@object-ui/core:Restore leg: source clean,
MARKER_IN_DIST=0,@object-ui/coretype-check exit 0.Build-failing vs merely
page-carrying — the honest splitThe card names five lists with no type comparison at all. Four of the five are now
build-failing; none of them merely gained a word.
core/utils/normalize-list-view.tsLIST_VIEW_KINDSListViewVisualization; newUNDRAWABLE_VIEW_KINDSmakes the other half of the union total tooplugin-list/ViewSwitcher.tsxViewType+ its 2 mapsListViewVisualizationplugin-list/index.tsxSDUI enum ×2Record; both had drifted, missingchartandtreeplugin-list/ListView.tsxallowlistisListViewVisualization, the map's own predicateapp-shell/CreateViewDialog.tsxpickertype: string, never comparedRecordtypes/designer.tsUnifiedViewTypeVisualizationType(gainstree)plugin-viewDEFAULT_VIEW_LABELS/DEFAULT_VIEW_ICONS/iconMapViewType, so these do carrypage— they label any view type that existsThe silence is closed, the renderer is not widened
⛔ This PR does not add a page renderer — that is a feature, not this card. A
type: 'page'view still falls back to a grid. What changes is that it says so once,using this repo's existing loud-fail-open convention (
core/evaluator/fieldRules.ts,objectstack#5149), so a spec-valid view no longer degrades identically to a typo.
i18n: ten locale packs, zero new keys — and why
Triage priced this as "⚠️ i18n 是硬工作量:十个语言包,每个要一个 label 和一个 description".
Measured, that work is not owed. Every locale-backed view-type label belongs to a
visualization picker or switcher, and
pageis correctly absent from those byconstruction.
check:i18n-keys,check:i18n-driftandcheck:i18n-dead-keysare allgreen with no locale file touched.
(
t(...viewType${stem}...)) type-checks fine and turns ten packs' worth of coverage intoa prefix check.
check:i18n-keyscaught it ([undeclared-dynamic-family]). The keys areback to literals inside the total record — totality from the
Record, checkability fromthe literals.
验收备注
on
5505aec:zod/views.zod.tsis line 26 (card/comment said 25); theListView.tsxallowlist is 2241 (card said 2224); the SDUI enums carry 7 entries each, not the
nine the card states.
plugin-list/src/ViewSwitcher.tsxdeclared and exported its own nine-arm
ViewType. The card describes that file's twomaps as total over the shared union; they were total over this private copy. One level
deeper than filed.
plugin-view/src/__tests__/ViewSwitcher.test.tsxcarriedconst ALL_VIEW_TYPES: ViewType[] = [...]under the comment "adding a member to the unionwithout extending this list is a compile error here too". That claim was false — a short
array is assignable to an array type; the annotation rejects an invalid member and says
nothing about a missing one. It compiled green at eleven entries and only the runtime
toEqualwent red. Nowsatisfies Record< ViewType, true>, which actually delivers it.ListView.tsx:2293was deliberately left alone — it sitsinside a commented-out
useEffect("DISABLED: interfering with schema-defined views"). Deadcode, not a site.
earlier instances (the capability set,
ObjectFieldGroup.visibleWhen) could not be confirmedas local re-declarations from this checkout: it is shallow and both fixes are outside the log
window. ⛔ Not guessed. What this PR does establish is that the pattern is at least
four re-declarations of this one vocabulary, two of which nobody had filed.
defaultViewType/ designervariant lists), all outside the declared file surface and none of them the
pagedefect:types/objectql.ts:1804and:1988,types/zod/objectql.zod.ts:258,plugin-view/index.tsx:76,app-shell/.../anchors.ts:269,app-shell/.../ViewVariantInspector.tsx:191.Review
needs:contract-reviewis on the card and onthis PR; ⛔ neither carrier cleared by me. Staying draft, not enqueued, auto-merge not armed.
Generated by Claude Code