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
15 changes: 10 additions & 5 deletions .changeset/8655-object-tree-props-schema-typed.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
`ObjectTreeProps.schema` is the published `object-tree` node instead of `any`,
and `getTreeConfig`'s parameter with it (objectui#8655).

The type is DERIVED, not written out: `Extract< ObjectQLComponentSchema, { type:
The type WAS DERIVED rather than written out: `Extract< ObjectQLComponentSchema, { type:
'object-tree' } >`, the spelling that union's own docblock teaches. So every key
name and key type still has exactly one declaration, `ObjectTreeSchema` in
`@object-ui/types`, and a key added or retyped there arrives here without an
edit. `Extract` rather than a named import because the TS barrel does not export
`ObjectTreeSchema` — the zod barrel does, its nine siblings are all on the TS
one, and repairing that omission is a published-surface addition on another
package, so it is reported rather than smuggled in here.
edit. `Extract` rather than a named import because, when this landed, the TS barrel did
not export `ObjectTreeSchema` — the zod barrel did, and its eleven sibling arms
were all on the TS one. Repairing that omission was a published-surface addition
on another package, so it was reported rather than smuggled in here, and
objectui#9550 took it: the name is on that barrel in THIS SAME RELEASE and this
package now imports it — see that entry. ⚠️ So the derivation described above is the
state this card left behind, ⛔ not the state being released; the shipped code reads
as a named import. The claim that survives both spellings is the one that mattered:
one declaration, `ObjectTreeSchema`, and no copy of it here.

Accept-set change on the published props type, stated plainly:

Expand Down
35 changes: 35 additions & 0 deletions .changeset/9550-object-tree-root-barrel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
'@object-ui/types': minor
---

Export `ObjectTreeSchema` from the `@object-ui/types` root barrel (objectui#9550)

`ObjectQLComponentSchema` declares the node types an ObjectQL block may be.
Every one of its arms was a named export of this package's root barrel except
`ObjectTreeSchema`, which was declared in `objectql.ts`, applied by the union,
and re-exported by the `./zod` barrel (objectui#7917) — while no TypeScript
consumer could name it. There is no `./objectql` subpath to reach around the
barrel: the package's `exports` map is pinned by
`packages/types/src/__tests__/package-exports-manifest.test.ts`, and the root
barrel was the only route to this type.

The omission was not inert. The seat that stopped `ObjectTreeProps.schema`
being `any` in `@object-ui/plugin-tree` (objectui#8655) could not import the
name, so it had to spell the node as
`Extract< ObjectQLComponentSchema, { type: 'object-tree' } >` — an idiom that
worked, and that every reader of that file had to decode. A type nobody
can import mints a fresh hand-written copy of itself for each consumer that
needs it, which is the second-authority shape objectui#6349 is burning down.

The change is one name added to an existing explicit named re-export list.
Nothing is removed, retyped or narrowed: `Extract` off the union still
resolves, and `packages/types/src/__tests__/object-tree-root-barrel-9550.test.ts`
pins that the imported name and that `Extract` are the same declaration, that
the list stays explicit rather than becoming a wildcard, and that the
declaration itself stays in `objectql.ts`.

The one hand-written copy this gap had already minted is retired in the same change: `@object-ui/plugin-tree` now imports the name instead of re-deriving it. See that package's own entry.

⛔ Not done here: a gate over barrel completeness for every declared node
schema. That was the card's own third option and it is a wider design with its
own review; objectui#9526 is the sibling card in the same family.
26 changes: 26 additions & 0 deletions .changeset/9550-plugin-tree-import-object-tree-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@object-ui/plugin-tree': patch
---

Import `ObjectTreeSchema` instead of re-deriving it (objectui#9550)

`ObjectTree` typed its node by narrowing the published union on the `object-tree`
tag and binding that to a module-local alias. That spelling was correct and it was
forced: the name it wanted was declared in `@object-ui/types` and re-exported by
the `./zod` barrel, but the root barrel did not carry it, so there was nothing to
import. objectui#9550 put the name on that barrel, and this package now imports it.

Nothing about the type changes — the imported name and the narrowing resolve to the
same declaration, which `ObjectTree.schemaTyped-8655.test.ts` asserts invariantly and
independently through `ObjectTreeProps['schema']`. What changes is that the renderer
no longer carries a derived restatement of a published type. A type nobody can import
mints one of those per consumer, and each one is correct on the day it is written —
that is the second-authority shape objectui#6349 is burning down, and this was one of
its instances rather than a tidy-up.

The docblock's justification paragraph went with it: it stated, as its measured reason
for choosing the narrowing, that the barrel omits the name so it "cannot be imported
today". That sentence is false as of objectui#9550 and nothing re-derived it. The
objectui#8651 warning beside it is KEPT and re-pointed at the import: a module-local
type wearing a published type's name is the two-layers-one-word trap, and it is more
load-bearing now that the published name is importable, not less.
12 changes: 10 additions & 2 deletions packages/plugin-tree/src/ObjectTree.schemaTyped-8655.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,15 @@ const HERE = dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = join(HERE, '..', '..', '..');
const TREE_READER = 'packages/plugin-tree/src/ObjectTree.tsx';

/** The node the checker now sees at every read, spelled the way the renderer spells it. */
/**
* The node spelled INDEPENDENTLY of the renderer: narrowed off the published
* union, here and only here. ⭐ A deliberate SECOND spelling, kept on purpose —
* `ObjectTree.tsx` imports `ObjectTreeSchema` by name as of objectui#9550, so the
* ① row below is a CONSUMER-SIDE proof that the named import and the union arm
* are one declaration. ⛔ Do not "tidy" this into the same import the renderer
* uses: two independently written spellings that must agree IS the assertion, and
* one spelling agreeing with itself asserts nothing.
*/
type ObjectTreeNode = Extract<ObjectQLComponentSchema, { type: 'object-tree' }>;

/** A key nothing reads and nothing declares — the both-ways control. */
Expand Down Expand Up @@ -205,7 +213,7 @@ type DeclaredKeys<T> = keyof {
};
type Declares<T, K extends PropertyKey> = K extends DeclaredKeys<T> ? true : false;

/** ① — the prop is the published node, derived off the union, and it is not `any`. */
/** ① — the prop is the published node, equal to the union arm, and it is not `any`. */
export type _PropIsThePublishedNode = Expect<Equal<ObjectTreeProps['schema'], ObjectTreeNode>>;
export type _PropIsNotAnyAnyMore = Expect<Equal<IsAny<ObjectTreeProps['schema']>, false>>;
/** The `Equal` helper can FAIL — synthetic control, so the two rows above count. */
Expand Down
58 changes: 24 additions & 34 deletions packages/plugin-tree/src/ObjectTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,28 @@
*/

import React, { useEffect, useMemo, useState } from 'react';
import type { DataSource, ObjectQLComponentSchema, TreeViewConfig } from '@object-ui/types';
/**
* The `object-tree` NODE this renderer draws, imported BY NAME from the
* published barrel (objectui#8655 step ①; objectui#9550 is what put the name
* on that barrel, which is what makes a named import possible at all).
*
* ⛔ Not a second declaration of the node's shape. `ObjectTreeSchema` in
* `@object-ui/types` is the one declaration — the interface whose own docblock
* calls itself the "Object Tree (tree-grid) Component Schema", and which
* `views.ts` names as "the node an author writes". Every key and every key TYPE
* arrives from there, so a key added, renamed or retyped on it reaches this
* renderer without an edit here: the property a hand-copied interface cannot
* have, and the same derivation `ResolvedTreeConfig` below already uses against
* `TreeViewConfig`.
*
* ⛔ Never re-declared LOCALLY under this name, and not aliased on the way in:
* a module-local type under a published type's name is the two-layers-one-word
* trap objectui#8651 recorded when a local `CalendarSchema` shadowed the
* published one. That is what makes the line below a plain named import, and
* the warning is MORE load-bearing now that the published name is importable,
* not less.
*/
import type { DataSource, ObjectTreeSchema, TreeViewConfig } from '@object-ui/types';
import {
useNavigationOverlay,
useSafeFieldLabel,
Expand Down Expand Up @@ -78,37 +99,6 @@ const useTreeTranslation = createSafeTranslation(
'detail.recordDetail',
);

/**
* The `object-tree` NODE, taken off the PUBLISHED union rather than written
* out here (objectui#8655 step ①).
*
* ⛔ Not a second declaration of the node's shape. `ObjectTreeSchema` in
* `@object-ui/types` is the one declaration — the interface whose own docblock
* calls itself the "Object Tree (tree-grid) Component Schema", and which
* `views.ts` names as "the node an author writes". Every key and every key TYPE
* arrives from there, so a key added, renamed or retyped on it reaches this
* renderer without an edit here: the property a hand-copied interface cannot
* have, and the same derivation `ResolvedTreeConfig` below already uses against
* `TreeViewConfig`.
*
* ⚠️ `Extract` off `ObjectQLComponentSchema`, and NOT a named import, for a
* measured reason: `ObjectTreeSchema` is declared in `objectql.ts` and re-exported
* from the ZOD barrel (objectui#8784 / PR #8777 repaired that half), but the TS
* barrel's `export type { … } from './objectql.js'` block omits it, while its nine
* siblings — `ObjectMapSchema`, `ObjectGanttSchema`, `ObjectCalendarSchema`,
* `ObjectKanbanSchema`, `ObjectChartSchema`, `ObjectGallerySchema`,
* `ObjectDataTableSchema`, `ObjectGridSchema`, `ObjectFormSchema` — are all on it.
* So the name cannot be imported today. ⛔ That omission is NOT repaired here: it
* is a published-surface addition on another package and belongs to whoever files
* it. `Extract` is the spelling `ObjectQLComponentSchema`'s own docblock teaches,
* and it needs nothing added to any published face.
*
* ⛔ Deliberately NOT spelled `ObjectTreeSchema` locally: a module-local type under
* a published type's name is the two-layers-one-word trap objectui#8651 recorded
* when a local `CalendarSchema` shadowed the published one.
*/
type ObjectTreeNodeSchema = Extract<ObjectQLComponentSchema, { type: 'object-tree' }>;

export interface ObjectTreeProps {
/**
* The `object-tree` node this renderer draws.
Expand Down Expand Up @@ -136,7 +126,7 @@ export interface ObjectTreeProps {
* question becomes ANSWERABLE by the checker, ⛔ not that an undeclared read is
* refused — the same ceiling objectui#5155 / objectui#7927 record for the mirror.
*/
schema: ObjectTreeNodeSchema;
schema: ObjectTreeSchema;
dataSource?: DataSource;
className?: string;
/**
Expand Down Expand Up @@ -242,7 +232,7 @@ function fieldKey(f: any): string | undefined {
* `parentField` alone) — else delete the read. This is that deletion, executed
* on objectui#8841.
*/
function getTreeConfig(schema: ObjectTreeNodeSchema): ResolvedTreeConfig {
function getTreeConfig(schema: ObjectTreeSchema): ResolvedTreeConfig {
const nested = (schema.tree || schema.filter?.tree || {}) as TreeViewConfig;
const rawFields = Array.isArray(schema.fields)
? schema.fields
Expand Down
Loading
Loading