Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f7cd0c0
feat(types,react)!: a node slot and SchemaRenderer's schema prop take…
claude Oct 2, 2026
bf9768e
fix(types): `app` and `list` are one arm each in DeclaredNode (object…
claude Oct 2, 2026
3b080de
refactor(components,core): page.tsx crosses into DeclaredNode once, a…
claude Oct 2, 2026
cba6c42
test: the test-only node types are declared, deliberately undeclared …
claude Oct 2, 2026
4aad474
docs: examples name the declared node type; a registered custom type …
claude Oct 2, 2026
e4bb726
chore(changeset): objectui#11466, and dated notes on seven pending en…
claude Oct 2, 2026
c6ccffb
fix(types,docs): DeclaredNode leaves the app document out by its `typ…
claude Oct 2, 2026
cd98e0f
test(components): page-body-single-node-8310 reads the root README's …
claude Oct 2, 2026
9a491a0
test(types): overlay-trigger-union-7081 drops the import its pin no l…
claude Oct 2, 2026
ff7f748
chore(changeset): dated notes on two pending entries that read the pr…
claude Oct 2, 2026
c7c37b7
Merge main (160c6c6e) into claude/issue-11466-node-slot-union-v1 (obj…
claude Oct 3, 2026
b13093d
test(types): MeasuredArmDrift11466 drops the three arms objectui#1151…
claude Oct 3, 2026
af7b616
feat(plugin-dashboard): plugin-dashboard:metric is declared in Custom…
claude Oct 3, 2026
f90ba8d
chore(changeset): plugin-dashboard's metric node and static pivot, an…
claude Oct 3, 2026
20fb227
fix(plugin-dashboard): an object-metric in a widget's component envel…
claude Oct 3, 2026
fab635a
docs(plugin-dashboard): the envelope's object-metric retirement in th…
claude Oct 3, 2026
5afa026
Merge main (154075ab) into claude/issue-11466-node-slot-union-v1 (obj…
claude Oct 3, 2026
cb687df
Merge main (d0c0c7fe9) into claude/issue-11466-node-slot-union-v1 (ob…
claude Oct 3, 2026
3184478
Merge main (0a53c67f9) into claude/issue-11466-node-slot-union-v1 (ob…
claude Oct 3, 2026
cae2224
test(types): MeasuredArmDrift11466's docblock names where its remaini…
claude Oct 3, 2026
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
2 changes: 2 additions & 0 deletions .changeset/10528-grid-object-table-fetch.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ Later in this same release objectui#10859 batch 8 (phase 2b) unregistered the `d
node key. `DashboardGridLayout` is unchanged and stays exported, so this fix still holds wherever
a host mounts it; it is no longer reachable as a schema `type`. The rest of this entry is kept as
the reading of this change.

⚠️ **Dated note, 2026-10-03 — the grid's static pivot node states its keys — objectui#11466.** At this change, "Widgets bound to inline rows are unchanged, including static-data pivots" held, and the grid's static-data pivot node spread the widget's `options` whole. Now that node states the keys `PivotTableSchema` declares and `PivotTable` draws, read from `options`, and no other option key reaches it. `.changeset/11466-dashboard-metric-node.md` states what ships. The rest of this entry is kept as the reading of this change.
2 changes: 2 additions & 0 deletions .changeset/11348-dashboard-widget-reads.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ literal is unchanged.
⚠️ **Dated note, 2026-10-02 — the component arm now declares `title` — objectui#11467.** "`title` and `colorVariant` are still declared on the widget arm only" in the note above held when that note was written. Later in this same release, objectui#11467 declared `metric-card`'s registered inputs on the component arm, `title` among them as the card's heading, typed as `MetricCard` reads it. So `title` is declared on both arms, and it reads as `string | I18nLabel` straight off a `widgets[]` entry. `colorVariant` is still declared on the widget arm only. `.changeset/11467-metric-card-arm-inputs.md` states what ships; the text above is kept as the reading of this change.

⚠️ **Dated note, 2026-10-03 — the read sites move to the slot's element type — objectui#11514.** At this change, the `layout` / `title` / `colorVariant` sites above read a `widgets[]` entry through `DashboardWidgetSchema`, and the component arm was assignable to it. Now `DashboardWidgetSchema['type']` names no component type, so the component arm is not assignable to it, and the sites in `DashboardGridLayout`, `DashboardRenderer`, `DashboardWithConfig` and the designer's `DashboardEditor` read an entry by the slot's element type, `DashboardComponentSchema['widgets'][number]`. `layout` and `title` read with their declared types off either arm; `colorVariant` is still declared on the widget arm only. `.changeset/11514-dashboard-slot-entry-types.md` states what ships. The rest of this entry is kept as the reading of this change.

⚠️ **Dated note, 2026-10-03 — the broadcast narrows over two types — objectui#11466.** At this change, the dashboard-filter broadcast's type guard ran "over the same three `object-*` types". Now `object-metric` has left that set: the broadcast narrows over `object-chart` and `object-data-table`, the two node schemas that declare `filter`, and an `object-metric` node in a widget's legacy `component` envelope draws the retired-format placeholder instead (`.changeset/11466-envelope-object-metric-retired.md`). The rest of this entry is kept as the reading of this change.
2 changes: 2 additions & 0 deletions .changeset/11354-plugin-form-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
No package released. `@object-ui/plugin-form`'s `DrawerForm`, `ModalForm`, `SplitForm`, `TabbedForm` and `WizardForm` each build a child `form` node and hand it to `SchemaRenderer`. Each node is now a `FormSchema` const before it reaches `SchemaRenderer`. Written inline in `SchemaRenderer`'s `schema` slot, which is typed `BaseSchema`, the node was checked against `BaseSchema` rather than against the `FormSchema` the `form` renderer reads (objectui#11354, preparing objectui#8347's removal of `BaseSchema`'s index signature).

No key is added, removed or renamed, and every value is unchanged. No declaration changes: `FormSchema` already declares every key these nodes carry. The rendered output, the props each component accepts, and the published `.d.ts` are unchanged, so there is nothing to release.

⚠️ **Dated note, 2026-10-02 — the prop takes the declared-node union — objectui#11466.** At this change a node written inline in `SchemaRenderer`'s `schema` slot was checked against `BaseSchema`; now, later in this same release, the slot is `DeclaredNode | string | null | undefined`, the union of the declared node types keyed by `type`, so an inline `form` node is checked against `FormSchema`. The rest of this entry is kept as the reading of this change.
2 changes: 2 additions & 0 deletions .changeset/11364-public-block-ts-faces.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ does needs to narrow first. Once objectui#8347 removes the index signature,
a key misspelled inside one of these nodes' bags is refused, and the spec's
spelling compiles. None of these types carries an index signature, and the
prop gains none.

⚠️ **Dated note, 2026-10-02 — the prop takes the declared-node union — objectui#11466.** At this change `SchemaRendererProps.schema` was `BaseSchema | AuthoringNode | string | null | undefined`, and every value the old union accepted was still accepted; now, later in this same release, it is `DeclaredNode | string | null | undefined`. `DeclaredNode` (`@object-ui/types`) is the union, keyed by `type`, of the component schemas `AnySchema` declares (without its `BaseSchema` arm and without the app-level document `AppComponentSchema`), `AuthoringNode`, and the types an application declares in `CustomNodeRegistry`, so a value typed `BaseSchema` or a `type` nothing declares is refused. `PageDocumentNode` admits every page kind but the interface-mode `list`. The rest of this entry is kept as the reading of this change.
9 changes: 9 additions & 0 deletions .changeset/11466-dashboard-metric-node.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@object-ui/plugin-dashboard': minor
---

The dashboards' metric card node, `plugin-dashboard:metric`, is a declared node type, and both dashboard surfaces hand `SchemaRenderer` declared nodes with no cast (objectui#11466).

- **New export `DashboardMetricNodeSchema`:** the node `MetricWidget` renders under `plugin-dashboard:metric`, with the keys it reads off the node, typed by `MetricWidgetProps`. This package enters it in `@object-ui/types`' `CustomNodeRegistry` under that key, so a program that loads this package's typings has the node as a member of `DeclaredNode`: authorable in a node slot and at `SchemaRenderer`'s `schema` prop, with its keys checked.
- **`DashboardGridLayout`'s static `pivot` node** (a `pivot` widget drawn from inline rows) states the keys `PivotTableSchema` declares and `PivotTable` draws, read from `options`: `title`, `rowField`, `columnField`, `valueField`, `aggregation`, `showRowTotals`, `showColumnTotals`, `format`, `columnColors` and `className`. It used to spread `options` whole, so every option key became a key of the node, and `SchemaRenderer` handed each one on as a React prop. Now no other option key reaches the node: not a host prop of `PivotTable`'s (such as `rowLabels`), and not a `BaseSchema` node key other than `className` (such as `id`, `style` or `hidden`). Each key listed above draws as before.
- **The two surfaces' casts before `SchemaRenderer` are gone** (`DashboardGridLayout`'s `as BaseSchema | string | null | undefined` and `DashboardRenderer`'s `as BaseSchema`). Each surface's node builder returns `SchemaRenderer`'s own prop type, so every node is checked against its declared type where it is built. No runtime change.
20 changes: 20 additions & 0 deletions .changeset/11466-envelope-object-metric-retired.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'@object-ui/plugin-dashboard': minor
---

**An `object-metric` node in a dashboard widget's legacy `component` envelope now draws the retired-format prompt instead of its number (objectui#11466).** This follows the maintainer's ruling A on objectui#11466, which extends ruling C on objectui#11525 (a dashboard metric takes its number only through a `dataset`) to this last inline metric form.

**What changes on screen.** The change covers a stored widget written in objectui's legacy envelope format, `{ id, component: { type: 'object-metric', objectName, aggregate, … }, layout }`, which the spec's widget has no member for:

- **Before:** `DashboardRenderer` and `DashboardGridLayout` drew the aggregated number in the tile, and on `DashboardRenderer` the dashboard filter bar's value was merged into the node as a flat `filter`.
- **Now:** both surfaces draw "This widget uses a retired data format. Edit it to bind a dataset." and send no query. The same holds when the node is written under its registration's full name, `plugin-dashboard:object-metric`. The filter bar's broadcast no longer covers `object-metric`.

Breaking for stored dashboards that still carry this form; `minor` because objectui never declares `major`. No producer of the form was measured in objectui or objectstack outside the pin that covered it (the measurement is on objectui#11466); stored customer dashboards are unmeasured, the gap ruling C accepted.

**The fix.** Replace the envelope with a dashboard widget bound to a dataset: set `dataset` and select its `values` by name, as for any metric widget. A dataset-bound metric draws its number.

**What does not move:**

- Every other node in a `component` envelope draws as before, and the filter bar still scopes an envelope's `object-chart` and `object-data-table`.
- An `object-metric` block authored on a page (`{ type: 'object-metric', properties: { … } }`) draws as before.
- The zod and TypeScript authoring faces are unchanged.
44 changes: 44 additions & 0 deletions .changeset/11466-node-slot-union.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
'@object-ui/types': minor
'@object-ui/react': minor
'@object-ui/core': minor
'@object-ui/components': patch
---

A node slot and `SchemaRenderer`'s `schema` prop take the union of the declared node types, `DeclaredNode` (objectui#11466).

**BREAKING (TypeScript authoring face only), shipped as `minor` per this repository's version policy.** `SchemaNode` was `BaseSchema | string | number | boolean | null | undefined`, and the prop was `BaseSchema | AuthoringNode | string | null | undefined`. Both object members are now `DeclaredNode`, a new export of `@object-ui/types`: the discriminated union, keyed by the literal `type`, of

- every component schema `AnySchema` declares, without its `BaseSchema` arm (whose `type` is `string`) and without `AppComponentSchema` (the app-level document, which `AppSchemaRenderer` reads structurally and `ComponentRegistry` never dispatches: as a node, `type: 'app'` is the stored page of that kind);
- every spec-declared `AuthoringNode`;
- every type an application declares in the new `CustomNodeRegistry` interface.

There is no `type: string` arm and no index signature. What moves:

- A node whose `type` no declaration names is refused, nested or at the prop: `{ type: 'card', children: [{ type: 'txt' }] }` no longer compiles. So is a value typed `BaseSchema`, which names no declared type.
- An inline child is checked against its own type's arm wherever it is nested, so a misspelled key on a node type without an index signature (the `AuthoringNode`s, a closed `CustomNodeRegistry` entry) is refused at the slot. Node types that extend `BaseSchema` keep its index signature until objectui#8347 removes it.
- A node's REQUIRED keys are required (a stored `home` page document needs its `label`, a `data-table` its `columns`), because no `BaseSchema` arm accepts the same literal structurally any more.
- `app` and `list` are one arm each: `PageDocumentNode` admits every page kind but the interface-mode `list`, which `PageView` renders through `InterfaceListPage` and never hands to `SchemaRenderer`. Narrowing a `DeclaredNode` on `type === 'app'` gives `PageDocumentNode`, and on `'list'` gives `ListSchema`. `SchemaByType` reads `AnySchema` and does not move.
- `@object-ui/core`'s schema builder takes `DeclaredNode` where it took `BaseSchema`: `.child()` / `.children()` on the grid and flex builders and the card builder's `.content()`.
- `toRenderableSchema` (`@object-ui/react`) follows `SchemaNode` and the prop by reference.

What does NOT move: every zod face (`AnyComponentSchema`, `SchemaNodeSchema`, the strict authoring face) and every runtime path. `zod/base.zod.ts` writes the node slot's zod type out through type aliases so the declared-node union can name the zod-derived `AuthoringNode`s without a circular reference; the schema objects are unchanged. `FlexBlockNode` is now an interface for the same reason, with the same members. `@object-ui/components`' `kind: 'html'` page crosses its runtime-parsed tree into `DeclaredNode` at one documented boundary, after `validateTree` reports no error; it renders exactly what it rendered.

**Migration.** Annotate a node with its declared type, or with `DeclaredNode` where any node goes. Declare each type you register with `ComponentRegistry` in `CustomNodeRegistry`:

```ts
import type { BaseSchema } from '@object-ui/types';

interface MyWidgetSchema extends BaseSchema {
type: 'my-widget';
customProp?: string;
}

declare module '@object-ui/types' {
interface CustomNodeRegistry {
'my-widget': MyWidgetSchema;
}
}
```

An entry joins the union under its KEY (the arm is the entry intersected with `{ type: KEY }`), so it never adds a `type: string` arm. A value built at runtime whose `type` the compiler cannot know is narrowed to a declared type, or crosses at one validated boundary, as the html-tier page does.
2 changes: 2 additions & 0 deletions .changeset/11479-producers-declared-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
`toRenderableSchema` declares its parameter as `SchemaNode` (objectui#11479).

The parameter used to spell out `SchemaNode`'s members one by one. It now names the union itself, so it follows `SchemaNode` instead of keeping a hand-written copy of it. Nothing it accepts changes today: the two spellings are the same type, and the function's return type and behaviour are untouched.

⚠️ **Dated note, 2026-10-02 — the parameter narrows with `SchemaNode` — objectui#11466.** At this change the parameter's two spellings were the same type and nothing it accepted changed; now, later in this same release, `SchemaNode`'s object arm is `DeclaredNode`, the union of the declared node types, so the parameter follows it and no longer accepts a value typed `BaseSchema`. That is the point of naming the union instead of copying it. The rest of this entry is kept as the reading of this change.
4 changes: 4 additions & 0 deletions .changeset/11514-dashboard-slot-entry-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
- **What does not move.** Every widget that names a known family draws as before: the dispatch routes the same families, and each `object-chart` node carries the same `chartType` it carried. A legacy `component` envelope with no `type` is not the spec's widget and is not given its default; it draws under its card heading as before, and a number or `true` in its `component` draws the same text, now forwarded through `toRenderableSchema`.

⚠️ **Dated note, 2026-10-03 — a dataset-less `provider: 'object'` metric is retired — objectui#11525.** At this change, "Every widget that names a known family draws as before" held for the single-value family's inline `provider: 'object'` widget, which drew its number through a flat `object-metric` node. Now a `metric`, `gauge`, `solid-gauge`, `kpi` or `bullet` widget, or a typeless one, with no `dataset` and an `options.data` (or widget-level `data`) of `{ provider: 'object', … }` draws the retired-format placeholder on both surfaces, by the maintainer's ruling C on objectui#11525. A typeless widget still draws exactly as the same widget with `type: 'metric'`, so it draws that placeholder too. `.changeset/11525-inline-metric-retired.md` states what ships. The rest of this entry is kept as the reading of this change.

⚠️ **Dated note, 2026-10-03 — the grid's static pivot node states its keys — objectui#11466.** At this change, "Every widget that names a known family draws as before" held for a `pivot` widget drawn from inline rows on `DashboardGridLayout`, whose node spread the widget's `options` whole. Now that node states the keys `PivotTableSchema` declares and `PivotTable` draws, read from `options`, and no other option key reaches it. `.changeset/11466-dashboard-metric-node.md` states what ships. The rest of this entry is kept as the reading of this change.

⚠️ **Dated note, 2026-10-03 — an envelope's `object-metric` draws the placeholder — objectui#11466.** At this change, a legacy `component` envelope "draws under its card heading as before" whatever node it held. Now an `object-metric` node in it draws the retired-format placeholder under that heading and sends no query, by the maintainer's ruling A on objectui#11466; every other envelope node draws as before. `.changeset/11466-envelope-object-metric-retired.md` states what ships. The rest of this entry is kept as the reading of this change.
2 changes: 2 additions & 0 deletions .changeset/11525-inline-metric-retired.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ measured emitting the form (the measurements are on objectui#11525).

The flat `object-metric` node the two surfaces used to build carried an
ObjectQL-dialect `filter` that no node type declares; it is gone.

⚠️ **Dated note, 2026-10-03 — the envelope's `object-metric` is retired too — objectui#11466.** At this change, "An `object-metric` node an author places in a widget's legacy `component` envelope still receives the dashboard filter bar's values" held, because `object-metric` stayed in the filter broadcast's filterable set for that node. Now, by the maintainer's ruling A on objectui#11466 (extending this change's ruling C), that envelope node draws the same retired-format placeholder on both surfaces and sends no query, and `object-metric` left the filterable set. `.changeset/11466-envelope-object-metric-retired.md` states what ships. The rest of this entry is kept as the reading of this change.
2 changes: 2 additions & 0 deletions .changeset/7767-collapsible-trigger-union.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@
This is a **widening**, not a replacement: every singular `trigger` keeps type-checking unchanged. The Zod mirror is untouched — `zod/disclosure.zod.ts` already spelled this key `z.union([SchemaNodeSchema, z.array(SchemaNodeSchema)])` — and so is the runtime: `renderers/disclosure/collapsible.tsx` hands `schema.trigger` to `renderChildren`, whose `Array.isArray` branch has served the array form all along, and that same registration's `defaultProps.trigger` ships as an array. What changes is that the TypeScript face stops under-reporting an accept set that already ships: copying the renderer's own default into a typed document is no longer a type error against the type that shipped it. The docs page's `trigger` row follows the declaration.

This is the eighth member of the change objectui#7081 made to the overlay family, carried out under the same ruling (2026-09-03 on that card): the validator's accept set does not move, so this is a declaration catching up with what ships rather than a new capability. Per this repository's version-alignment convention, a widening of a published type surface ships as `minor` with the semantics spelled out here rather than as `major` (see AGENTS.md, "版本号策略").

⚠️ **Dated note, 2026-10-02 — `SchemaNode`'s object arm — objectui#11466.** At this change `SchemaNode` was `BaseSchema | string | number | boolean | null | undefined`; now, later in this same release, its object arm is `DeclaredNode`, the union of the declared node types. `string` is still a member, so `string | SchemaNode` still denotes exactly `SchemaNode`. The rest of this entry is kept as the reading of this change.
Loading
Loading