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
7 changes: 7 additions & 0 deletions .changeset/expression-bindable-text-keys.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
---
"@objectstack/spec": minor
---

Declare the closed vocabulary of expression-bindable text keys (objectui#4795 Direction 1, spec half — #9599).

`@objectstack/spec/ui` now exports `EXPRESSION_BINDABLE_TEXT_KEYS` (`title` / `label` / `value` / `description` — a closed enum per the 2026-08-17 maintainer ruling's terms, reopened 2026-08-18), the `ExpressionBindableTextKey` type and `ExpressionBindableTextKeySchema` Zod face, the per-component carriage map `EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT` (`statistic`: `label`/`value`/`description`, `card`: `title`/`description`, `button`: `label` — measured against the objectui renderers' read points at the `.objectui-sha` pin), and the runtime lookup `expressionBindableTextKeysFor(componentType)`. These are consumed by the objectui SchemaRenderer evaluation memo (the downstream half, riding objectui#4795) so the set of top-level text keys the memo evaluates is declared here once, never inferred or hard-coded as a twin list. Purely additive — no existing schema accepts or rejects anything new in this release.
2 changes: 1 addition & 1 deletion content/docs/getting-started/quick-reference.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -46,7 +46,7 @@ Core business logic and data modeling schemas.
| **[Postgres Driver](/docs/references/data/driver-postgres)** | `driver/postgres.zod.ts` | PostgresConfig | PostgreSQL configuration |
| **[Mongo Driver](/docs/references/data/driver-mongo)** | `driver/mongo.zod.ts` | MongoConfig | MongoDB configuration |

## UI Protocol (11 of 16 schemas)
## UI Protocol (11 of 17 schemas)

Presentation layer - views, forms, dashboards, and themes.

Expand Down
9 changes: 5 additions & 4 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1587 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1588 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand DownExpand Up@@ -32,8 +32,8 @@ counts are sums of the rows they head. Regenerate with
| [Shared Protocol](/docs/references/shared) | 8 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 36 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 161 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **198** | **1587** | 14 protocol modules |
| [UI Protocol](/docs/references/ui) | 17 | 162 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **199** | **1588** | 14 protocol modules |

---

Expand DownExpand Up@@ -364,7 +364,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a

## UI Protocol

**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **16 pages, 161 schemas**
**Source:** `packages/spec/src/ui/` · **Import:** `@objectstack/spec/ui` · **17 pages, 162 schemas**

Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer.

Expand All@@ -378,6 +378,7 @@ Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI lay
| [`component.zod.ts`](/docs/references/ui/component) | `AIChatWindowProps`, `ElementButtonProps`, `ElementFilterProps`, `ElementFormProps`, `ElementImageProps`, `ElementMetadataViewerProps`, `ElementNumberProps`, `ElementRecordPickerProps`, `ElementTextInputProps`, `ElementTextProps`, `ObjectCalendarProps`, `ObjectFormProps`, `ObjectGridProps`, `ObjectKanbanProps`, `ObjectMasterDetailFormProps`, `ObjectMetricProps`, `PageAccordionProps`, `PageCardProps`, `PageContainerProps`, `PageHeaderProps`, `PageTabsProps`, `RecordActivityProps`, `RecordAlertAction`, `RecordAlertProps`, `RecordChatterProps`, `RecordDetailsProps`, `RecordHighlightsField`, `RecordHighlightsProps`, `RecordHistoryProps`, `RecordPathProps`, `RecordQuickActionsProps`, `RecordReferenceRailProps`, `RecordRelatedListProps`, `ReferenceRailEntry` |
| [`dashboard.zod.ts`](/docs/references/ui/dashboard) | `Dashboard`, `DashboardHeader`, `DashboardHeaderAction`, `DashboardWidget`, `DashboardWidgetOptions`, `GlobalFilter`, `GlobalFilterOptionsFrom`, `WidgetActionType`, `WidgetColorVariant` |
| [`dataset.zod.ts`](/docs/references/ui/dataset) | `Dataset`, `DatasetDimension`, `DatasetMeasure`, `DerivedMeasureOp` |
| [`expression-bindable-text-keys.zod.ts`](/docs/references/ui/expression-bindable-text-keys) | `ExpressionBindableTextKey` |
| [`i18n.zod.ts`](/docs/references/ui/i18n) | `AriaProps`, `I18nLabel`, `InlineLocaleMap` |
| [`notification.zod.ts`](/docs/references/ui/notification) | `NotificationPosition`, `NotificationSeverity`, `NotificationType` |
| [`page.zod.ts`](/docs/references/ui/page) | `ElementDataSource`, `InterfacePageConfig`, `Page`, `PageComponent`, `PageComponentType`, `PageRegion`, `PageType`, `PageVariable` |
Expand Down
102 changes: 102 additions & 0 deletions content/docs/references/ui/expression-bindable-text-keys.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
---
title: Expression Bindable Text Keys
description: Expression Bindable Text Keys protocol schemas
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

Expression-bindable text keys — the CLOSED vocabulary of top-level text keys
a SchemaRenderer evaluation memo evaluates, and the per-component carriage
map that says which component types carry which of them.

## Provenance (not re-litigable here)

objectui#4795 measured the hole: apart from `content`, no top-level text key
on a rendered SDUI node was BOTH evaluated by the renderer's expression memo
AND read back by the component renderer — `statistic.value: '${data.n}'`
rendered the literal `${data.n}`, and the `props`-envelope workaround
rendered blank. The 2026-08-17 maintainer ruling on that card deferred the
fix (Direction 1) behind a restart condition and pre-defined its terms,
verbatim: *"the key set is declared in `@objectstack/spec`/types as a closed
enum, never inferred"*. On 2026-08-18 the maintainer ruled the restart
condition met by product intent (a metadata-authored live dashboard is a
basic requirement), reopening Direction 1 on those terms. This module is the
spec half (objectstack#9599); the objectui half extends the evaluation memo
to CONSUME these exports (it rides objectui#4795) instead of hard-coding a
twin list.

Related, and deliberately out of this module's reach:

- ⛔ Direction 2 (merging the `props` envelope into the node) is
permanently rejected (same 08-17 ruling) — nothing here re-legalizes the
envelope shape the objectui#4786 teaching rewrite retired.
- `content` is NOT a member. It already has its own evaluation leg in the
memo and its own read-back contract; adding it here would give one key
two declared evaluation paths.
- The `properties` / `props` config bags are evaluated per-value by their
own memo legs (objectui#4799 / #5122) — this vocabulary is only about
keys authored at the NODE'S TOP LEVEL.

## The contract

For a component type with a row in
`EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT`, the renderer's
evaluation memo evaluates exactly the listed keys (when the authored value
is an expression-bearing string); every other key stays inert text. For a
component type with NO row, the answer is the empty set — closed and
mechanically answerable in both directions, never inferred from what a
renderer happens to read.

## Why these rows (measured, not inferred)

Rows are a RECORD of what each component renderer already reads back from
the node's top level, measured at the objectui pin (`.objectui-sha`
`82a9417`, re-verified identical at objectui `origin/main` `6c68b13` on
2026-08-18) — declaring a key a renderer does not read back would recreate
the evaluated-but-blank half of the objectui#4795 table:

| type | renderer read points (top level, within the closed set) |
|-------------|----------------------------------------------------------|
| `statistic` | `schema.label`, `schema.value`, `schema.description` (`data-display/statistic.tsx`) |
| `card` | `schema.title`, `schema.description` (`layout/card.tsx`) |
| `button` | `schema.label` (`form/button.tsx`, `action/action-button.tsx`) |

These are the measured motivating cases from objectui#4795 (dashboard
workhorses). Other registered renderers also read keys from this closed set
at the top level (`alert`/`empty`/`dialog` `title`+`description`, `badge`
`label`, form inputs' `value`/`label`, …) — those rows are deliberately NOT
declared yet: form-control `value` is interactive state rather than display
text, and each row is an accept-surface widening that should arrive with its
own measurement, not ride this one (startup scope discipline). Adding a row
is additive and spec-first; do it here, never as a renderer-side inference.

<Callout type="info">
**Source:** `packages/spec/src/ui/expression-bindable-text-keys.zod.ts`
</Callout>

## TypeScript Usage

```typescript
import { ExpressionBindableTextKeySchema } from '@objectstack/spec/ui';
import type { ExpressionBindableTextKey } from '@objectstack/spec/ui';

// Validate data
const result = ExpressionBindableTextKeySchema.parse(data);
```

---

## ExpressionBindableTextKey

One of the closed set of expression-bindable text keys — the top-level node keys a SchemaRenderer evaluation memo may evaluate (objectui#4795 Direction 1; carriage per component type is EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT).

### Allowed Values

* `title`
* `label`
* `value`
* `description`


---

1 change: 1 addition & 0 deletions content/docs/references/ui/index.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,7 @@ This section contains all protocol schemas for the ui layer of ObjectStack.
<Card href="/docs/references/ui/component" title="Component" description="Source: packages/spec/src/ui/component.zod.ts" />
<Card href="/docs/references/ui/dashboard" title="Dashboard" description="Source: packages/spec/src/ui/dashboard.zod.ts" />
<Card href="/docs/references/ui/dataset" title="Dataset" description="Source: packages/spec/src/ui/dataset.zod.ts" />
<Card href="/docs/references/ui/expression-bindable-text-keys" title="Expression Bindable Text Keys" description="Source: packages/spec/src/ui/expression-bindable-text-keys.zod.ts" />
<Card href="/docs/references/ui/i18n" title="I18n" description="Source: packages/spec/src/ui/i18n.zod.ts" />
<Card href="/docs/references/ui/notification" title="Notification" description="Source: packages/spec/src/ui/notification.zod.ts" />
<Card href="/docs/references/ui/page" title="Page" description="Source: packages/spec/src/ui/page.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/ui/meta.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,6 +12,7 @@
"component",
"dashboard",
"dataset",
"expression-bindable-text-keys",
"report",
"---Interaction & Layout---",
"responsive",
Expand Down
6 changes: 6 additions & 0 deletions packages/spec/api-surface/ui.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -135,6 +135,8 @@
"DateRangePreset (type)",
"DerivedMeasureOp (const)",
"DerivedMeasureOpValue (type)",
"EXPRESSION_BINDABLE_TEXT_KEYS (const)",
"EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT (const)",
"ElementButtonPropsSchema (const)",
"ElementDataSource (type)",
"ElementDataSourceSchema (const)",
Expand All@@ -150,6 +152,8 @@
"ElementTextPropsSchema (const)",
"ExpandViewResult (interface)",
"ExpandedViewItem (interface)",
"ExpressionBindableTextKey (type)",
"ExpressionBindableTextKeySchema (const)",
"FieldWidgetProps (type)",
"FieldWidgetPropsParsed (type)",
"FieldWidgetPropsSchema (const)",
Expand DownExpand Up@@ -422,8 +426,10 @@
"diagnoseViewMetadata (function)",
"expandViewContainer (function)",
"expandViewContainerWithDiagnostics (function)",
"expressionBindableTextKeysFor (function)",
"isActionParamValuePresent (function)",
"isAggregatedViewContainer (function)",
"isExpressionBindableTextKey (function)",
"isRecordContextBlockType (function)",
"isViewContainerShaped (function)",
"normalizeFilterOperator (function)",
Expand Down
6 changes: 6 additions & 0 deletions packages/spec/export-origins/ui.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -135,6 +135,8 @@
"DateRangePreset": "src/data/date-range-presets.ts#DateRangePreset (type)",
"DerivedMeasureOp": "src/ui/dataset.zod.ts#DerivedMeasureOp (const)",
"DerivedMeasureOpValue": "src/ui/dataset.zod.ts#DerivedMeasureOpValue (type)",
"EXPRESSION_BINDABLE_TEXT_KEYS": "src/ui/expression-bindable-text-keys.zod.ts#EXPRESSION_BINDABLE_TEXT_KEYS (const)",
"EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT": "src/ui/expression-bindable-text-keys.zod.ts#EXPRESSION_BINDABLE_TEXT_KEYS_BY_COMPONENT (const)",
"ElementButtonPropsSchema": "src/ui/component.zod.ts#ElementButtonPropsSchema (const)",
"ElementDataSource": "src/ui/page.zod.ts#ElementDataSource (type)",
"ElementDataSourceSchema": "src/ui/page.zod.ts#ElementDataSourceSchema (const)",
Expand All@@ -150,6 +152,8 @@
"ElementTextPropsSchema": "src/ui/component.zod.ts#ElementTextPropsSchema (const)",
"ExpandViewResult": "src/ui/view.zod.ts#ExpandViewResult (interface)",
"ExpandedViewItem": "src/ui/view.zod.ts#ExpandedViewItem (interface)",
"ExpressionBindableTextKey": "src/ui/expression-bindable-text-keys.zod.ts#ExpressionBindableTextKey (type)",
"ExpressionBindableTextKeySchema": "src/ui/expression-bindable-text-keys.zod.ts#ExpressionBindableTextKeySchema (const)",
"FieldWidgetProps": "src/ui/widget.zod.ts#FieldWidgetProps (type)",
"FieldWidgetPropsParsed": "src/ui/widget.zod.ts#FieldWidgetPropsParsed (type)",
"FieldWidgetPropsSchema": "src/ui/widget.zod.ts#FieldWidgetPropsSchema (const)",
Expand DownExpand Up@@ -422,8 +426,10 @@
"diagnoseViewMetadata": "src/ui/view.zod.ts#diagnoseViewMetadata (function)",
"expandViewContainer": "src/ui/view.zod.ts#expandViewContainer (function)",
"expandViewContainerWithDiagnostics": "src/ui/view.zod.ts#expandViewContainerWithDiagnostics (function)",
"expressionBindableTextKeysFor": "src/ui/expression-bindable-text-keys.zod.ts#expressionBindableTextKeysFor (function)",
"isActionParamValuePresent": "src/ui/action-params.zod.ts#isActionParamValuePresent (function)",
"isAggregatedViewContainer": "src/ui/view.zod.ts#isAggregatedViewContainer (function)",
"isExpressionBindableTextKey": "src/ui/expression-bindable-text-keys.zod.ts#isExpressionBindableTextKey (function)",
"isRecordContextBlockType": "src/ui/react-blocks.ts#isRecordContextBlockType (function)",
"isViewContainerShaped": "src/ui/assembled-views.zod.ts#isViewContainerShaped (function)",
"normalizeFilterOperator": "src/ui/view.zod.ts#normalizeFilterOperator (function)",
Expand Down
1 change: 1 addition & 0 deletions packages/spec/json-schema.manifest/ui.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,7 @@
"ui/ElementRecordPickerProps",
"ui/ElementTextInputProps",
"ui/ElementTextProps",
"ui/ExpressionBindableTextKey",
"ui/FormButtonConfig",
"ui/FormField",
"ui/FormFieldPublicPicker",
Expand Down
7 changes: 6 additions & 1 deletion packages/spec/scripts/build-docs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -544,7 +544,12 @@ const SECTION_GROUPS: Record<string, Array<{ section: string; pages: string[] }>
// #5697), and a reader who found the action declaration should find what a
// body receives in the same section rather than under "More".
{ section: 'Apps & Navigation', pages: ['app', 'page', 'view', 'action', 'action-params'] },
{ section: 'Visualization', pages: ['chart', 'dashboard', 'dataset', 'report', 'widget', 'component'] },
// `expression-bindable-text-keys` sits beside `component` deliberately: it
// is the SDUI rendering contract's evaluation vocabulary (objectui#4795
// Direction 1 — which top-level text keys the renderer's expression memo
// evaluates, per component type), and a reader who found the component
// prop schemas should find it in the same section rather than under "More".
{ section: 'Visualization', pages: ['chart', 'dashboard', 'dataset', 'report', 'widget', 'component', 'expression-bindable-text-keys'] },
// `animation` / `dnd` / `keyboard` / `touch` / `offline` left this section at
// #4988: the five `ui/` interaction config modules were retired whole
// (ADR-0049 — no carrier key, nothing parsed them), and their generated
Expand Down
Loading
Loading