Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .changeset/i18nlabel-comment-4611.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
'@object-ui/types': patch
---

`ActionParam`'s doc block no longer claims that spec 17 narrowed `I18nLabel` to a
plain string (objectui#4611).

The paragraph explaining why `label` / `options[].label` are inherited rather than
locally overridden justified itself with a claim about `@objectstack/spec` that was
never true: "in spec 17 `I18nLabelSchema` is `z.ZodString` — inline per-locale objects
were dropped in favour of translation files". Measured against the installed GA pin
`@objectstack/spec@17.0.0` (`dist/ui/index.d.ts:614`), `I18nLabelSchema` is a union of
a string and a string-to-string record, and the schema's own doc block states two
authorized forms with "Both are real; neither is deprecated by this schema". Executed
against `dist/ui/index.mjs`: plain string accepted, inline locale map accepted,
`{ key, defaultValue }` rejected. A reader who believed the comment would have taken a
widening to `string | I18nLabel` for a no-op — which is what the finding recorded, one
seat having nearly done exactly that.

The replacement describes what `I18nLabel` admits and cites the spec's own doc block
rather than restating a zod expression; where today's spelling is named it is scoped as
a measurement against 17.0.0 with its file and line, so it ages as a reading rather than
as a standing fact. The decision itself is unchanged and never depended on the false
premise — `label` flows in by reference through the spec's schema, and a local
`string | I18nLabel` collapses to `I18nLabel` whichever forms the union holds.

Documentation only, and the release-visible surface is the declaration file: measured
with the package's real `tsc` build (`removeComments: false`, per `tsconfig.base.json`),
108 emitted files on both sides, `dist/ui-action.d.ts` 29,176 → 31,026 bytes, and every
other file byte-identical — including `dist/ui-action.js` (3,480 bytes, unchanged sha),
because the comment documents an `interface`, which is erased at emit along with its
leading comment. No behaviour changes; hover text and the shipped `.d.ts` do.
47 changes: 38 additions & 9 deletions packages/types/src/ui-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,15 +312,44 @@ export type ResolvableParamFieldType = ActionParamFieldType | ObjectUiLocalParam
* regex?) and adding it to `@objectstack/spec`, which is where such a capability
* would have to start.
*
* `label` and `options[].label` are NOT pinned, though the comment above used
* to justify them as a widening: "labels take the spec's `I18nLabel` (a string
* or a per-locale record)". In spec 17 `I18nLabelSchema` is `z.ZodString` —
* inline per-locale objects were dropped in favour of translation files — so
* the local override had become an exact restatement of the spec's own type
* while still claiming to be wider than it. Both keys are now inherited.
* (`__tests__/page-nav-misc-spec-parity.test.ts` pins that reading, so the day
* the spec re-widens `I18nLabel` this decision gets re-made rather than
* inherited.)
* `label` and `options[].label` are NOT pinned: both flow in by reference with
* the rest of the spec's keys. The comment here used to justify a local
* override as a widening — "labels take the spec's `I18nLabel` (a string or a
* per-locale record)" — and then justified deleting that override with a claim
* about the spec that was never true: "in spec 17 `I18nLabelSchema` is
* `z.ZodString`, inline per-locale objects were dropped in favour of
* translation files". The spec never narrowed that way (objectui#4611).
*
* What `I18nLabel` admits, stated in as many words by its own doc block in
* `@objectstack/spec/ui` (objectstack#5728, maintainer ruling 2026-08-06): a
* display label in one of TWO authorized forms — a plain default-language
* string, whose translations live in a translation bundle addressed by
* convention, or an inline locale map keyed by locale tag (`en`, `zh-CN`, ...)
* picked at render time — closing with "Both are real; neither is deprecated
* by this schema." The map form is live here, not theoretical: this repo's
* renderers resolve it through `pickLocalized` (e.g. `schema.title` in
* `packages/components/src/renderers/layout/containers.tsx`), and the spec
* ships `resolveI18nLabel` as the matching resolver on its own side.
*
* Deleting the override was still right, for a reason that does not depend on
* which forms the union holds: a plain string is one of the forms `I18nLabel`
* admits, so a local `string | I18nLabel` collapses to `I18nLabel` — an exact
* restatement of the inherited type while claiming to be wider than it.
* Inheriting by reference is what keeps these keys correct as the spec's set of
* authorized forms moves; an override is what cannot.
*
* If you need today's spelling rather than the authorized forms, measure it:
* against `@objectstack/spec@17.0.0` it is `z.ZodUnion([z.ZodString,
* z.ZodRecord(z.ZodString, z.ZodString)])` (`dist/ui/index.d.ts:614`). That is
* a reading of one version, not a standing fact about the schema — which is
* the distinction the sentence this replaces failed to make.
*
* NOT guarded: the `it(...)` case in
* `__tests__/page-nav-misc-spec-parity.test.ts` that calls itself an inverted
* pin on this decision asserts only that a plain string is assignable to
* `I18nLabel`, which holds under either form — so the widening above went
* through it green. Filed as objectui#5612; do not read that case as coverage
* for this paragraph.
*
* Drift guard: `__tests__/page-nav-misc-spec-parity.test.ts`.
*/
Expand Down
Loading