diff --git a/.changeset/6349-name-authority-batch-3.md b/.changeset/6349-name-authority-batch-3.md new file mode 100644 index 0000000000..3f4bb1e2f5 --- /dev/null +++ b/.changeset/6349-name-authority-batch-3.md @@ -0,0 +1,35 @@ +--- +'@object-ui/components': minor +'@object-ui/plugin-grid': patch +'@object-ui/app-shell': patch +--- + +One authority per exported type name, batch 3 of objectui#6349: `ComboboxOption`, +`NamedActionDef`, `OrgTranslate`. + +**`@object-ui/components` — `ComboboxOption` now IS `@object-ui/types`' declaration.** +The component declared its own `{ value, label }`, a strict subset of the +`ComboboxOption` that `@object-ui/types` declares for `ComboboxSchema.options` and +mirrors in `form.zod.ts` (`{ value, label, disabled? }`). The component now re-exports +the types declaration (through the `@object-ui/types/form` subpath — the root barrel +does not publish the name), so the name `ComboboxOption` exported from +`@object-ui/components` gains the optional `disabled?: boolean` member. Every value +that type-checked before still does — nothing narrows and no key changes type; the +one thing that moves is `keyof ComboboxOption`, so a consumer that EXHAUSTS the type +(a `Record` over its keys) will need the new key. Note that the `Combobox` component +itself does not read `option.disabled` — that member was already declared on the +`@object-ui/types` face and is now visible on this one too; it is recorded as a +separate finding, not changed here. + +**`@object-ui/plugin-grid` / `@object-ui/app-shell` — internal, surface unchanged.** +`NamedActionDef` was declared identically in `resolveBulkActions.ts` and +`resolveLegacyRowActions.ts`; the latter is now the one authority and the former +re-exports it. `OrgTranslate` was declared identically in `orgErrorMessage.ts` and +`orgRoleLabel.ts`; the former is now the one authority and the latter re-exports it. +Neither name is on its package's public entry, and every deep-`dist` module still +exports the same name with the same shape. + +`FilterBuilderCondition` / `FilterGroup` (the other two names this batch was sized +with) are deliberately NOT converged: their shapes disagree on `id`, `value` and on +`operator`, and the only dependency-legal re-point would retype `operator` — the +vocabulary objectui#7561 is asking a maintainer to rule on. diff --git a/packages/app-shell/src/console/organizations/orgRoleLabel.ts b/packages/app-shell/src/console/organizations/orgRoleLabel.ts index 62acc8d171..679e9b8e27 100644 --- a/packages/app-shell/src/console/organizations/orgRoleLabel.ts +++ b/packages/app-shell/src/console/organizations/orgRoleLabel.ts @@ -55,12 +55,13 @@ import { ORG_ROLE_LABELS } from '@object-ui/auth'; import type { OrgRole } from '@object-ui/auth'; +// One authority for `OrgTranslate` (objectui#6349): `orgErrorMessage.ts` +// declares the translate-function shape both organization helpers take, and +// this module re-exports it so an import from `./orgRoleLabel.js` keeps +// resolving to the same type. A re-export, not a second declaration. +import type { OrgTranslate } from './orgErrorMessage.js'; -/** - * The translate function shape this module needs — structurally what - * `useObjectTranslation().t` and `createSafeTranslation()` both provide. - */ -export type OrgTranslate = (key: string, options?: Record) => string; +export type { OrgTranslate } from './orgErrorMessage.js'; /** True when `role` is one of the four framework-owned membership roles. */ function isKnownRole(role: string): role is OrgRole { diff --git a/packages/components/src/custom/combobox.tsx b/packages/components/src/custom/combobox.tsx index 965f7612a6..6eae8e5468 100644 --- a/packages/components/src/custom/combobox.tsx +++ b/packages/components/src/custom/combobox.tsx @@ -27,10 +27,13 @@ import { PopoverTrigger, } from "../ui/popover" -export interface ComboboxOption { - value: string - label: string -} +// One authority for `ComboboxOption` (objectui#6349): `@object-ui/types` +// declares it (`packages/types/src/form.ts` — the shape `ComboboxSchema.options` +// carries and `form.zod.ts` mirrors), and this component read a strict SUBSET +// of it, `value` and `label`. The `./form` subpath is the door because the +// root barrel does not publish the name. A re-export, not a second declaration. +import type { ComboboxOption } from "@object-ui/types/form" +export type { ComboboxOption } from "@object-ui/types/form" /** * Beyond its own named props, the combobox accepts standard button attributes diff --git a/packages/plugin-grid/src/resolveBulkActions.ts b/packages/plugin-grid/src/resolveBulkActions.ts index a6b5e79627..ba993c40bc 100644 --- a/packages/plugin-grid/src/resolveBulkActions.ts +++ b/packages/plugin-grid/src/resolveBulkActions.ts @@ -64,12 +64,12 @@ */ import type { BulkActionDef, BulkActionParam } from '@object-ui/types'; - -/** The subset of an `ActionDef` this fold reads; everything else is carried. */ -export interface NamedActionDef { - name?: string; - [key: string]: unknown; -} +// One authority for `NamedActionDef` (objectui#6349): `resolveLegacyRowActions.ts` +// declares the `ActionDef` subset both folds read, and this module re-exports +// it so the name stays reachable from here exactly as before. A re-export, not +// a second declaration — the one-authority gate counts declarations only. +import type { NamedActionDef } from './resolveLegacyRowActions'; +export type { NamedActionDef } from './resolveLegacyRowActions'; /** * Concurrency for a promoted action's per-record fan-out. Well below the diff --git a/scripts/__tests__/one-authority-per-exported-name-6273.test.ts b/scripts/__tests__/one-authority-per-exported-name-6273.test.ts index 906488fbea..49574c8ac1 100644 --- a/scripts/__tests__/one-authority-per-exported-name-6273.test.ts +++ b/scripts/__tests__/one-authority-per-exported-name-6273.test.ts @@ -364,7 +364,12 @@ const KNOWN_COLLISIONS: ReadonlyMap = new Map([ ['CalendarSchema', ['packages/plugin-calendar/src/ObjectCalendar.tsx', 'packages/types/src/form.ts']], ['ChatMessage', ['packages/plugin-chatbot/src/ChatbotEnhanced.tsx', 'packages/types/src/complex.ts']], ['ChatToolInvocation', ['packages/plugin-chatbot/src/ChatbotEnhanced.tsx', 'packages/types/src/complex.ts']], - ['ComboboxOption', ['packages/components/src/custom/combobox.tsx', 'packages/types/src/form.ts']], + // `ComboboxOption` sat here, colliding between + // `packages/components/src/custom/combobox.tsx` and `packages/types/src/form.ts`. + // The component's copy was a strict SUBSET (`value`, `label`; the types copy + // adds `disabled?`), so the component re-points at the one authority in + // `@object-ui/types` — through the `./form` subpath, since the root barrel + // does not publish the name (objectui#6349). // `ComponentConfig` sat here, colliding between // `packages/core/src/registry/Registry.ts` and `packages/types/src/base.ts`. // objectui#6298 made `@object-ui/types` the one authority — it gained the @@ -385,6 +390,14 @@ const KNOWN_COLLISIONS: ReadonlyMap = new Map([ ['Diagnostic', ['packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts', 'packages/cli/src/commands/doctor.ts', 'packages/sdui-parser/src/types.ts']], ['DiagnosticLevel', ['packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts', 'packages/cli/src/commands/doctor.ts']], ['DomProps', ['packages/core/src/utils/dom-props.ts', 'packages/fields/src/widgets/toDomProps.ts']], + // ⚠️ `FilterBuilderCondition` / `FilterGroup` are NOT one shape declared twice: + // the component's condition carries a required `id`, a required narrow `value` + // and `operator: string` (the dropdown's camelCase ids); the types copy has no + // `id`, `value?: any` and `operator: FilterBuilderOperator` (the snake_case + // union). Both headers claim ONE concept, so the remedy is a re-point, and the + // only dependency-legal direction (components -> types) retypes `operator` — + // the vocabulary objectui#7561 is asking a maintainer to rule on. Both rows + // wait for that ruling (objectui#6349, batch 3, stop-and-report). ['FilterBuilderCondition', ['packages/components/src/custom/filter-builder.tsx', 'packages/types/src/complex.ts']], ['FilterBuilderOperator', ['packages/components/src/custom/filter-builder.tsx', 'packages/types/src/complex.ts']], ['FilterGroup', ['packages/components/src/custom/filter-builder.tsx', 'packages/types/src/complex.ts']], @@ -428,8 +441,12 @@ const KNOWN_COLLISIONS: ReadonlyMap = new Map([ // the RENAME branch: app's declaration now spells `AppMenuItem`, the name // `src/index.ts` always published it under (objectui#6349). ['MetadataTypeStatus', ['packages/app-shell/src/providers/MetadataProvider.tsx', 'packages/react/src/context/AppShellContext.tsx']], - ['NamedActionDef', ['packages/plugin-grid/src/resolveBulkActions.ts', 'packages/plugin-grid/src/resolveLegacyRowActions.ts']], - ['OrgTranslate', ['packages/app-shell/src/console/organizations/orgErrorMessage.ts', 'packages/app-shell/src/console/organizations/orgRoleLabel.ts']], + // `NamedActionDef` sat here — two IDENTICAL declarations inside plugin-grid. + // `resolveLegacyRowActions.ts` is the one authority; `resolveBulkActions.ts` + // re-exports it (objectui#6349). + // `OrgTranslate` sat here — two IDENTICAL declarations inside app-shell's + // organizations console. `orgErrorMessage.ts` is the one authority; + // `orgRoleLabel.ts` re-exports it (objectui#6349). ['PageHeaderComponentProps', ['packages/app-shell/src/layout/PageHeader.tsx', 'packages/layout/src/PageHeader.tsx']], ['RecordDetailDrawerProps', ['packages/plugin-dashboard/src/RecordDetailDrawer.tsx', 'packages/plugin-detail/src/RecordDetailDrawer.tsx']], ['SchemaNode', ['packages/sdui-parser/src/types.ts', 'packages/types/src/base.ts']],