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
11 changes: 11 additions & 0 deletions .changeset/10685-drilldown-per-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,14 @@ node. Nothing ever drilled a `pivot` node; a pivot drill is authored on `object-
entry says. `object-pivot` still has no zod mirror, so what this entry says of a stored
`object-pivot` config stands. `.changeset/10932-pivot-drilldown-retired.md` (PR objectui#10972)
states what ships; the text above is kept as the reading of this change.

⚠️ **Dated note, 2026-10-02 — `object-pivot` has a zod arm now — objectui#11440.**
Later in this same release `object-pivot` gained an arm in `@object-ui/types/zod`,
`ObjectPivotBlockSchema`, whose `properties.drillDown` is this entry's per-block shape: the shared
drill config with `mode` refused by name (`invalid_type` at `properties.drillDown.mode`). So "A
stored JSON pivot config carrying `mode` is still accepted" and "neither `object-pivot` nor
`PivotTableSchema` has a zod mirror, so no validator in this repository reads the members of a
pivot's `drillDown`" above, and "`object-pivot` still has no zod mirror" in the 2026-09-29 note, no
longer hold: `safeValidateSchema` and the strict face refuse that `mode`.
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
reading of this change.
20 changes: 20 additions & 0 deletions .changeset/11440-arm-passing-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'@object-ui/types': minor
---

`safeValidateSchema` — and so `objectui validate` — accepts seven more registered node types: the spec page kinds `record`, `home` and `utility`, and `app-schema-renderer`, `object-pivot`, `embeddable-form` and `detail-section` (objectui#11440, the objectui#10859 follow-up for the keys that pass the criterion).

**Clause-②: yes** — the accept set of `AnyComponentSchema` widens by seven `type` literals, and `@object-ui/types/zod` exports five new schemas. Nothing that parsed before is refused now: every document naming one of the seven was refused with `invalid_union` at `type`, at the root and at every child slot.

**What changed, in observable terms.** Each new authorable spelling, and what it accepts:

- **`{ type: 'record' | 'home' | 'utility', … }`** — a stored `@objectstack/spec` page whose `type` is its page kind, the spelling the spec's `PageSchema` declares and the node `PageView` renders. `PageKindNodeSchema` takes every member of the `page` node (`PageNodeSchema`) by reference, so the spec's own page document validates (`{ type: 'home', name, label, regions }`), each region component is judged by the node union, and the `page` node's refusals (`actions`, `breadcrumbs`, `maxWidth`, `padding`, `body`) apply. The spec's other two kinds are not armed here: `app` stays the app document's literal (`AppComponentSchema`) and `list` the `list` component's. `{ type: 'page', pageType }` validates as before; which spelling is canonical is not changed.
- **`{ type: 'app-schema-renderer', mobileNavMode?, basePath? }`** — `AppSchemaRendererNodeSchema`, the whole-shell node `@object-ui/layout` registers. `mobileNavMode` is `'drawer'` or `'bottom_nav'`, as the registration declares it, so the node the mobile guide teaches validates and a misspelt mode is refused at `mobileNavMode`. The registration's third input, `schema`, is not declared: `SchemaRenderer` strips that key, so a node never delivers it, and the strict face refuses it as an unrecognized key. `children` and `body` are refused by name: a node draws neither.
- **`{ type: 'object-pivot', properties: { … } }`** — `ObjectPivotBlockSchema`. The `properties` bag holds the registration's inputs (`objectName`, `title`, `rowField`, `columnField`, `valueField`, `aggregation`, `showRowTotals`, `showColumnTotals`, `filter`, `format`, `drillDown`) and is required: `rowField`, `columnField` and `valueField` are required members. `objectName` is required unless the node's `dataSource.object` names the object (`RECORD_SOURCE_REQUIRED`). `drillDown` refuses `mode` by name, and the retired `dataProvider` is refused by name.
- **`{ type: 'embeddable-form', properties: { … } }`** — `EmbeddableFormBlockSchema`. The required bag holds exactly the registration's inputs (`formId`, required; `objectName`, required unless `dataSource.object` names the object; `title`, `description`, `fields` as field names, `allowMultiple`).
- On both blocks, a prop written flat on the node instead of in `properties` is refused by name, with a message naming `properties.KEY`, as `@objectstack/spec`'s page component refuses it as mis-layered; the node declares the `dataSource` binding (the spec's `ElementDataSourceSchema`) and `responsiveStyles`; `children` and `body` are refused by name.
- **`{ type: 'detail-section', fields, … }`** — `DetailSectionNodeSchema`, one field section of `@object-ui/plugin-detail` as a node: the ten `DetailViewSectionSchema` members its registration publishes (`title`, `description`, `icon`, `fields`, `collapsible`, `defaultCollapsed`, `columns`, `showBorder`, `headerColor`, `hideEmpty`), flat on the node, `fields` required. The plugin-detail README's `detail-view` tab with a `detail-section` content validates. `children` and `body` are refused by name.

The strict authoring face (`StrictAnyComponentSchema`) derives from these arms and accepts the same documents, closed to undeclared keys.

**The TypeScript face.** `@object-ui/types` exports `ObjectPivotBlockNode` and `EmbeddableFormBlockNode`, the two arms' authoring inputs, derived by reference as the other `ObjectQLPublicBlockNode` members are, so they are in `AuthoringNode` and `SchemaRenderer`'s `schema` prop accepts them. Each bag is required and closed: a misspelled bag key, a missing bag and a prop written flat do not compile. The page kinds were already typed (`PageDocumentNode`, the spec's own page input).
7 changes: 7 additions & 0 deletions .changeset/11440-object-pivot-drilldown-input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@object-ui/plugin-dashboard': minor
---

The `object-pivot` registration publishes `drillDown` as an input (objectui#11440).

`ObjectPivotTable` has always read `drillDown` (whether the drill is on, its filter, title, target, columns, row cap and report), and the `pivot` node's refusal of `drillDown` names `object-pivot` as where a pivot drill is authored. The registration did not list it, so the `sdui-parser` manifest and the designer did not offer it. Its description says `drillDown.mode` does not apply: every click point on a pivot is an aggregated bucket, so the pivot always drills through. `@object-ui/types/zod`'s new `object-pivot` arm declares the same member (`.changeset/11440-arm-passing-types.md`). Nothing else about the block changes.
9 changes: 9 additions & 0 deletions .changeset/11468-bag-carrier-node-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,12 @@ accepted before is still accepted. Reading is a different matter: on a union, a
key that one closed member does not declare is TS2339, so code that reads a key
off `SchemaRendererProps['schema']` or off `toRenderableSchema`'s return needs
to narrow first.

⚠️ **Dated note, 2026-10-02 — two more members — objectui#11440.**
Later in this same release `ObjectQLPublicBlockComponentSchema` gained the `object-pivot` and
`embeddable-form` arms, so `ObjectQLPublicBlockNode` derives ten members, and "The eight members
are also exported by name" above reads ten: `ObjectPivotBlockNode` and `EmbeddableFormBlockNode`
join the list. Their bags have no row either (each is built from the block's registration
inputs) and are REQUIRED, and the derivation closes a required bag as it closes an optional one.
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
reading of this change.
10 changes: 10 additions & 0 deletions .changeset/7352-drill-down-config-mirror.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,13 @@ change's reading, not the release's, and that mirror's `drillDown` member refuse
the key rather than mirroring it. `.changeset/10932-pivot-drilldown-retired.md`
(PR objectui#10972) states what ships; the text above is kept as the reading of
this change.

⚠️ **Dated note, 2026-10-02 — `object-pivot`'s arm references this mirror — objectui#11440.**
Later in this same release `object-pivot` gained an arm in `@object-ui/types/zod`
(`ObjectPivotBlockSchema`), whose `properties.drillDown` extends this entry's
`DrillDownConfigSchema` per block, with `mode` refused by name (objectui#10685). So "for the
release the referencing declarations are the opening paragraph's two" in the 2026-09-29 note
above no longer holds: they are three, `ChartSchema`, `ObjectDataTableSchema` and the
`object-pivot` arm, the last two each extending the mirror per block.
`.changeset/11440-arm-passing-types.md` states what ships. The rest of this entry is kept as the
reading of this change.
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,19 @@ import { validate } from '../commands/validate.js';
* more, see `UNREGISTERED_10859_BATCH_8_PHASE_2C`; objectui#11441, run as one
* more batch of this card: minus `navigation-renderer` and `responsive-grid`,
* see `UNREGISTERED_11441`; batch 8 phase 2d: minus the ten `sidebar-*`
* primitives, see `UNREGISTERED_10859_BATCH_8_PHASE_2D`). LOWER it when a batch
* arms or retires more keys; never raise it.
* primitives, see `UNREGISTERED_10859_BATCH_8_PHASE_2D`; objectui#11440: minus
* the seven keys its first pull request armed, see `ARMED_11440`). LOWER it
* when a batch arms or retires more keys; never raise it.
*
* The 8 that remain are named in `STILL_REFUSED_10859`, and the row below
* The 1 that remains is named in `STILL_REFUSED_10859`, and the row below
* pins the refused set to exactly that list, so this comment cannot drift from
* the measurement. They are the eight the seat ruling left registered —
* `object-pivot`, `embeddable-form`, `detail-section`, `home`, `record`,
* `utility`, `spec-report` and `app-schema-renderer` (objectui#11440, which
* arms them).
* the measurement. It is `spec-report`, the eighth key the seat ruling left
* registered: an alias of `report`, retired there in the ruling's step order
* (`report` declares the wrapper shape `spec-report` carries, the dashboard's
* drill drawer moves to `report`, then the alias goes), in objectui#11440's
* second pull request. The other seven — `object-pivot`, `embeddable-form`,
* `detail-section`, `home`, `record`, `utility` and `app-schema-renderer` —
* are armed.
*
* `navigation-renderer` and `responsive-grid`, the two the seat sent to the
* maintainer, were ruled B / B on objectui#11441 (record `5950208338`) and are
Expand All @@ -110,7 +114,7 @@ import { validate } from '../commands/validate.js';
* last 2 waiting on objectui#11441. That card ran first, so the count is 8
* after phase 2d, and objectui#11440 then takes it to 0.
*/
const REFUSED_AT_TYPE = 8;
const REFUSED_AT_TYPE = 1;

/**
* The head's refused count over the NAMESPACED keys (objectui#10872 batch 1:
Expand All @@ -132,7 +136,8 @@ const REFUSED_AT_TYPE = 8;
* twins of the two keys it unregistered; objectui#10859 batch 8 phase 2d: minus
* the ten `ui:sidebar-*` twins of the ten `sidebar-*` primitives it
* unregistered). LOWER it when a batch arms or retires more keys; never raise
* it.
* it. objectui#11440 armed seven bare keys and none of their namespaced twins
* (`ARMED_11440` maps each to its twins), so it does not move this pin.
*/
const NAMESPACED_REFUSED_AT_TYPE = 362;

Expand Down Expand Up @@ -250,14 +255,28 @@ const UNREGISTERED_10859_BATCH_8_PHASE_2D = {
} as const;

/**
* Every bare key still refused at `type` after objectui#11441 and objectui#10859
* batch 8 phase 2d — the `REFUSED_AT_TYPE` docblock says why each is still
* here. Alphabetical.
* The seven bare keys objectui#11440's first pull request ARMED (the seat
* ruling `5945530142`, as amended by `5945583855`), each mapped to its
* namespaced twins. A twin is a separate registry key with its own literal, so
* arming the bare key does not arm it: each twin is still refused at `type`,
* and `NAMESPACED_REFUSED_AT_TYPE` does not move. The three page kinds are
* registered in the `ui` namespace by `@object-ui/components`.
*/
const STILL_REFUSED_10859 = [
'app-schema-renderer', 'detail-section', 'embeddable-form', 'home',
'object-pivot', 'record', 'spec-report', 'utility',
] as const;
const ARMED_11440 = {
home: ['ui:home'],
record: ['ui:record'],
utility: ['ui:utility'],
'app-schema-renderer': ['layout:app-schema-renderer'],
'object-pivot': ['plugin-dashboard:object-pivot'],
'embeddable-form': ['plugin-form:embeddable-form'],
'detail-section': ['plugin-detail:detail-section'],
} as const;

/**
* Every bare key still refused at `type` after objectui#11440's first pull
* request — the `REFUSED_AT_TYPE` docblock says why it is still here.
*/
const STILL_REFUSED_10859 = ['spec-report'] as const;

/** Is `type` unclaimed by every arm of the validator's root union? */
function refusedAtType(type: string): boolean {
Expand Down Expand Up @@ -397,8 +416,8 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
}
// Lit controls: `grid`, the spelling the `responsive-grid` retirement
// points authors to, is registered and claimed; `app-schema-renderer`, the
// whole-shell door navigation goes through, stays registered (and refused,
// until objectui#11440 arms it).
// whole-shell door navigation goes through, stays registered (armed since
// objectui#11440).
expect(BARE_KEYS).toContain('grid');
expect(refusedAtType('grid')).toBe(false);
expect(BARE_KEYS).toContain('app-schema-renderer');
Expand All @@ -422,6 +441,24 @@ describe('registered component types refused at `type` — a ratchet (objectui#1
expect(NAMESPACED_KEYS).toContain('ui:sidebar');
});

it('counts the seven keys objectui#11440 armed, and none of their namespaced twins (objectui#10859)', () => {
expect(Object.keys(ARMED_11440)).toHaveLength(7);
for (const [key, twins] of Object.entries(ARMED_11440)) {
// Armed: still registered, and claimed by an arm.
expect(BARE_KEYS, key).toContain(key);
expect(refusedAtType(key), key).toBe(false);
// The twin is its own literal, so the bare arm does not claim it.
for (const twin of twins) {
expect(NAMESPACED_KEYS, twin).toContain(twin);
expect(refusedAtType(twin), twin).toBe(true);
}
}
// Lit control: the eighth key, `spec-report`, is still registered and
// still refused, until objectui#11440's second pull request retires it.
expect(BARE_KEYS).toContain('spec-report');
expect(refusedAtType('spec-report')).toBe(true);
});

it('the refused set is exactly the named remainder — the pin comment cannot drift (objectui#10859 batch 8)', () => {
const refused = BARE_KEYS.filter(refusedAtType).sort();
expect(refused).toEqual([...STILL_REFUSED_10859]);
Expand Down
Loading
Loading