Repository navigation
fix(types): declare hideEmpty on the record:details section mirror and hold the member set to the spec (objectui#12348) - #12388
Conversation
…and hold the member set to the spec `RecordDetailsComponentProps.sections[]` omitted `hideEmpty`, which `@objectstack/spec` declares on the section and `RecordDetailsRenderer` reads, so a spec-valid section failed to type-check with TS2353. The section-member pin now holds the whole member set (keys and each member's type) to the installed spec in both directions. Also: the `object-grid` `emptyState` describe and its MEMBER_PINS row now account for the spec EmptyStateSchema's fourth member, `link`. Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Contract reviewServed-tier: Inputs read: card #12348 (body and all four comments: ① Derived judgments
File surface honoured: the five files are exactly the claim's four named surfaces plus the changeset. No breach. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #12348
Clause-②: yes — this widens the accept set of the published
@object-ui/typestypeRecordDetailsComponentProps.sections[]by one member,hideEmpty, to follow the spec row. The seat's claim takes the stricter reading over triage'sno, so a contract review is owed before the queue.Contract review: what the section type accepts, against the installed spec
The spec was measured at the PR head (
80506a725, after mergingmainwith objectui#12370):@objectstack/spec18.0.0-next.1. It was measured first at based75f21285on 18.0.0-next.0, and both versions declare the same thirteen section members. The member list was read offRecordDetailsProps.shape.sectionselement shape innode_modules, not copied from the card.RecordDetailsProps.sections[]member@object-ui/typesbeforenamelabelcolumnsgroupfieldshideEmptybooleancollapsibleshowBorderdefaultCollapsedicondescriptionheaderColorcountfield, optionalhideEmptyis the only member added, and nothing is removed. The type now declares exactly the spec's member set.Still refused on a typed section literal: any key the spec does not declare on the section. That includes the retired
collapsed(objectui#8583 item 2) and any misspelling. The type has no index signature, so excess-property checking still applies. The pin'srefusedByTscandretiredSpellingRefusedByTscliterals still compile only through their@ts-expect-errordirectives. Cross-key rules (fieldsversusgroup,countneeding a label hole) stay the spec parser's job, the same as before. The type does not encode them.The ruling this follows: objectui#7129 Q1-A rested on "the spec refuses
hideEmpty". The director's dated note on #7129 records that premise as superseded by the objectui#8603 ruling (batch #137 item 3): spec 17.3.0+ declares the key, and objectui follows the protocol.RecordDetailsRendereralready reads it ass.hideEmpty ?? true, so nothing changes at runtime.What changed
packages/types/src/record-components.ts: the section entry declareshideEmpty?: boolean, with a doc comment covering the renderer default,falsesemantics, and that it is permitted besidegroup.packages/types/src/__tests__/record-details-section-members-8583.test.ts(the types:RecordDetailsComponentProps.sections[]omits six member keys@objectstack/spec17.3.0 declares, and declares one (collapsed) the spec refuses — a spec-valid section does not type-check #8583 pin), extended so the mirror cannot drift in either direction:_HideEmpty: an invariantEqualof the member's type against the spec'sz.inputtype._KeySetMirrorsSpec:Equalofkeyof Sectionandkeyof SpecSection. It goes red undertype-checkon a missing member or an extra one._EveryMemberTypeMirrorsSpec: a mappedEqualover every spec member, so a member added later on both sides is held to the spec's type without anyone writing a new line.MIRRORED_SECTION_KEYSis tied to the type by_ListIsTheTypesKeySet. A vitest case then asserts that list equals the installed spec's section keys, sorted, so drift in either direction is also red undervitest, which strips types.hideEmpty: falseparses clean and survives the parse, both on an enumerated section and besidegroup. A control on the same position refuses a non-boolean value withinvalid_typeatsections.0.hideEmpty.packages/types/src/zod/objectql.zod.ts: theobject-gridemptyStatedescribe now reads{ title, message, icon, link }. This is a text-only change: the schema is the specEmptyStateSchemaby reference, and that schema's shape has four keys on both next.0 and next.1.apps/console/src/__tests__/registry-inputs-spec-parity.test.ts: theMEMBER_PINSrowobject-grid.emptyStatenow says three of the four members are pinned there and nameslink's pin,ObjectGrid.emptyStateLink-12270.test.tsx. This too is a text-only change inside a string literal..changeset/12348-details-sections-mirror.md:@object-ui/typesminor.Verification (head
80506a725)packages/typespnpm type-check(all three legs:tsc --noEmit, examples,tsconfig.test.json): exit 0.pnpm exec vitest run packages/types/ apps/console/src/__tests__/registry-inputs-spec-parity.test.ts: Test Files 381 passed (381), Tests 10577 passed (10577). The console file alone, run at the first commit: 255 passed (255).pnpm check:NAMEexit 0:phantom-deps,self-import,unreferenced-sources,doc-example-readers,handler-key-reads,side-effects-array,element-data-source-declaration,spec-symbols,action-forward-parity,designer-field-key-parity,icon-record-names,i18n-keys,i18n-drift,i18n-designer-parity,new-line-citations. Also exit 0:node scripts/check-changeset-fixed.mjsandnode scripts/check-changeset-presence.mjs.check:spec-floors: exit 1, "The scan collapsed: 0 package(s) declare @objectstack/spec in a consumer-facing field". This failure is already onmain(objectui#12334). It is recorded here and left as it is..tsfiles:eslint --no-inline-config --format jsonreturns 4 files in its JSON (none ignored by the config), with 0 errors and 1 warning. The warning is the existingno-explicit-anyon the base file'sEqual of anydirection proof.eslint.config.jssets noparserOptions.project/projectService, so linting is not type-aware and this diff cannot move the verdict on any untouched file. The repo-widepnpm lintis left to CI.type-check. The reason isPREREQUISITE NOT MET: the workspacedist/for the console's dependency closure is not built in this container, so all 726 errors are module-resolution errors (Cannot find module '@object-ui/...') and their cascades. The run never reached the console's own types. The console edit is a text change inside a string literal. Declared to CI.Ablations (one-off, each mutation verified on disk by
ablation-replace.mjsand restored to the HEAD blob withgit diff HEADempty)hideEmpty?: boolean;from the type madetsc -p tsconfig.test.jsonexit 2 with errors on_HideEmpty(TS2339),_KeySetMirrorsSpec,_EveryMemberTypeMirrorsSpecand_ListIsTheTypesKeySet(TS2344), and TS2353 on both newhideEmptyliterals. This is the card's own TS2353.extraKeyTheSpecLacks?: boolean) made it exit 2 with errors on_KeySetMirrorsSpecand_ListIsTheTypesKeySet. The first two attempts at this leg were no-ops: in each, the replacement text still contained the anchor, so the tool refused with "a drop of 0" and nothing was measured. The third attempt changed the anchor text itself.'hideEmpty'fromMIRRORED_SECTION_KEYSmade vitest fail 1 test of 9, the new set-equality case, with the diff naming"hideEmpty".Acceptance notes (observations, not filed)
sections[].countdescribe still reads "[EXPERIMENTAL — not enforced] … until the record detail renderer reads it", while this type'scountdoc saysRecordDetailsRendererfills the hole (objectui#12192). This is spec-side describe text and outside this card's file surface. Carrier: none.collapsiblemember has no doc comment, unlike its neighbours. This is polish only, and it is not changed here.Overlap
PR #12380 (another
.describe()inobjectql.zod.ts) landed before this branch mergedmain, and the auto-merge was clean.Generated by Claude Code