@object-ui/plugin-charts 17.6.0 → 17.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,2845 @@
1
1
  # @object-ui/plugin-charts
2
2
 
3
+ ## 17.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ad1785c: refactor(plugin-charts)!: retire the `pie-chart`, `donut-chart` and `radar-chart` node type keys; the families are `chart` + `chartType` (objectui#10859, batch 8 phase 2c)
8
+
9
+ **BREAKING (authoring):** the plugin no longer registers `pie-chart`, `donut-chart` or `radar-chart` (and with them `plugin-charts:pie-chart`, `plugin-charts:donut-chart` and `plugin-charts:radar-chart`), and their `CHART_TYPE_KEYWORD_FAMILIES` rows are gone. `objectui validate` already refused each at `type`. Their one producer, this package's `examples/chart-examples.ts`, now authors `{ type: 'chart', chartType: … }`, which `objectui validate` accepts on both faces. A node still authored with one of the three types now renders the "Unknown component type" panel.
10
+
11
+ Migration:
12
+
13
+ - `{ "type": "pie-chart", … }` → `{ "type": "chart", "chartType": "pie", … }`, with the same keys;
14
+ - `{ "type": "donut-chart", … }` → `{ "type": "chart", "chartType": "donut", … }`, with the same keys;
15
+ - `{ "type": "radar-chart", … }` → `{ "type": "chart", "chartType": "radar", … }`, with the same keys.
16
+
17
+ A legacy report section whose `chart.type` says one of the three now reaches the generic `chart` renderer with no family derived from that type; give it the matching `chartType`. `chart:bar` is the one family keyword this package still registers.
18
+
19
+ **Clause-②: yes** — registrations leave the runtime (narrowing), released as `minor` with this banner.
20
+ - 37140f4: refactor(plugin-charts)!: retire the `scatter-chart` node type key; scatter is `chart` + `chartType: 'scatter'` (objectui#10859, batch 8 phase 2b)
21
+
22
+ **BREAKING (authoring):** the plugin no longer registers `scatter-chart` (and with it `plugin-charts:scatter-chart`), a second key on the generic `ChartRenderer` whose family came from its `CHART_TYPE_KEYWORD_FAMILIES` row. That row is gone too. `objectui validate` refused a `scatter-chart` node at `type`, and nothing in this repository, its examples or objectstack authored it. A node authored `type: "scatter-chart"` now renders the "Unknown component type" panel.
23
+
24
+ Migration:
25
+
26
+ - `{ "type": "scatter-chart", … }` → `{ "type": "chart", "chartType": "scatter", … }`, with the same keys.
27
+
28
+ A legacy report section whose `chart.type` says `scatter-chart` now reaches the generic `chart` renderer with no family derived from that type; give it `chartType: "scatter"`.
29
+
30
+ `pie-chart`, `donut-chart` and `radar-chart` were ruled the same way but stay registered: this package's `examples/chart-examples.ts` still authors them, so they wait on their own decision.
31
+
32
+ **Clause-②: yes** — a registration leaves the runtime (narrowing), released as `minor` with this banner.
33
+
34
+ ⚠️ **Dated note, 2026-10-02 — the three chart aliases are retired too — objectui#10859.**
35
+ The decision this entry said `pie-chart`, `donut-chart` and `radar-chart` wait on is made (the seat's fork ruling on objectui#10859): later in this same release, phase 2c moved `examples/chart-examples.ts` to `{ type: 'chart', chartType: 'pie' | 'donut' | 'radar' }` and then unregistered all three keys, with their `plugin-charts:` twins and `CHART_TYPE_KEYWORD_FAMILIES` rows. The rest of this entry is kept as the reading of this change.
36
+ - 19f484f: `FormulaFieldMetadata` declares `@objectstack/spec`'s `expression` in place of `formula`, and three readers of a lookup's display pointer read the spec's `displayField` alone (objectui#11070, round 6). Both retired spellings go at once, with no alias.
37
+
38
+ - **Types.** `FormulaFieldMetadata.formula` is removed. `FormulaFieldMetadata.expression` is `FieldSchema`'s `expression` by reference: a CEL source string, or the spec's `{ dialect, source, … }` envelope. Nothing in ObjectUI read the removed member through the type. `FieldSchema` refuses `formula` by name on every field type, with a rename hint to `expression`.
39
+ - **Form payloads.** `sanitizeFormData` (`@object-ui/plugin-form`) no longer treats a `formula` key as a "computed" flag. Every `type: 'formula'` field is still dropped from the payload by its type, as before. Only a field of some other type that carries `formula` changes: its value is now sent like any writable field's. The spec refuses such a definition at publish, so a served one cannot carry it.
40
+ - **Display pointer.** `deriveColumns` and `hydrateColumns` (`@object-ui/plugin-form`, the master-detail grid columns), `ObjectChart`'s group-by labels (`@object-ui/plugin-charts`) and the action-param resolver (`@object-ui/app-shell`) read `displayField`, then `reference_field`. None of them reads `display_field` any more.
41
+ - **A fix for spec-spelled lookups in master-detail grids.** `deriveColumns` and `hydrateColumns` read `display_field || reference_field` before, with no `displayField` leg. A lookup that declared only `displayField`, which is the spec's spelling, got no display pointer on its grid column. It now gets one.
42
+
43
+ A definition served through `ObjectStackAdapter.getObjectSchema` or `MetadataProvider` loses nothing: the ingestion pass (objectui#7650) stamps a stored `display_field` onto `displayField` before any of these readers sees it. Measured with a lookup carrying `display_field: 'title'` served through `ObjectStackAdapter.getObjectSchema`: the master-detail column (`deriveColumns` and `hydrateColumns`), the chart's axis label and the action param all resolve the `title` column before and after this change.
44
+
45
+ ## ⚠️ BREAKING, priced as minor under the fixed group's version policy
46
+
47
+ TypeScript that writes `formula` on a `FormulaFieldMetadata` no longer compiles (an excess-property error naming the key). Rename it to `expression` and write the formula in CEL against the record, for example `record.quantity * record.unit_price`.
48
+
49
+ At runtime, a lookup whose display pointer is spelled only `display_field` loses it wherever the ingestion pass does not run first. Measured before and after this change, on a lookup with `display_field: 'title'` handed to the readers directly:
50
+
51
+ - **`deriveColumns` / `hydrateColumns` with a `childSchema` that did not come through the ingestion pass** (an external caller, or a master-detail form whose `DataSource` is not `ObjectStackAdapter`): the column's `displayField` was `title`. It is now absent.
52
+ - **`ObjectChart` on a `DataSource` other than `ObjectStackAdapter`**, grouped by that lookup: the axis label came from the `title` column. It now comes from the `name` column, the generic fallback.
53
+ - **The action-param resolver, when a host passes its own unfolded `objects`** (for example through `RecordDetailView`'s `objects` prop): the lookup param's `displayField` was `title`. It is now absent.
54
+
55
+ The same lookups spelled `displayField` resolve the `title` column in all three after this change. Before it, the chart and the action param already did, and the master-detail columns did not (the fix above).
56
+
57
+ **Fix:** spell the pointer `displayField`, or serve the definition through `ObjectStackAdapter`.
58
+ - 06634af: fix(plugin-charts): an `object-chart` whose `specType` is a single-value or tabular spec family draws its routed form, not a silent bar chart; the renderer has no default family (objectui#11520)
59
+
60
+ **What an accepted document draws changes.** `specType` is the react tier's chart family: the react-page wrapper parks `<ObjectChart type="gauge">` there, because `type` is the node's discriminator. Both faces declare it as the spec's whole `ChartTypeSchema`, so an `object-chart` with `specType` set to `gauge`, `solid-gauge`, `metric`, `kpi`, `bullet`, `table` or `pivot` parsed on both faces, and drew a **bar chart**, with no note. The same families on `chartType` already drew the forms below.
61
+
62
+ - **`gauge`, `solid-gauge`, `metric`, `kpi`, `bullet` on `specType`** now draw the number card: one row's number, the form the same family draws on `chartType`.
63
+ - **`table`, `pivot` on `specType`** now draw the tabular notice, which names the data-table and pivot components.
64
+ - **An off-spec `specType`** (both faces refuse it) draws the unknown-type notice, which names the value.
65
+ - **A chart that names no family at all** (both faces refuse it: `object-chart` needs `chartType` or `specType`, and `chart` needs `chartType`) draws a notice reading "This chart names no chart type — nothing was drawn." It used to draw a bar chart. A caller that means a bar names `bar`.
66
+
67
+ Every family this block draws as a chart draws exactly what it drew before, on both keys.
68
+
69
+ How: `normalizeChartSchema` used to keep only the families this package draws as a chart, and `AdvancedChartImpl` drew a missing family as `'bar'`. The normalizer now hands the named family on as named, so `specType` reaches the same family dispatch `chartType` reaches, and the `'bar'` default is removed (with its four copies in the refusal guards).
70
+
71
+ ⚠️ **Type.** `NormalizedChartSchema['chartType']`, exported from this package, widens from the thirteen drawn families to the declared chart families (`DeclaredChartFamily` in the emitted declarations; not a new entry export): `object-chart`'s `chartType` union together with its `specType` (the spec's `ChartType`), both read off `ObjectChartSchema` in `@object-ui/types` by reference. It is now the family the schema names, drawn or not, and a misspelled family still fails to compile. Shipped as `minor`, not `major` (objectui never declares `major`); a consumer that reads it exhaustively needs a branch for each single-value and tabular family, which this package draws no chart of.
72
+ - 045d20b: Relationship-target readers resolve a lookup's target from `reference` alone,
73
+ dropping the `reference_to` fallback arm (objectui#6837, half 2).
74
+
75
+ Maintainer ruling, 2026-08-31, 原文照录: 「objectui不是前端的项目吗?后端的元数据只要
76
+ 对,前端按协议执行就行了呀」. Protocol normalization belongs on the SERVER; the front
77
+ end just executes the protocol. objectstack#13847 landed the server half — a
78
+ `field-reference-to-alias` conversion rewrites stored `reference_to` to
79
+ `reference` on the serve path and in `os migrate meta`.
80
+
81
+ `reference` is the only target spelling `@objectstack/spec`'s `FieldSchema`
82
+ declares. Measured on the installed 17.2.0: it refuses `reference_to`,
83
+ `referenceTo` and `target` with `unrecognized_keys`, each carrying its own
84
+ "Did you mean -> `reference`?" rename, while a nonsense key gets the same
85
+ refusal with NO rename hint and `reference` parses clean.
86
+
87
+ ## ⚠️ BREAKING for a hand-written schema that spells `reference_to` — read this
88
+
89
+ **This is a behaviour change for BYO consumers, and it is being stated rather
90
+ than shipped silently.** ObjectUI is usable without an ObjectStack backend
91
+ (`examples/byo-backend-console`), and a hand-written TypeScript schema passes
92
+ through no zod door, so nothing rejects the legacy spelling at authoring time.
93
+
94
+ **The break surface is narrower than "all BYO consumers", and this is the
95
+ measurement rather than a blanket claim.** Two ingestion choke points stamp both
96
+ snake_case keys from whichever spelling arrived — `MetadataProvider`'s type
97
+ cache for metadata type `object`, and `ObjectStackAdapter.getObjectSchema`. Any
98
+ def that passed either one already carries `reference` and is **completely
99
+ unaffected**. What is affected is exactly:
100
+
101
+ - **A `DataSource` implementation other than `ObjectStackAdapter`.**
102
+ `getObjectSchema` is a required member of the published `DataSource`
103
+ interface, and the readers call it on the generic `dataSource` (through
104
+ `useSettledSchema` and directly), so a host adapter's object schema reaches
105
+ them raw. Every in-repo example of one is on this path:
106
+ `ApiDataSource`, `ValueDataSource`, `packages/types/examples/rest-data-source.ts`,
107
+ `examples/byo-backend-console/src/mockDataSource.ts`,
108
+ `packages/runner/src/lib/mockDataSource.ts`,
109
+ `apps/site/app/components/galleryDataSource.ts`,
110
+ `apps/console/src/sdui-workbench-preview.tsx`,
111
+ `packages/plugin-grid/demo/bulk-actions.tsx`.
112
+
113
+ **Measured on this tree, none of those eight emits a relationship target at all** —
114
+ `reference_to` and `reference` are both zero in each, and
115
+ `examples/byo-backend-console` carries no lookup or master_detail field
116
+ anywhere (its only `reference` hits are a vite triple-slash directive and a
117
+ tsconfig `references` array). The single in-repo producer that WAS on this
118
+ surface, `packages/plugin-gantt/demo/main.tsx`, is fixed here at the producer.
119
+
120
+ ⇒ **If you author object metadata by hand and spell a lookup's target
121
+ `reference_to`, rename that key to `reference`.** Symptom if you do not: the
122
+ target silently fails to resolve, and the affected surface degrades rather than
123
+ erroring — a related list is not derived, a gantt quick filter falls back to the
124
+ distinct values in the loaded rows instead of the referenced object's full
125
+ domain, a tree stops auto-detecting its parent pointer, a lookup cell shows a
126
+ raw id, a chart's group-by labels stay unresolved.
127
+
128
+ The ingestion choke point now emits a **dev-mode warning** when a def arrives
129
+ carrying only `reference_to` or `referenceTo` and no `reference`. It names the
130
+ object, the field and the offending key, and points at this ruling. Stamping is
131
+ deliberately unchanged, so nothing that worked stops working. It is memoised
132
+ once per **(object name, field name, spelling, target value)** — every segment
133
+ of that key is pinned, in both directions, in
134
+ `reference-keys.legacyWarning-6837.test.ts`.
135
+
136
+ ⛔ **This warning does NOT cover the break described above, and it is worth being
137
+ exact about that rather than letting it read as mitigation.** It lives in
138
+ `normalizeFieldReferenceKeys`, reachable only through
139
+ `normalizeSchemaReferenceKeys`, which has exactly two production call sites —
140
+ `MetadataProvider` (metadata type `object`) and
141
+ `ObjectStackAdapter.getObjectSchema`. Both of those also STAMP the def, so the
142
+ warning fires precisely where the def still resolves and nothing is broken. A
143
+ hand-written schema served through any OTHER `DataSource` — the break surface —
144
+ reaches a reader raw: it never passes through this code and produces **no
145
+ warning at all**. On that path the failure is exactly as silent as before.
146
+ A reader-side or shared-resolver diagnostic, which would cover it, remains open
147
+ on objectui#6837.
148
+
149
+ ⚠️ **Dated note, 2026-09-29 — the call-site count above has since moved —
150
+ objectui#7650.** "exactly two production call sites" above held when this change landed
151
+ (`045d20ba8`). Later in this same release objectui#7650 (PR objectui#8868) added a call
152
+ on `MetadataProvider`'s by-name object-schema serve path. Re-measured for objectui#10979
153
+ on `main` at `2eaf5be27`, `normalizeSchemaReferenceKeys` had three production call sites:
154
+ two in `MetadataProvider`, on its list path and on its by-name path, and one in
155
+ `ObjectStackAdapter.getObjectSchema`.
156
+ `.changeset/7650-metadata-item-reference-canonicalisation.md` (PR objectui#8868) states
157
+ what ships; the text above is kept as the reading of this change.
158
+
159
+ ## What did NOT change
160
+
161
+ **Every key these readers EMIT is byte-identical**, and that was verified
162
+ mechanically over the whole diff rather than asserted. Eleven of the sixteen
163
+ sites write a target onto a bag whose own contract spells it `reference_to` (or
164
+ camelCase `referenceTo`): the six whose read and write share a line —
165
+ `RecordDetailDrawer`, `RelatedList`, `buildDefaultPageSchema`, `ListView`,
166
+ `FilterConditionField`, `resolveActionParams` — plus five more that read on one
167
+ line and emit on another, and so are just as much emitters: `RecordDetailView`,
168
+ `RecordMetaFooter`, `ObjectGallery`, `fieldEnrichment` (all `reference_to`) and
169
+ `UserFilters` (`referenceTo`). Only the right-hand read narrowed anywhere; the
170
+ emitted key is what its target contract declares, and renaming it would be a
171
+ separate change.
172
+
173
+ **Three readers were deliberately left alone.** `LookupCellRenderer`
174
+ (`fields/src/index.tsx`), `LookupField` and `UserField` read `FieldMetadata` —
175
+ ObjectUI's OWN contract, whose `LookupFieldMetadata` declares `reference_to` and
176
+ never declares `reference`. They are fed by the emitters above and by published
177
+ example schemas (`examples/schema-catalog/src/schemas/fields-lookup/*.json`), so
178
+ narrowing them would break in-repo producers, and `plugin-grid`'s
179
+ `relationalMetaCopySet.derivation.test.ts` re-derives its read set from exactly
180
+ those three sources — where `reference_to` is recorded with verdict
181
+ `adapter-stamped`. `DetailViewFieldSchema` is likewise untouched.
182
+
183
+ ⚠️ **Dated note, 2026-09-30 — the stamp, the three readers and the emitted key have since moved — objectui#11070.** Later in this same release objectui#11070 (round 4) retired `reference_to` inside ObjectUI as well. `normalizeFieldReferenceKeys` now FOLDS a legacy `reference_to` / `referenceTo` onto `reference` and no longer stamps `reference_to`, so "Stamping is deliberately unchanged" and "stamp both snake_case keys" above no longer describe what ships. `LookupCellRenderer`, `LookupField` and `UserField` read `reference` only; `LookupFieldMetadata`, `MasterDetailFieldMetadata` and `DetailViewField` (with `DetailViewFieldSchema`) declare `reference`; and the emitters listed under "What did NOT change" write `reference`. A hand-written schema served through a `DataSource` other than `ObjectStackAdapter` that spells only `reference_to` therefore also loses the lookup picker's target and the read cell's name. `.changeset/11070-reference-to-round4.md` states what ships; the text above is kept as the reading of this change.
184
+ - 0349555: A sankey with no positive flow says so, instead of rendering an empty div
185
+ (objectui#7140).
186
+
187
+ `AdvancedChartImpl`'s sankey arm keeps only strictly positive measures, so a
188
+ chart handed **real rows** whose measure is all `0`, all `null`, all negative,
189
+ or unparseable built no links and returned a bare `<div>`. Measured in Chromium
190
+ against a populated control: the control drew 1 `<svg>` / 7 `<path>` /
191
+ 26 descendants; each of those four tiles rendered `descendantCount: 1`,
192
+ `svgCount: 0`, `textContent: ''`, and their screenshots hashed identical to one
193
+ another. No marks, no text, no `role` — a tile indistinguishable from a widget
194
+ that had crashed, which is the one distinction the file's other refusals exist
195
+ to make.
196
+
197
+ It now renders through the `ChartRefusal` shell those refusals already use —
198
+ same box, same `role="status"`, and a new `data-chart-error="no-positive-flow"`
199
+ — reading *"This chart has no flow to draw: no row's `<measure>` is above
200
+ zero."*
201
+
202
+ Two boundaries are deliberate and pinned:
203
+
204
+ - **No rows at all is untouched.** That is the empty-result question, answered
205
+ upstream in `ObjectChart` where the query outcome is known; a sentence about
206
+ what the rows contain would be false about a dataset with no rows in it.
207
+ - **One positive row among zeros still draws.** The refusal fires on an empty
208
+ link set, never on a thin one.
209
+
210
+ One code and one sentence for three causes (a genuinely all-zero flow, values a
211
+ flow cannot represent because they are negative, and measures `Number(…) || 0`
212
+ folds to zero): naming any single cause would be false for the other two, so
213
+ the copy names the predicate the filter actually applies, which is true for all
214
+ three. No recovery is promised. Every other chart family is byte-identical —
215
+ eight of the twelve tiles in the browser sweep hashed unchanged.
216
+ - 0dc2c93: `compareTo` on a `scatter` chart is no longer supported — scatter joins pie / donut /
217
+ funnel on the list of chart families that ignore it (objectui#7402, maintainer ruling
218
+ 2026-09-03).
219
+
220
+ **This removes a published capability, deliberately.** Until now a `chartType: 'scatter'`
221
+ chart (and the dashboard widget types `scatter` and `bubble`, which both render as one)
222
+ with `compareTo` set synthesised a muted "previous period" overlay series. It drew the
223
+ wrong picture: a scatter binds ONE measure, and the renderer reads y through the single
224
+ `YAxis dataKey={series[0].dataKey}`, so the overlay was plotted on the PRIMARY series' y
225
+ — "previous period" painted exactly on top of "current" (objectui#7194).
226
+
227
+ Enforce-or-remove: rather than keep drawing that, the capability is removed until it can
228
+ be drawn honestly. Drawing a real second measure on a scatter needs the multi-measure
229
+ projection recorded as option A of objectui#7194, which is not built (zero authored
230
+ callers). **If and when that projection lands, `compareTo` on a scatter returns with
231
+ it** — it is the same missing mechanism, one payment.
232
+
233
+ What changes for authors:
234
+
235
+ - A `compareTo` on a scatter is now IGNORED rather than drawn. The primary series still
236
+ renders exactly as before — nothing refuses, nothing goes blank, and no comparison
237
+ query is issued on the inline chart path.
238
+ - No `<measure>__comparison` (inline chart) / `<measure>__compare` (dashboard) series is
239
+ appended for a scatter, so a compare-to scatter document also never reaches the
240
+ two-or-more-series scatter refusal being added under objectui#7194.
241
+ - Charts that keep the overlay: line, area, bar, horizontal-bar, combo. Charts that
242
+ ignore `compareTo`: pie, donut, funnel and — as of this change — scatter (and the
243
+ `bubble` widget type that renders as a scatter).
244
+
245
+ Reachability at the time of the change: **0** authored scatter/bubble instances in-repo
246
+ across both spellings (control `"type": "bar"` fires at 5 example files); incidence in
247
+ deployed tenant metadata is not measurable from this repo.
248
+ - 01c27c4: Fix: a chart series' `type` override (`ChartDataSeries.type`, objectui#6121) is now
249
+ honoured when the series array is written in the internal `dataKey` binding, not only
250
+ the `name` binding — the same sentence #2945 shipped for the other
251
+ dialect.
252
+
253
+ `ChartRenderer`'s `isInternalShaped` fast path (introduced by #2945 to fix a different
254
+ bug — a `name`-shaped `series` shadowing the normalized `dataKey`-shaped one) took the
255
+ raw authored array untouched whenever every entry already carried `dataKey`, bypassing
256
+ `normalizeSeries` — the only place `type` is translated to the renderer-internal
257
+ `chartType`. So an author who wrote `series: [{ dataKey: 'revenue' }, { dataKey:
258
+ 'margin', type: 'line' }]` — both keys independently valid on `ChartDataSeriesSchema` —
259
+ got neither the override nor a combo chart, silently.
260
+
261
+ `ChartRenderer` now always takes the series array through the one normalization layer
262
+ (objectui#2880 S1) instead of special-casing the `dataKey` shape around it.
263
+
264
+ **Breaking on unmodified documents, deliberately — this changes rendered output.** A
265
+ chart authored with a `dataKey`-shaped series carrying a `type` override used to render
266
+ one family for every series; it now renders the mix the author actually described (the
267
+ card's own regression case: two bars become one bar and one line).
268
+
269
+ **Two more effects on the delivered key set for a `dataKey`-shaped series array**,
270
+ undisclosed until now — `normalizeSeries` is a no-op on a well-formed entry, but these
271
+ two cases were never well-formed under the old fast path either:
272
+
273
+ - An i18n `label` written as a `{ en, zh-CN, … }` record on a `dataKey`-shaped entry was
274
+ previously forwarded as that raw object; it is now resolved to a plain string (the
275
+ first string-valued limb), matching what a `name`-shaped series already got from
276
+ `normalizeChartSchema`.
277
+ - A `dataKey`-shaped entry whose `dataKey` does not resolve to a non-empty string (and
278
+ has no `name` to fall back to) was previously forwarded as-is; it is now dropped from
279
+ the delivered series array, matching what a `name`-shaped series with no usable key
280
+ already got.
281
+
282
+ No existing well-formed internal caller (`DashboardRenderer`, `ObjectView`, the dataset
283
+ path) changes behaviour — every series entry those callers construct already carries a
284
+ non-empty string `dataKey` and a string `label`.
285
+ - 967e5d8: Chart `series[].opacity` and `series[].dashArray` are honoured on every series,
286
+ not only on a `variant: 'comparison'` one (objectui#7698).
287
+
288
+ `@objectstack/spec` declares `ChartSeries.opacity` ("Override series opacity")
289
+ and `ChartSeries.dashArray` ("Override stroke dash pattern") as unconditional
290
+ per-series overrides, and `normalizeSeries` read both off every series. The
291
+ renderer then honoured them on a comparison overlay only, so an author who
292
+ wrote `{ name: 'cost', opacity: 0.6 }` or `{ name: 'cost', dashArray: '4 4' }`
293
+ on a primary series got a mark drawn exactly as if the key were absent. Fixed
294
+ in the renderer rather than by narrowing the published declaration to match:
295
+ the spec is the contract of record, and a renderer's partial implementation
296
+ does not get to dictate it (AGENTS.md #0.1).
297
+
298
+ **Two gaps, not one.** The `variant` guard was the visible half — `comparisonStyle`
299
+ returned `null` for any other variant. The second half only showed on
300
+ `dashArray`: that helper already returned an AUTHORED dash for every family
301
+ (the `??` takes the left side whatever the kind), and the **Bar and Scatter
302
+ marks** then passed `fillOpacity` only, dropping `strokeDasharray` and
303
+ `strokeOpacity` on the floor — so an authored dash was lost on those two
304
+ families even on a comparison series. A fix aimed at the guard alone would have
305
+ left that untouched. `comparisonStyle` is now `seriesStyle`, and the Bar and
306
+ Scatter marks pass all three channels.
307
+
308
+ **Comparison series are unaffected.** The authored branch already won over the
309
+ muted defaults, and those defaults stay gated on `variant: 'comparison'`: a
310
+ comparison series carrying neither key keeps its lower opacity and its `'4 4'`
311
+ line/area dash exactly as before. The two stroke defaults no mark ever consumed
312
+ (bar and scatter — neither is stroked by this renderer) are now spelled
313
+ `undefined`, so opening `strokeOpacity` on those marks does not hand them a
314
+ default they never had.
315
+
316
+ Only a stroked mark can show a dash, so on a `bar` or `scatter` mark an
317
+ authored `dashArray` reaches the mark and paints nothing — the mark's geometry,
318
+ not a condition on the key. The `ChartDataSeries` mirror docs and the
319
+ plugin-charts reference, which stated the comparison-only condition as an
320
+ interim measure, are corrected in the same change.
321
+ - edea22a: **BREAKING** — `SchemaRendererProvider`'s `dataSource` prop, and the context
322
+ type every `useSchemaContext()` consumer reads back, are the published
323
+ `DataSource` contract instead of `any`.
324
+
325
+ **FROM** `dataSource={anything}` **TO** `dataSource={adapter}` — a `DataSource`
326
+ from `@object-ui/types`, or `null` / `undefined` when the host has no adapter
327
+ bound.
328
+
329
+ ```ts
330
+ // before — compiled, and failed at runtime on the first find()
331
+ <SchemaRendererProvider dataSource={'not-an-adapter'}>
332
+ // before — compiled, and no reader can do anything with it
333
+ <SchemaRendererProvider dataSource={{}}>
334
+ // after
335
+ <SchemaRendererProvider dataSource={adapter}> // a DataSource
336
+ <SchemaRendererProvider dataSource={undefined}> // "I have no adapter"
337
+ ```
338
+
339
+ Both sites are typed `DataSource | null | undefined` — the spelling
340
+ `useSettledSchema` in this same package already used. The two absences are part
341
+ of the contract, not a weakening of it: a Studio preview, a `kind:'react'` page
342
+ rendered before the host's adapter connects, and a widget probe driving
343
+ `apiFetch` alone all render with nothing bound, and every reader in the tree
344
+ already guards for it. What the union refuses is everything that is not an
345
+ adapter: a string, an empty object, a plain data bag, a partial adapter missing
346
+ a required member.
347
+
348
+ The measured cost, both halves, because the two `any`s have different blast
349
+ radii (measured separately on `origin/main`, whole-repo type-check over the 33
350
+ packages that depend on `@object-ui/react`):
351
+
352
+ - the **context type** — the `any` that reaches every `useSchemaContext()`
353
+ reader — reds **7 diagnostics at 7 sites in 4 packages**, all of them
354
+ production code or a mocked module factory.
355
+ - the **provider prop** — the injection points — reds **52 diagnostics at 27
356
+ sites in 11 packages**, all but one of them test doubles.
357
+
358
+ That ordering is the reverse of the prediction on the card: the context `any`
359
+ was expected to be the expensive one because it infects the whole tree, and it
360
+ is the cheap one, because every reader in the tree already guarded and none of
361
+ them ever reached past `find` / `getObjectSchema`. The prop is the expensive
362
+ one, because the injection points are overwhelmingly test doubles that were
363
+ never complete adapters. The full accounting is on objectui#7912.
364
+
365
+ Two runtime behaviours change, both in the "no adapter" direction and both
366
+ strictly closer to what the surrounding code already intended:
367
+
368
+ - `@object-ui/components`' `kind:'react'` page passed an empty object as its
369
+ "no adapter yet" stand-in. An empty object is TRUTHY, so it walked past every
370
+ `if (!dataSource)` guard written to catch exactly that state and failed later,
371
+ at the call. It now passes the absent adapter itself, so the guard fires where
372
+ it was meant to. The module-constant identity that stand-in existed for is
373
+ preserved: `null` is a primitive, so the provider's memo is unaffected.
374
+ - `@object-ui/plugin-calendar`, `@object-ui/plugin-gantt` and
375
+ `@object-ui/plugin-kanban` collapse a `null` adapter from the context to
376
+ `undefined` before handing it to their widget, whose prop declares the single
377
+ spelling `dataSource?: DataSource`.
378
+
379
+ Nothing else moves at runtime: no value flowing through this key changes, and
380
+ no data path is touched. A TypeScript consumer outside this repo that handed
381
+ this prop something other than an adapter now gets a compile error naming the
382
+ key (TS2322 / TS2739 / TS2740), which is why the FROM/TO is spelled out above.
383
+
384
+ Five `as any` reads of this context in `@object-ui/fields` are gone — they were
385
+ redundant the moment the seam became honest — and `LookupField`'s local
386
+ re-declaration of the imported context as a `Context` of `any`, which laundered
387
+ its `dataSource` read while looking typed, is gone with them. Both directions of
388
+ the contract are pinned against the real compiler in
389
+ `SchemaRendererContext.dataSourceType.pin.test.ts`, and the card's planted
390
+ documentation probe (a bare string in `packages/react/README.md`'s provider
391
+ example) now fails `pnpm check:doc-snippets`, where it used to exit 0 with zero
392
+ diagnostics.
393
+
394
+ objectui#7912.
395
+ - aa6be30: `ChartRendererProps.schema.series`: the `dataKey` arm now declares `type?: string`, the
396
+ per-series family override the `name` arm already declares, with the same member type
397
+ (objectui#8086). The TypeScript face now states what `ChartDataSeriesSchema` (where
398
+ `dataKey` and `name` are each independently optional beside `type`) and the renderer
399
+ (every entry goes through `normalizeSeries`, `01c27c431`) accept. No other arm changes:
400
+ `chartType` stays on the `dataKey` arm alone, and wins when an entry writes both.
401
+
402
+ The accept set only widens. A value typed as the `dataKey` arm may now carry `type`, and
403
+ an entry narrowed with `'dataKey' in entry` may read it; nothing that compiled before is
404
+ refused. `ChartRendererProps` is not re-exported by name from the package entry: it
405
+ reaches consumers as the props type of the exported `ChartRenderer` component. An object
406
+ literal written against the whole `series` union was already accepted before this
407
+ change, because `type` is known to the `name` arm; the gap was at the arm.
408
+
409
+ The `ObjectChartSchema.series` docblock in `@object-ui/types` is restated to match: that
410
+ copy of the internal arm does not carry `type`, and its type is unchanged.
411
+ - 0758bd8: `ObjectChart` refuses an object-bound chart that declares no category axis (objectui#8168).
412
+
413
+ An object-bound chart that named no field to group by used to be composed anyway.
414
+ `runAggregate` passed `schema.aggregate` to `ds.aggregate(objectName, { field,
415
+ function, groupBy, filter })` with no guard on `groupBy`, and the `ds.find` leg
416
+ handed the same bag to `aggregateRecords`, which buckets every record on
417
+ `record[groupBy] ?? 'Unknown'`. So one path asked a driver to group by `undefined`
418
+ and the other collapsed the whole object into a single `'Unknown'` bar — and which
419
+ of those a reader saw was decided by the data source, not by the renderer. The only
420
+ loud states this component had were a fetch `error` (`chart-error`) and a generic
421
+ "No data yet"; neither is a statement about an absent binding.
422
+
423
+ `ObjectCalendar`, `ObjectGantt` and `ObjectTimeline` each already refuse a view that
424
+ declares no axis. This is the fourth, following `ObjectTimeline`'s shape
425
+ (objectui#7459): a `role="alert"` box, `data-testid="chart-missing-category-axis"`,
426
+ naming the bindings the author can declare — `aggregate.groupBy`, `xAxisKey`,
427
+ `xAxis.field` — rendered from the resolver's own vocabulary so the message cannot
428
+ drift from what the resolver reads.
429
+
430
+ **Breaking, deliberately — and this repo ships breaking as `minor`.** A chart that
431
+ previously rendered an `'Unknown'`-bucketed bar (or whatever the driver did with
432
+ `groupBy: undefined`) now renders the refusal instead. That is the intent: the
433
+ picture it drew was not a picture of the data.
434
+
435
+ It keys on the CATEGORY alone. A measure may legitimately be absent — `count` takes
436
+ no field — so refusing on an absent measure would refuse `count` grouped by a
437
+ declared category, a chart that renders correctly. Four shapes are deliberately
438
+ untouched: an ADR-0021 `dataset` chart (which may declare no dimension), a chart
439
+ carrying authored `data` or a `bind` scope (no field name is read to fetch those
440
+ rows), a spec-shape `xAxis: { field }` with no `xAxisKey` (resolved through
441
+ `normalizeChartSchema`, this package's one translation of the author-facing shape),
442
+ and every schema the five in-repo producers compose today — all five floor their own
443
+ category, so none of them can reach the refusal.
444
+
445
+ This does **not** retire the six `'name'` / `'value'` floors at the three relay faces;
446
+ that is the remainder of objectui#7547 and is mechanical only once this screen exists.
447
+
448
+ New key `chart.unconfigured.noCategoryAxis` in all ten locale packs.
449
+ - 0a174f3: fix(plugin-dashboard,core,plugin-charts): route a structured `aggregate.groupBy` on `object-metric` to the spec-shape wire
450
+
451
+ An authored `aggregate.groupBy` is a union — a bare field name, or the
452
+ structured date-bucketing node `{ field, dateGranularity?, alias? }` — and the
453
+ two need different queries. The spec-shape
454
+ `{ groupBy: GroupByNode[], aggregations, where }` reaches `engine.aggregate` and
455
+ runs the server-side date-bucket engine; the legacy
456
+ `{ field, function, groupBy, filter }` query reaches the cube/analytics wire,
457
+ whose `dimensions` the contract declares as an array of dimension NAMES and
458
+ which does not honour `dateGranularity` at all.
459
+
460
+ `ObjectChart.runAggregate` has routed between the two since objectui#7946.
461
+ `ObjectMetricWidget.computeOne` had no such branch: it forwarded the authored
462
+ value straight through, so a metric widget carrying a structured node posted
463
+ `dimensions: [{ field: 'closed_at', dateGranularity: 'month' }]` — an object
464
+ where a name is declared. Nothing refused it and nothing reported it, so the
465
+ author asked for one question and the platform answered another (objectui#8613).
466
+
467
+ - The routing test and the payload are now one function,
468
+ `objectAggregateSpecQuery` (with `isStructuredGroupBy`) in `@object-ui/core`.
469
+ `ObjectChart.runAggregate` and `ObjectMetricWidget.computeOne` both call it,
470
+ so the two renderers cannot post different wires for one authored shape. The
471
+ measure alias is `chartMeasureKey`'s answer, i.e. what the chart's own
472
+ `aggregateValueKey` already delegated to — the chart's posted payload is
473
+ unchanged.
474
+ - `ObjectMetricWidgetProps.aggregate.groupBy` said `string`. That was a claim
475
+ about the author which nothing upstream backed: the value crosses two `any`
476
+ seams on the way in, so the declaration refused the node at neither compile
477
+ time nor runtime. It is now the contract's union, taken by reference through
478
+ `ObjectChartSchema['aggregate']`.
479
+
480
+ Unchanged, and pinned as controls: a plain string `groupBy` and an absent one
481
+ (floored at the single `'_all'` bucket) still take the legacy query byte for
482
+ byte, and an ARRAY `groupBy` still travels there too — it is not this union's
483
+ object arm, and it must keep reaching the producer-side refusal `503cd8b89`
484
+ landed in the adapter.
485
+ - bb383e8: Retire the "Tremor/simple format" adapter in `ChartRenderer` — the `index`,
486
+ `category` and `value` reads (objectui#8650, triage ruling `5619609278` on
487
+ AGENTS.md #0.1: route to the producer, ⛔ not a declaration).
488
+
489
+ **Breaking, deliberately, for three keys — and NOT for the fourth.** The card
490
+ filed four undeclared reads as one group. A cast-aware read census plus a
491
+ TypeScript-checker declaredness reading measured them apart, and they do not
492
+ share one verdict:
493
+
494
+ - `index` / `category` (they aliased the category axis) and `value` (it became
495
+ a single series) are **retired**. They are declared on no published face —
496
+ not `ChartSchema`, not its zod mirror, not `ChartRendererProps.schema` — are
497
+ advertised by no registry `inputs`, and are taught by no doc, guide or skill.
498
+ A structural producer census over `packages/`, `apps/`, `examples/`,
499
+ `content/docs/` and the skills corpus (5695 files, 74 chart nodes) found
500
+ **zero** nodes writing `category` or `value` and **one** writing `index` —
501
+ this repo's own test for the adapter. The zero is read against controls that
502
+ fire in the same population (`xAxisKey` 41 nodes, `series` 44, `chartType`
503
+ 40, `data` 52) and a nonsense key that returns 0.
504
+ - `categories` is **not retired and is unaffected**. It is a declared member of
505
+ the published `ChartSchema` and of its zod mirror, is documented in the
506
+ schema reference as an alternative series list, and was ruled live by
507
+ the maintainer ruling of 2026-08-31. `normalizeChartSchema` — the single translation point
508
+ (objectui#2880 S1) — already consumed it, so `ChartRenderer`'s own branch was
509
+ a second, un-normalized read that no well-formed chart could reach. Removing
510
+ it changes nothing for a well-formed chart and removes two wrong answers for
511
+ a malformed one: `categories: 'revenue'` reached `.map` on a string and threw
512
+ during render, and a `categories` whose entries the normalizer rejects
513
+ produced a `[{ dataKey: '' }]` series.
514
+
515
+ **Migration.** Write the canonical spellings, which every producer in the
516
+ measured corpora already writes: `xAxisKey` (or the spec's `xAxis: { field }`)
517
+ for the category axis, and `series` (or `categories`) for the plotted columns.
518
+ A chart that still writes `index` / `category` binds no category axis, so
519
+ `AdvancedChartImpl` falls back to its default category key, `name`. What that
520
+ degrades to depends on the rows, and only one half of it is a refusal: rows
521
+ carrying no `name` column hit its existing on-screen `missing-category-key`
522
+ refusal, while rows that DO carry one plot silently against `name` instead of
523
+ the column the author named — a wrong picture rather than a refusal. One that
524
+ still writes `value` plots nothing.
525
+
526
+ Also deletes six `(schema as any)` casts that the published declarations had
527
+ already made unnecessary — `colors`, `categoryColors` and `categoryOrder` on
528
+ `ChartRenderer`, and `colors`, `compareTo` and `series` on `ObjectChart` (the
529
+ objectui#8327 bucket-(b) class: declared, then read through a needless cast).
530
+ No behaviour changes with them; `ObjectChart.tsx` now has no `(schema as any)`
531
+ read left at all.
532
+ - 47ba790: **BREAKING** — `ObjectChartBlock`, the registry shell exported beside `ObjectChart`, declares its props instead of taking `(props: any)` (objectui#8885).
533
+
534
+ **Clause-②: yes** — a published component signature narrows. `@object-ui/plugin-charts` 17.6.0 publishes `ObjectChartBlock: (props: any) => React.JSX.Element`. It is now `(props: Omit<ObjectChartProps, 'schema'> & { schema: BaseSchema }) => React.JSX.Element`. No export is added, removed or renamed, and no runtime behaviour changes.
535
+
536
+ **FROM** any props bag **TO** `ObjectChart`'s own props, with `schema` as the raw node:
537
+
538
+ ```tsx
539
+ // before: all of these compiled
540
+ <ObjectChartBlock schema={node} dataSorce={adapter} /> // misspelled prop, silently dropped
541
+ <ObjectChartBlock schema="object-chart" /> // a string, not a node
542
+ <ObjectChartBlock schema={{ objectName: 'deal' }} /> // a node with no `type`
543
+ // after: pass a node (`BaseSchema`: at least `type`) and only ObjectChart's props
544
+ <ObjectChartBlock schema={{ type: 'object-chart', objectName: 'deal', chartType: 'bar' }} dataSource={adapter} />
545
+ ```
546
+
547
+ `schema` is `BaseSchema`, the node `SchemaRenderer` hands every registered renderer, and not `ObjectChartSchema`. The shell receives the node BEFORE `ElementDataSourceGate` maps its `dataSource` binding, so a node that carries only `{ type, dataSource: { object, view } }` is legal input. So is a node typed `chart`, the alias this same shell is registered under. The other props (`dataSource`, `onSegmentClick`) are `ObjectChartProps`' own, so a misspelled prop name is now an excess-property error.
548
+
549
+ Migration: a direct caller that passed extra props through the shell must pass only `schema`, `dataSource` and `onSegmentClick`, and a node without `type` must add one. Nodes rendered through the registry (`SchemaRenderer`) are unaffected: the registry resolves renderers as `any`, and the type annotations and the single hand-off cast erase at build.
550
+ - 1b969ae: A scatter's two numeric axes now honour the spec `ChartAxis` every other chart
551
+ family already honours, instead of dropping it.
552
+
553
+ `min` / `max` / `stepSize` / `logarithmic` / `title` / `format` written on a
554
+ chart's `xAxis` or `yAxis` were accepted by the schema and resolved by
555
+ `normalizeChartSchema`, and then the scatter branch — alone among the families —
556
+ never handed them to its axes. An author who pinned `min: 0` to stop a
557
+ truncated-baseline reading got the truncated baseline back; a `logarithmic` axis
558
+ rendered linear; explicit `stepSize` ticks and an axis `title` never appeared.
559
+ Nothing refused and nothing looked broken: the chart drew confidently, at a scale
560
+ the author had overridden.
561
+
562
+ **This is a user-visible render change.** A scatter that already declared any of
563
+ those keys will now draw at the domain, tick spacing, scale and labelling it
564
+ asked for rather than at recharts' auto-fitted ones. A scatter that declares no
565
+ axis config renders exactly as before — the derivation contributes no prop when
566
+ there is no axis to derive from.
567
+
568
+ Both axes are covered, not just the y the report named: scatter is this
569
+ renderer's only family whose x is a numeric MEASURE rather than a category band,
570
+ so it is the only one where these keys mean anything on x, and it was dropping
571
+ them there too.
572
+
573
+ The repair composes with the scatter edge margin rather than shadowing it. That
574
+ margin is spent as recharts' axis `padding`, which insets the pixel range and
575
+ leaves the domain alone, so an authored domain and the margin are now live at
576
+ once — the domain is the author's, and no extreme mark is half-painted outside
577
+ the plot box. A pin holds that pair together.
578
+
579
+ No new key, no new exported symbol: the derivation the other families use is now
580
+ shared by both orientations, and the scatter axes spread it.
581
+ - 3f983f4: A chart heading now follows the VIEWER's language instead of the author's key order
582
+ (objectui#8943).
583
+
584
+ `@objectstack/spec` types `ChartConfigSchema.title` as `I18nLabel` — a plain string OR
585
+ an inline locale map — so `{ "title": { "zh-CN": "定价", "en": "Pricing" } }` is authored
586
+ surface, not an accident. `normalizeChartSchema`'s module-local `label()` resolved the
587
+ map arm with `Object.values(v).find(isString)`: **the first string in key order**. It
588
+ never read the active language, never preferred `default` or `en`, and had no diagnostic
589
+ — the chart rendered confidently in whichever language the author happened to type first.
590
+ Reordering the JSON, with no other change, showed the same viewer a different language.
591
+
592
+ `label()` now delegates to `pickLocalized` (`@object-ui/i18n`), this repository's one
593
+ answer for that union: exact tag -> base language -> a region-qualified sibling ->
594
+ `default` -> `en` -> first value, pinned as the twin of the backend's `resolveI18nLabel`.
595
+ The drill-drawer heading in the same component already routed through it, so `ObjectChart`
596
+ had two answers for one union on one node; it now has one.
597
+
598
+ Every `I18nLabel` slot this module resolves is covered, not only the heading: `title`,
599
+ `subtitle`, `description`, an axis `title` (`normalizeAxis`) and a series `label`
600
+ (`normalizeSeries`).
601
+
602
+ - `normalizeChartSchema(schema, language?)` takes an OPTIONAL second argument — the
603
+ viewer's active language. Every existing call compiles and every non-label key is
604
+ byte-for-byte unchanged. `ChartRenderer` reads it from `useObjectTranslation()` and
605
+ passes it down, which is what closes the defect on the rendered path.
606
+ - Omitting it is not neutral: `pickLocalized` reads an absent language as `en`, so a
607
+ locale map resolves through `default` -> `en` -> first value. That is deterministic
608
+ rather than key-order-dependent, and it is the right answer only for a caller with no
609
+ viewer. A caller that reads a heading off the result should pass one.
610
+ - The admission test did not widen. Only a string or an inline locale map is accepted;
611
+ a number or boolean `title` is still refused rather than stringified, because
612
+ broadening what the renderer accepts belongs in the spec and not in a renderer-side
613
+ coercion (AGENTS.md #0.1).
614
+ - 14582b8: feat(types,plugin-charts): anchor `ObjectChart`'s props to `ObjectChartSchema` and declare the four keys its producers write
615
+
616
+ `ObjectChart` was published as `(props: any)`, so `ObjectChartSchema` anchored
617
+ nothing: every `schema={{ … }}` literal handed to the component was type-checked
618
+ against nothing at all. Four keys its producers write and its renderer reads —
619
+ `xAxisKey`, `series`, `aggregate`, `filter` — were declared on neither published
620
+ copy of the shape, and rode `BaseSchema`'s index signature / `.passthrough()`
621
+ unvalidated. That is the mechanism that let objectui#7891's undeclared `config`
622
+ rung survive from the day it was written.
623
+
624
+ Maintainer ruling 2026-09-09 (option A), applying objectui#6576's gallery
625
+ treatment to the chart:
626
+
627
+ - `ObjectChartProps.schema` is `ObjectChartSchema`; the published `.d.ts` no
628
+ longer says `props: any`. `ObjectChartProps` is exported.
629
+ - The four keys are declared on BOTH copies, with value types taken from their
630
+ READ sites (`ChartRendererProps` for `xAxisKey` / `series`, `ObjectChart.tsx`
631
+ for `filter`) rather than copied from any producer's literal — except where
632
+ `@objectstack/spec` already owns the shape, which is `aggregate`: that one is
633
+ declared BY REFERENCE as `ChartAggregate` / `ChartAggregateSchema`, so the
634
+ authoring door here and at the react-page publish gate are one shape and
635
+ cannot drift into two dialects.
636
+ - `colors` converges: the zod mirror has declared it since objectui#3913 and the
637
+ TS interface did not, a drift no ratchet could see because a mirror-only key
638
+ is in neither of the parity guard's two difference ledgers.
639
+
640
+ Two of the four are AUTHORABLE (`aggregate`, `filter` — the spec names this
641
+ component's own props as their carrier and parses `aggregate` at the react-page
642
+ publish gate) and two are INTERNAL, relay-composed (`xAxisKey`, `series` — every
643
+ producer computes them and the spec's author-facing vocabulary refuses the
644
+ internal spellings by name). The internal pair is declared anyway, because it was
645
+ already passing through unvalidated: declaring buys the value check without
646
+ minting authorable vocabulary, and each description says which it is.
647
+
648
+ ⚠️ **Dated note, 2026-09-28 — `series` has since gained an AUTHOR arm — objectui#10770.**
649
+ Later in this same release each `ObjectChartSchema.series` entry became ONE of two arms,
650
+ on both faces: the spec's `ChartSeriesSchema` (TS: `ChartSeries`), by reference, which is
651
+ AUTHORABLE (the react tier's `ObjectChart` block publishes it as its `series` prop, and the
652
+ react-page wrapper forwards the authored array onto the node), or the unchanged INTERNAL
653
+ `{ dataKey }` arm, still typed from `ChartRendererProps`. So "two are INTERNAL,
654
+ relay-composed (`xAxisKey`, `series` …)" now holds for `xAxisKey` and for the
655
+ `{ dataKey }` arm only, and "except where `@objectstack/spec` already owns the shape,
656
+ which is `aggregate`" now takes in the `{ name }` arm of `series` too. The `series`
657
+ description names both arms. `.changeset/10770-object-chart-react-tier-node.md` (PR
658
+ objectui#10802) states what ships; the text above is kept as the reading of this change.
659
+
660
+ `filter` keeps BOTH arms (a `FilterArray` or the ObjectQL `$filter` object), and
661
+ narrowing to one is a decision LOCAL TO THIS NODE rather than a fleet-wide one:
662
+ the six sibling `object-*` widgets that declare `filter` are already array-only,
663
+ so there is no cross-widget convention to renegotiate. What blocks the narrowing
664
+ is this component's own drill-down spread, which mis-composes the array arm into
665
+ index keys; that is named as the successor on the member's docblock.
666
+
667
+ BEHAVIOUR, from what the anchor made visible: `ObjectChart` resolved the
668
+ group-by column twice and only one site normalised the structured
669
+ `groupBy: { field, dateGranularity }` node. The other used the raw union as a row
670
+ index, a field name and a drill-filter key, so a date-bucketed chart lost its
671
+ option-colour resolution, its label→raw reverse map and its drill filter to a
672
+ lookup on the node's stringification. Both sites now share one normalisation, and
673
+ the behaviour is pinned at runtime by
674
+ `plugin-charts/src/__tests__/ObjectChart.structuredGroupBy-7946.test.tsx` (drill
675
+ filter keyed by the projected column, alias and field arms, and the label→raw
676
+ recovery) — a compile-time pin cannot see a wrong runtime value flowing from a
677
+ correctly-typed read.
678
+
679
+ The drill drawer's heading fallback now resolves `schema.title` through
680
+ `pickLocalized` (`@object-ui/i18n`) instead of using it as a bare string. The
681
+ spec types that slot as `I18nLabel` — a plain string or an inline locale map —
682
+ and the map arm used to reach the heading as an object.
683
+
684
+ ## Migration — what a TS consumer of `<ObjectChart schema={…}>` must change
685
+
686
+ The headline is that a wrong VALUE TYPE is now a compile error, but three
687
+ NARROWINGS bite first, and they are what the eight edited test files in this
688
+ change had to absorb:
689
+
690
+ - **`type: 'object-chart'` is now required on the literal.** A minimal
691
+ `schema={{ objectName: 'account', chartType: 'bar' }}` no longer compiles.
692
+ - **`chartType` must be the declared union.** A literal written inline is fine;
693
+ one hoisted into a non-`const` object widens to `string` and is refused. Use
694
+ `as const` (or annotate the holder as `ObjectChartSchema`).
695
+ - **`series` entries must be `dataKey`-shaped.** The renderer's internal arm is
696
+ `{ dataKey, … }`; the spec's author-facing `{ name, … }` arm is a different
697
+ shape, translated by `normalizeChartSchema` one layer down.
698
+
699
+ ⚠️ **Dated note, 2026-09-28 — a `{ name }`-shaped `series` entry is now accepted —
700
+ objectui#10770.** Later in this same release `ObjectChartSchema.series` took the spec's
701
+ `ChartSeriesSchema` (TS: `ChartSeries`), by reference, as a second arm beside the
702
+ `{ dataKey }` one, on both faces. So "`series` entries must be `dataKey`-shaped" no
703
+ longer holds, and that narrowing no longer bites:
704
+ `series: [{ name: 'total', label: 'Invoice value' }]` compiles and parses. An entry with
705
+ neither `name` nor `dataKey` is still refused, and `normalizeChartSchema` is still the one
706
+ translation between the arms.
707
+ What bites instead is reading: an element of `ObjectChartSchema['series']` is a union, so
708
+ a consumer narrows it (for example with `'dataKey' in entry`) before it reads `.dataKey`.
709
+ `.changeset/10770-object-chart-react-tier-node.md` (PR objectui#10802) states what ships;
710
+ the text above is kept as the reading of this change.
711
+
712
+ And, from the by-reference `aggregate`:
713
+
714
+ - **`aggregate.function` and `aggregate.groupBy` are REQUIRED**, `aggregate.field`
715
+ stays optional (only `count` counts rows rather than a column), the structured
716
+ `groupBy` node must name its `field`, and unknown members are REFUSED by name
717
+ rather than dropped. `aggregate: {}` and `{ field: 'amount' }` used to compile
718
+ and no longer do. This is `ChartAggregateSchema`'s accept set, which the publish
719
+ gate has always enforced on authored `<ObjectChart aggregate={…}>` literals —
720
+ so a document that compiles today is one the platform already accepted.
721
+
722
+ The RENDERER still accepts more than this and still draws its named refusal
723
+ screen for an aggregate that declares no category axis (objectui#8168): untyped
724
+ producers forward `aggregate` as `any`, so out-of-contract documents keep
725
+ arriving at runtime. Narrowing the declaration is about what an author may
726
+ WRITE, not about what the renderer will tolerate.
727
+
728
+ ⚠️ Anchoring does not buy rejection of a MISSPELLED key on the node itself:
729
+ `BaseSchema` carries `[key: string]: any` (objectui#5155), the same ceiling
730
+ objectui#6576 accepted. `aggregate` is the exception, and only because the spec's
731
+ own object is strict.
732
+
733
+ ⚠️ **Dated note, 2026-09-28 — `aggregate` is no longer the only strict member —
734
+ objectui#10770.** Later in this same release a `series` entry on the `{ name }` arm became
735
+ the spec's closed `ChartSeriesSchema`, by reference, so on the zod face a misspelled key
736
+ in one is refused, while one in a `{ dataKey }` entry is still stripped. So "`aggregate`
737
+ is the exception" no longer holds alone. Other changes in this release did the same for
738
+ `drillDown` (objectui#8885) and for `xAxis` / `yAxis` (objectui#10518), each a strict spec
739
+ object by reference. A misspelled key on the node itself still passes, as above.
740
+ `.changeset/10770-object-chart-react-tier-node.md` (PR objectui#10802) states what ships;
741
+ the text above is kept as the reading of this change.
742
+ - 6f017e9: Dashboard chart widgets no longer render as a blank area when their height class
743
+ resolves to `auto`.
744
+
745
+ `ChartContainer`'s min-size fallback was applied only to the wrapper `div`.
746
+ Recharts measures its own `width:100%;height:100%` size-detector element, and a
747
+ percentage height never resolves against an ancestor's `min-height`, so the
748
+ wrapper obediently grew to 280px while the measured element stayed at 0 — and
749
+ Recharts renders no children at all for a non-positive box. The result was a
750
+ widget card with its title over an empty chart area: no marks, no refusal, no
751
+ empty state, and permanent, because a box that never changes fires no resize.
752
+ The floor is now applied to the measured element as well, under the same
753
+ condition, so an author's explicit height still wins.
754
+
755
+ ### Patch Changes
756
+
757
+ - a9f34df: fix(plugin-charts): the legend swatch carries its series colour as a custom property
758
+
759
+ `ChartLegendContent` painted each swatch with an inline `backgroundColor`, the
760
+ colour-bearing property AGENTS.md's styling carve-out names as forbidden. The
761
+ swatch now publishes the author-declared colour as `--color-bg` and a static
762
+ `bg-(--color-bg)` utility consumes it, the same shape the tooltip indicator in
763
+ the same file already uses, so the theme keeps control of the rule. The
764
+ rendered colour is unchanged.
765
+ - 3759c56: Gantt, chart and grid views refresh in place after a write instead of being rebuilt (objectui#10035).
766
+
767
+ `ObjectGrid`, `ObjectGantt` and `ObjectChart` query for themselves and read no refresh input, so both object-view layers could show them a write only by remounting them — which lost a gantt's scroll, collapsed groups and zoom, a chart's open drill-down, and a grid's selection, column state and in-progress edit. Each now refetches when the data-invalidation bus (`notifyDataChanged` / `useDataInvalidation` from `@object-ui/react`) reports a change to the object it reads:
768
+
769
+ - `@object-ui/plugin-grid`: `ObjectGrid` re-reads its rows into the same table.
770
+ - `@object-ui/plugin-gantt`: `ObjectGantt` re-reads silently and keeps the chart mounted, as its toolbar refresh does.
771
+ - `@object-ui/plugin-charts`: `ObjectChart` re-runs its query on both binding shapes — an object-bound aggregate follows its `objectName`, a dataset-bound chart follows the dataset's base object as the `queryDataset` answer names it.
772
+ - `@object-ui/plugin-view`: the object view keys every view on its identity alone, and declares on the bus its own saves and deletes and each `onMutation` write it hears.
773
+ - `@object-ui/app-shell`: the object page keys its list and its chart view on identity alone, and declares on the bus the changes it learns of without a data-source write — server actions, flows, imports, realtime events and an explicit refresh.
774
+
775
+ Rows a host hands these components (`data`, `bind`, inline values) are still the host's to refresh; only the components' own queries follow the bus.
776
+ - bff63cc: A cartesian chart whose series are bound to a column no row carries now says so, naming the
777
+ column, instead of drawing an empty frame (objectui#10396). The tile renders the existing
778
+ `data-chart-error` placeholder with the new code `missing-series-key` and a sentence worded after
779
+ `missing-category-key`: "This chart cannot plot its series: no row has a `KEY` field." Several
780
+ missing keys are joined with "or". A console warning names the missing keys and the keys the rows
781
+ do carry, as the category refusal's warning does.
782
+
783
+ The shape is objectui#8266's: a fieldless count projects its value as `count`, and a series bound to
784
+ `value` over those rows drew the category ticks and zero marks, silently. What now refuses, each of
785
+ which drew zero marks before: a bar / column / horizontal-bar / line / area / combo chart with rows,
786
+ when not one row carries ANY bound series key. That holds whatever else the chart declares: a
787
+ stack, a declared `min` / `max` or `stepSize`, or a second axis.
788
+
789
+ What keeps drawing, unchanged: a chart where even one row carries the key; a chart where one bound
790
+ key is carried and another is not (the carried series draws); and a dotted key such as `a.b`,
791
+ which the chart resolves into nested rows. A dotted key is never judged missing, so a chart that
792
+ binds one stays silent even when it resolves nothing. A column no row carries beside a key that is
793
+ carried but has no numeric value (for example an all-boolean series) also stays silent, as before.
794
+
795
+ Precedence: `missing-category-key` wins when the category key is missing too, and
796
+ `no-plottable-series` still owns a chart with no series at all. A key that IS carried but holds no
797
+ numeric value on any row keeps `no-numeric-value` (objectui#7195). Scatter, pie, donut, funnel,
798
+ treemap and radar never receive this code.
799
+ - a506001: A chart's `xAxis` object is no longer dropped when it carries `min`, `max`, `stepSize`,
800
+ `logarithmic` or `position` without also carrying `title`, `format` or `showGridLines`
801
+ (objectui#10516).
802
+
803
+ `normalizeChartSchema` kept the x-axis object only when one of those three hand-named keys
804
+ was present. Any other axis lost everything but its `field`, so a scatter authored with
805
+ `xAxis: { field: 'progress', min: 0, max: 200, stepSize: 50 }` drew the auto-fitted x
806
+ domain and ticks instead of the ones it declared, and nothing refused. The object is now
807
+ kept whenever it carries any key of the spec's `ChartAxisSchema` other than `field`. The
808
+ key set is the one the normalizer reads, not a second hand-written list.
809
+
810
+ **User-visible render change.** A scatter whose `xAxis` declares `min` / `max` /
811
+ `stepSize` / `logarithmic` with no title, format or grid flag now draws the x scale it asked
812
+ for. That completes objectui#9675's x-axis repair for axes written that way. An `xAxis`
813
+ carrying only `field` renders as before.
814
+
815
+ `position` on `xAxis` now survives normalization too, but at this change no renderer reads
816
+ it: the x axis draws along the bottom whatever it says.
817
+
818
+ ⚠️ **Dated note, 2026-09-25 — the renderer now reads `xAxis.position` — objectui#10587.**
819
+ Later in this same release the x axis takes the side `position` names where that axis can
820
+ run along it, and a side it cannot take draws the default side with a note naming the key.
821
+ The sentence above is kept as the reading of this change; the objectui#10587 entry states
822
+ what ships.
823
+ - 9fbbb17: BREAKING (`@object-ui/components`): `RefreshIndicator`'s `ariaLabel` prop is now required and has no default. It used to default to the English literal "Refreshing", so the progress bar on every view that passed no name was announced in English to screen-reader users in every locale (objectui#10580). A `RefreshIndicator` rendered without `ariaLabel` now fails to type-check.
824
+
825
+ (The bump is `minor` by this repo's release model: objectui's major follows the `@objectstack` family major, and its own breaking changes ship as `minor` with the breaking semantics stated here.)
826
+
827
+ Migration: pass the bar's accessible name from your own translation layer, for example `ariaLabel={t('grid.refreshing')}`. The component renders the string you pass as the bar's `aria-label` and does not translate it.
828
+
829
+ `ObjectGrid`, `ListView`, `ObjectChart` and `ObjectDataTable` now name their refresh bar in the active locale. The grid and the list read the `grid.refreshing` and `list.refreshing` keys their pull-to-refresh text already uses. The chart reads `chart.refreshing` and the dashboard data table reads `dashboard.refreshing`, both with no English literal behind them, so a host that renders either one with no i18next instance at all gets the key itself as the bar's name.
830
+
831
+ `@object-ui/i18n`: new keys `chart.refreshing` and `dashboard.refreshing` in every built-in locale pack.
832
+ - 33b324c: A chart's `xAxis.position` now places the x axis, and a side that axis cannot take is
833
+ refused with a note that names the key (objectui#10587).
834
+
835
+ `position` is declared on `@objectstack/spec`'s `ChartAxisSchema`, which `xAxis` takes by
836
+ reference, and since objectui#10516 the normalizer keeps it on the x-axis object. No axis
837
+ read it, so `xAxis: { field: 'month', position: 'top' }` validated and still drew the axis
838
+ along the bottom.
839
+
840
+ - **bar, line, area and scatter** (and a combo derived from its series' families): the x
841
+ axis runs across the plot. `top` draws it along the top; `bottom` draws it along the
842
+ bottom, the default.
843
+ - **horizontal-bar**: the `xAxis` object configures the category axis, which runs down the
844
+ plot there (its `format` already applied to that axis). `left` or `right` puts the
845
+ category axis on that side; the default stays the left.
846
+ - **A side the axis cannot take** (`left` / `right` across the plot, `top` / `bottom` on
847
+ horizontal-bar) draws the axis at its default side, and the chart shows a note under the
848
+ plot (`data-chart-note="x-axis-position"`) naming `xAxis.position`, the value, and the
849
+ side the axis was drawn on. The value still validates: `ChartAxisSchema` is shared by the
850
+ x and y axes, and this change does not narrow it.
851
+
852
+ A top axis also mirrors its title and its rotated tick labels, so both stay on the far side
853
+ of the axis from the plot.
854
+
855
+ **User-visible render change** only for a chart whose `xAxis` declares `position`. The
856
+ bottom / no-position controls in `ChartRenderer.xAxisPosition-10587.test.tsx` pin that a
857
+ chart declaring none still draws its x axis along the bottom with no note.
858
+ - a95ec20: An object-bound `object-chart` that names its category with `aggregate.groupBy` alone now draws that
859
+ category, instead of being refused with `missing-category-key` (objectui#10634).
860
+
861
+ A node with `objectName`, `aggregate: { field: 'amount', function: 'sum', groupBy: 'stage' }` and
862
+ `series`, and no `xAxisKey` or `xAxis`, passed `ObjectChart`'s absent-category screen (objectui#8168),
863
+ whose remedy lists `aggregate.groupBy` first. The schema `ObjectChart` then handed to `ChartRenderer`
864
+ carried no category key, so the chart floored it on `'name'`, found no `name` column in the grouped
865
+ rows, and refused. The author had followed the refusal's own remedy and was refused again, over a key
866
+ they never wrote.
867
+
868
+ `ObjectChart` now forwards the category when the author named it only through `groupBy`. The key it
869
+ forwards is the column the aggregate projects its group under, per the contract's result-column
870
+ convention (`chartAggregateResultKeys` in `@objectstack/spec/ui`): the `groupBy` string, or
871
+ `alias ?? field` for the structured `{ field, dateGranularity?, alias? }` node. This is the same
872
+ column the dashboard relays already bind (objectui#8269). An `alias` therefore binds the aliased
873
+ column the rows carry, not the underlying field.
874
+
875
+ What does not change:
876
+
877
+ - An authored category axis always wins. The forward fills an absent slot only, and asks this
878
+ package's own axis resolution whether one is authored: `xAxisKey`, a spec-shape `xAxis: { field }`,
879
+ or a bare string `xAxis`. It never writes over any of them.
880
+ - Charts that draw authored rows (`data`, or a `bind` scope) are unchanged, with or without
881
+ `objectName`. Their rows never went through the aggregate, so `groupBy` says nothing about their
882
+ columns.
883
+ - Dataset-bound charts (ADR-0021) are unchanged. They take their category from their dimensions.
884
+ - The absent-category refusal fires on exactly the same nodes as before.
885
+ - 4df0f3d: A chart's `yAxis[].position` now places each value axis, and `horizontal-bar` lays out and
886
+ binds its axes (objectui#10654, the y-side twin of objectui#10587; objectui#10655 folded in).
887
+
888
+ `position` is declared on `@objectstack/spec`'s `ChartAxisSchema`, which each `yAxis` entry
889
+ takes, and the normalizer keeps it. No axis was placed by it: it only chose which entry
890
+ configured the right-hand axis a combo always draws (and the value format of a right-bound
891
+ series), and how the normalizer bound the series it derives from the entries. So a lone
892
+ `position: 'right'` entry drew its axis on the left, `top` / `bottom` passed in silence, a second
893
+ entry was always the right-hand axis, and with no `series` a first entry at `right` took both
894
+ derived series to the right-hand axis.
895
+
896
+ - **Where each entry is drawn.** `placeYAxes` (in `normalizeChartSchema`) is the one place a y
897
+ side is resolved; the normalizer binds each series it derives from the entries with the same
898
+ call, so a derived series plots against its own entry's axis.
899
+ - bar (`column`), line, area, combo and scatter: `left` / `right`. Scatter draws one y axis,
900
+ the first entry's.
901
+ - horizontal-bar: the value axes run across the plot, so `bottom` / `top`; a series'
902
+ `yAxis: 'left'` binds the bottom axis and `'right'` the top one.
903
+ - A lone entry's axis is drawn on the side it names. With two entries each is drawn on the
904
+ side it names, and an entry that names no side takes the side the other left free.
905
+ - **Both entries naming the same side:** the first keeps it and the second is drawn on the
906
+ other side, with a note naming `yAxis[1].position`. `yAxis[0]` is already the primary axis
907
+ (its `showGridLines` governs the horizontal grid), and declaration order is the one
908
+ tie-break an author can read off the metadata.
909
+ - A side a value axis cannot take is refused: the entry is placed as if it named none, and
910
+ the chart shows a note under the plot (`data-chart-note="y-axis-position"`) naming
911
+ `yAxis[N].position`, the value and the side the axis was drawn on. `ChartAxisSchema` is
912
+ shared by both axes and is not narrowed.
913
+ - **horizontal-bar.** Two entries draw two value axes, along the bottom and the top, and the
914
+ bars bind to them; they were bound to `yAxisId`s the branch never rendered, and bars were
915
+ dropped. `xAxis.title` is drawn on the category axis, and the `yAxis` title is laid out for an
916
+ axis running across the plot, under it at the bottom and over it at the top.
917
+ - **Grid and annotations** bind to the rendered left value axis whenever the value axes carry
918
+ ids (a combo always; bar, line, area and horizontal-bar with two entries). They were bound to
919
+ an axis id those charts do not render: a two-entry chart's horizontal grid lost its per-tick
920
+ lines and its `axis: 'x'` annotations, a combo with fewer than two entries drew no annotation,
921
+ and a two-entry horizontal-bar dropped its `axis: 'y'` annotations.
922
+ - **A right-hand value axis** lays its title out on the far side of its tick labels, the left
923
+ layout mirrored, as a top x axis mirrors the bottom one.
924
+
925
+ **User-visible render change** for a chart whose `yAxis` entries declare `position`, a
926
+ horizontal-bar with an axis title or two `yAxis` entries, the grid and annotations of two-entry
927
+ and combo charts, and the title of every right-hand value axis.
928
+ `ChartRenderer.yAxisPosition-10654.test.tsx` pins each row against its `bar` control.
929
+ - daf1405: A chart's `yAxis` entry after the second now carries a note naming it, instead of going unused
930
+ in silence (objectui#10691).
931
+
932
+ `@objectstack/spec` declares `ChartConfig.yAxis` as an uncapped list, so a third entry
933
+ validates; a bar (`column`), line, area, combo or `horizontal-bar` chart draws at most two value
934
+ axes, one per slot. Such an entry drew no axis and no note, while its `title`, `format`, `min`
935
+ and `max` went unused and a series derived from it plotted against the right-hand axis.
936
+
937
+ - **The note.** Every entry after the second gets a `p role="note"
938
+ data-chart-note="y-axis-undrawn"` under the plot, in the `ChartFootnote` channel the
939
+ `x-axis-position` / `y-axis-position` notes use. It names `yAxis[N]` and, when the chart
940
+ declares neither `series` nor `categories`, the axis its `field` is plotted against: the one
941
+ in the right-hand slot (the top one on `horizontal-bar`), and the entry drawn there.
942
+ - **One answer.** `placeYAxes` lists the entries it draws on no axis (`undrawn`), with the slot
943
+ a series derived from one binds to. The normalizer binds that series from the list, and the
944
+ renderer writes the note from it. The binding itself is unchanged: the right-hand slot, as
945
+ before.
946
+ - **Not narrowed.** The accepted count does not move, and the chart is not refused: the spec
947
+ owns the `yAxis` face.
948
+
949
+ A chart with two entries or fewer, a `scatter` chart (it draws one y axis), and a family that
950
+ draws no value axis render as before.
951
+ - 1fc77fd: A `scatter` chart's `yAxis` entry after the first now carries a note naming it, instead of
952
+ going unused in silence (objectui#10721).
953
+
954
+ `@objectstack/spec` declares `ChartConfig.yAxis` as an uncapped list, so a second entry
955
+ validates; a `scatter` chart draws one value axis, the first entry's. objectui#10691 gave the
956
+ bar (`column`), line, area, combo and `horizontal-bar` charts a note for each entry after the
957
+ second, and left `scatter` as it was: with `series` authored, a second or later entry drew no
958
+ axis and no note, and its `title`, `format`, `min` and `max` went unused.
959
+
960
+ - **The note.** Every entry after the first gets a `p role="note"
961
+ data-chart-note="y-axis-undrawn"` under the plot, in the `ChartFootnote` channel
962
+ objectui#10691's note uses. It names `yAxis[N]` and the one axis the chart draws, `yAxis[0]`.
963
+ It says nothing about a series derived from the entry: with neither `series` nor
964
+ `categories`, two entries that carry a `field` are refused as before
965
+ (`data-chart-error="scatter-multi-series"`).
966
+ - **One answer.** The scatter slice moved into `placeYAxes`, which now takes the chart family,
967
+ so the renderer and the normalizer place the same entries. A series the normalizer derives
968
+ from an entry after the first now binds to the slot the one axis is drawn in, where it used to
969
+ bind as if the chart drew two axes. A scatter reads no series binding and measures its one
970
+ series against that axis, so what it draws does not move.
971
+ - **Not narrowed.** The accepted count does not move, and the chart is not refused: the spec
972
+ owns the `yAxis` face.
973
+
974
+ A `scatter` chart with one entry, and every other family, render as before.
975
+ - 256b4c9: A filter refused as malformed (`INVALID_FILTER`) no longer throws out of render or out of a click
976
+ handler at the sites that merge filters there. That holds whether this layer's filter converter
977
+ refused it or, at the drill seam, the spec's own `parseFilterAST`. Each site now surfaces the
978
+ refusal the way its siblings already do (objectui#10789). The filter stays **refused**: nothing
979
+ that was refused is accepted, and no site falls back to "no filter".
980
+
981
+ - **`ElementDataSourceGate` / `useElementDataSource`** (`@object-ui/react`): the gate's merge of
982
+ the component's `filter` with the composed binding, and the merge of a saved view's filter
983
+ with the binding's own, lowered through the throwing converter inside render. A refused
984
+ filter from either now makes the binding `missing` with a new optional `filterRefusal` on the
985
+ result (`error` carries its message). The gate then draws, in place of the block, the
986
+ malformed-filter notice the wrapped blocks draw: `view.malformedFilter`, naming the refused
987
+ operator or field. `element:record_picker`, which reads the same hook, shows its
988
+ configuration-error panel with the refusal.
989
+ - **`PeoplePicker` / `RecordPickerDialog`** (`@object-ui/fields`): the recents merge and the
990
+ dialog's `mergedFilter` lower through `toFilterNodeSafely`; a refusal is shown in the picker's
991
+ existing error state and no query runs without the filter.
992
+ - **Drill-downs** (`ObjectChart`, `ObjectPivotTable`, `DatasetWidget`, `DatasetReportRenderer`):
993
+ a widget or report filter refused at the drill seam made the drill click throw. The converter
994
+ refuses some (a spec `$not`, say). The spec's `parseFilterAST` refuses others, such as a scalar
995
+ on a list operator in either dialect: `[['stage', 'in', 'won']]` or `{ stage: { $in: 'won' } }`.
996
+ The drill is now not opened or emitted (never with the scope dropped), and the refusal is logged
997
+ with `console.warn`, naming the operator. `composeDrillFilter` (`@object-ui/core`) now throws
998
+ one refusal type: the spec's `INVALID_FILTER` error is re-raised as a `FilterOperatorError` with
999
+ its message and the same `INVALID_FILTER` / 400 envelope, and any other error passes through
1000
+ unchanged.
1001
+ - **`ListView` export** (`@object-ui/plugin-list`): the server export builds its filter inside
1002
+ its `try`, so a refusal is named in the export menu instead of escaping the click.
1003
+ - **`convertFiltersToAST`** (`@object-ui/core`): an `$or` member that is the TRUE identity no
1004
+ longer stops the members after it from being read. `{ $or: [{}, { a: {} }] }` is now refused
1005
+ like `{ $or: [{ a: {} }, {}] }` already was, instead of answering TRUE (every row); a TRUE
1006
+ disjunct beside members that lower still absorbs its `$or`.
1007
+ - eb97ce6: fix(plugin-timeline,plugin-charts,i18n): a stored date-only day reads as that day in every viewer zone on the timeline, on a chart's date axis and in the published date helpers (objectui#10866, slice 2)
1008
+
1009
+ These readers parsed a `YYYY-MM-DD` value with the engine's own `Date` parse, which reads it as UTC midnight, so every viewer west of UTC saw the day before. Each now reads the value through `toDisplayDate` from `@object-ui/core`, which rebuilds a date-only value at local midnight of the day it names. A value with a time part keeps its instant.
1010
+
1011
+ - **Timeline.** The object timeline's date bucket, its sort and the item date the renderer prints read the value that way. West of UTC an item due today no longer sits under "Overdue", one due tomorrow no longer sits under "Today", and the `short` and `long` item faces print the stored day. The `iso` face prints a date-only value's day from local getters, and an instant's UTC day as before. A day its month does not have, such as `2026-02-30`, is no longer rolled into March: the item sits under "No date", sorts with the dateless items, and its `short` and `long` faces are the ones an unparsable value already had. An unparsable value now sorts with the dateless items too, and the `iso` face prints such a value as written where it used to throw. The gantt variant's axis headers, extent and bar positions are not changed here, so west of UTC its bar tooltip, which prints through the same item-date function, now names the stored day while the axis above it still reads the day before, until a later slice of objectui#10866.
1012
+ - **Charts.** A date-only category on the x axis reads `Sep 1` for `2026-09-01`, and `Sep 2026` on a month-grained axis, in every zone. When the display locale is a tag `Intl` refuses, the tick still prints the stored day: it reads the local-midnight value with local getters, because its `toISOString()` would name the day before east of UTC. An instant's fallback keeps its UTC day. A date-only category naming a day its month does not have falls back to the raw category, the face any other non-date category gets, instead of the rolled day.
1013
+ - **i18n helpers.** `formatDate`, `formatDateTime`, `formatRelativeTime` and `formatDateSpec` read a date-only string as the day it names: `formatDateTime` shows midnight of that day, and `formatRelativeTime` counts to the start of it. `formatDateSpec` applies its `timeZone` to an instant only, and formats a date-only value in the local zone, where the shared step's local midnight reads back as its day; a `timeZone` west of UTC no longer turns `2026-09-01` into August 31st for every viewer. All four return a day its month does not have as the raw string. `formatRelativeTime` now returns an unparsable value as its string, the face the other three already gave one, where it used to throw a `RangeError`.
1014
+ - 6837bfa: fix(plugin-charts): an `object-chart` whose family is on `specType` gets `compareTo` exactly as the same family on `chartType` does, so a `specType: scatter` chart with `compareTo` draws instead of refusing, and `specType: pie` makes no comparison fetch (objectui#11530)
1015
+
1016
+ `ObjectChart` decides two things with `chartTypeIgnoresCompareTo` from `@object-ui/core`: whether to run the comparison query, and whether to add the `__comparison` overlay series. Both read only `chartType`. A family on `specType` (the react tier's channel, which objectui#11520 routes) was invisible to them. So a family that ignores `compareTo` (`pie`, `donut`, `funnel` and `scatter` at this release) still fetched the comparison window and still got the overlay:
1017
+
1018
+ - **`specType: scatter` with `compareTo`** made 2 aggregate calls and rendered the refusal "A scatter plots one measure. Keep exactly one series: amount, amount__comparison" (with the document's own measure). It now makes 1 call and draws the scatter.
1019
+ - **`specType: pie`, `donut` or `funnel` with `compareTo`** made a comparison call whose rows the chart never drew. It now makes 1 call. The picture is unchanged.
1020
+
1021
+ Both decisions now read the family the chart draws, which is the value `normalizeChartSchema` hands `ChartRenderer`: `chartType`, else `specType`. The family list stays the one in `chartTypeIgnoresCompareTo`. The object fetch also re-runs when that family changes, where it used to re-run on a `chartType` change only.
1022
+
1023
+ No other document changes: a family on `chartType` and every family that keeps `compareTo` (on either key) fetch and draw as before.
1024
+ - f82f857: fix(charts): a cartesian chart that declares no series at all now says so instead of drawing an empty frame (objectui#4695)
1025
+
1026
+ A `bar` / `horizontal-bar` / `line` / `area` / `combo` (and `column`) chart handed
1027
+ data rows but no series binding — no `series`, no `categories`, no y-axis `field`,
1028
+ so `series` reaches the renderer as `undefined` — drew axes, grid, tooltip and
1029
+ legend around zero marks and said nothing. It now renders the same placeholder
1030
+ objectui#4683 introduced for a computed-but-empty series list
1031
+ (`data-chart-error="no-plottable-series"`, `role="status"`), with its own sentence:
1032
+ "none was declared" rather than "no measure or group reached" the axis, and a
1033
+ console warning that lists the row keys a series could name.
1034
+
1035
+ Unchanged: a chart with no rows; pie, donut, funnel, radar and scatter charts,
1036
+ which fall back to a `value` column and keep drawing; and every chart that
1037
+ declares its series.
1038
+ - 39f4309: Published typings from every `vite-plugin-dts` package now carry an explicit extension on
1039
+ every relative specifier, and a type error in the declaration build now fails the build
1040
+ instead of being printed and ignored (objectui#5439, objectui#5483).
1041
+
1042
+ **Consumers on `moduleResolution: nodenext` or `node16` may see NEW type errors, and that
1043
+ is the fix working.** These packages re-export mostly through NAMED re-exports —
1044
+ `export { useObjectChat } from './useObjectChat'`. TypeScript could not follow the
1045
+ extensionless hop, but it still DECLARED the name, so the symbol resolved to a silent
1046
+ `any`. Nothing errored; consumers simply got no types. With the extension emitted, the
1047
+ symbol carries its real type, and any call site that was relying on the `any` now type
1048
+ checks for the first time. This is the mode that produced the 21 residual `TS7006` on
1049
+ `@object-ui/app-shell` reported against objectui#5365 — a type hole that opened quietly,
1050
+ unlike objectui#5365's own `export * from './ui'` packages where the same defect surfaced
1051
+ immediately as `TS2305: has no exported member`.
1052
+
1053
+ 410 extensionless relative specifiers across 19 packages were emitted before this change;
1054
+ the count is now 0 in all 22 packages that build typings through `vite-plugin-dts`.
1055
+ `@object-ui/fields` was already clean — its sources write explicit `.js` specifiers — and
1056
+ is wired so it stays that way.
1057
+
1058
+ The second half changes no emitted output today: 22/22 packages built green unmodified, so
1059
+ making the declaration step's exit code honest turns nothing red. It changes what a FUTURE
1060
+ regression does — print and exit 0, versus fail the build.
1061
+ - 3beef6d: The spec's `dataSource` element binding is now DECLARED by the blocks that read
1062
+ it, so the html tier stops reporting the one working saved-view spelling as
1063
+ `unknown-prop` (objectui#6678).
1064
+
1065
+ `PageComponentSchema.dataSource` — `{ object, view, filter, sort, limit }` — is
1066
+ the one spelling that resolves a saved view for an object-bound block. It works,
1067
+ and it drew the identical `unknown-prop` warning as the two spellings that do
1068
+ nothing (`viewName`, `view`), because `validateTree` looks a prop up in the
1069
+ block's declared `inputs` and no registration declared this key. On the tier
1070
+ built to accept AI-authored pages, where the diagnostic IS the contract, the
1071
+ only signal pointed away from the key that works.
1072
+
1073
+ Adopting the maintainer ruling of 2026-08-29 — option B **in the injection
1074
+ form**:
1075
+
1076
+ - `ELEMENT_DATA_SOURCE_INPUT` is the single declaration, in `@object-ui/core`
1077
+ beside the binding's own semantics; `Registry.register` emits it for any
1078
+ registration whose renderer passed through the new `elementDataSourceBlock()`
1079
+ seam. One mechanism, one copy — not a hand-kept declaration per block, which is
1080
+ the shape that drifts and that a new block forgets. The seam lives in
1081
+ `@object-ui/core` and is re-exported by `@object-ui/react` beside
1082
+ `ElementDataSourceGate` for discoverability; call sites take the core import,
1083
+ because a registration runs at module scope and this repo's suites partially
1084
+ mock `@object-ui/react`.
1085
+ - Seventeen renderers, in thirteen files across twelve packages, reach the seam
1086
+ and now publish the key to the save gate, the parser whitelist, the generated
1087
+ JSX authoring types and the block list. The card named nine blocks; the tree
1088
+ also has `plugin-grid`, `plugin-timeline`, two further `plugin-form` blocks and
1089
+ `element:record_picker` — nothing was hand-listed, so the mechanism covered
1090
+ them. `element:record_picker` consumes the gate's HOOK and status panels rather
1091
+ than the wrapper tag (its object lives under `properties`), and was found by a
1092
+ render probe rather than by reading sources.
1093
+ - `dataSource` on a block that does NOT read it (`flex`, `card`) still reports
1094
+ `unknown-prop`. Adding the key to `sdui-parser`'s `BASE_PROPS` was refused for
1095
+ exactly this reason — that set mirrors `BaseSchema`, and silencing the key
1096
+ everywhere would make the diagnostic lie in the other direction.
1097
+ - New `check:element-data-source-declaration` fails any source that consumes the
1098
+ gate without reaching the seam, so a block added tomorrow cannot forget.
1099
+
1100
+ Behaviour of the binding itself is unchanged — this is a declaration, not a
1101
+ resolution change. The saved view still resolves its columns, and an
1102
+ unresolvable `view` still fails loudly rather than widening to the object's full
1103
+ scope.
1104
+
1105
+ The spec/registry parity gates (repo-wide and the `record:related_list` per-block
1106
+ pin) now derive their accepted set from the WHOLE node contract rather than from
1107
+ `ComponentPropsMap[type]` alone. `PageComponentSchema` accepts and keeps
1108
+ `dataSource` on a page-component node — it is a node-level key, a sibling of
1109
+ `type` and `className`, not a per-block prop — so the gates' previous complaint
1110
+ was measurably wrong. Derived from the spec, not exempted, and both still
1111
+ discriminate against an invented key.
1112
+ - 40c4711: A sankey that drew only SOME of its rows now says how many (objectui#7148).
1113
+
1114
+ The sankey arm keeps strictly positive measures
1115
+ (`data.filter((r) => (Number(r?.[dataKey]) || 0) > 0)`), so a mixed dataset
1116
+ drew a normal, healthy, confident chart of a fraction of itself and nothing
1117
+ anywhere recorded that the other rows existed. Measured in Chromium across 27
1118
+ tiles: `[{New business: 40}, {Refunds: -25}, {Chargebacks: -12}]` rendered
1119
+ `svg: 1`, `path: 3`, 18 descendants, no `role`, no text, and — against a live
1120
+ console control that did fire on the same instrument — zero console output.
1121
+ Its screenshot hashed byte-identical to five other datasets, one of which
1122
+ genuinely had a single row. Six datasets, one image: a reader had no bit of
1123
+ information separating a complete flow from a third of one.
1124
+
1125
+ The discard itself stands — a flow has no negative width, so it is the only
1126
+ thing that arm can do with those rows. What is added is a footnote under the
1127
+ plot naming the ratio and the predicate the filter applies:
1128
+
1129
+ > Showing 1 of 3 rows — 2 rows have no `amount` above zero, which a flow
1130
+ > cannot draw.
1131
+
1132
+ It names the predicate rather than a cause because `Number(…) || 0` folds
1133
+ negatives, zeros, `null`, unparseable strings and a missing key into one
1134
+ discard, and all five were measured reaching this branch beside a survivor;
1135
+ naming any one of them is a sentence that is false for the other four.
1136
+
1137
+ A complete flow is byte-for-byte unchanged and gains no wrapper element, and a
1138
+ drawable sankey is never replaced by prose: the `no-positive-flow` refusal
1139
+ still owns the case where NOTHING survives the filter, and the "one positive
1140
+ among zeros still draws" boundary still draws — that fixture is itself a
1141
+ thinned dataset, so it now draws *and* says so.
1142
+ - e8c553b: A scatter handed more than one series now refuses instead of drawing a false picture.
1143
+
1144
+ Scatter binds one measure: `series[0].dataKey` is the y axis, and every series was
1145
+ handed the same rows through that one axis. A second series therefore added a
1146
+ colour and a legend entry and nothing else — measured, two series over two rows
1147
+ painted four symbols at two positions, each drawn twice, and the second measure's
1148
+ values appeared nowhere on the plot. The data was valid and the picture was
1149
+ confidently wrong, which no existing refusal could see.
1150
+
1151
+ A `chartType: 'scatter'` with two or more `series` now renders the renderer's
1152
+ refusal shell under `data-chart-error="scatter-multi-series"`, stating that a
1153
+ scatter plots one measure, naming the fix (keep exactly one series) and listing
1154
+ the series keys it was handed. A single-series scatter is unchanged.
1155
+
1156
+ This refusal counts authored `series` only. `compareTo` on scatter is out of
1157
+ its scope: objectui#7402 ruled (b) that scatter joins pie / donut / funnel in
1158
+ excluding `compareTo` — `supportsCompareTo` and the dashboard widget path stop
1159
+ synthesising a comparison series for it, so no `…__comparison` overlay is ever
1160
+ built for a scatter and this guard is never reached by a compare-to document.
1161
+ That exclusion ships as a separate change; until it lands, a `compareTo`
1162
+ document still reaches the renderer as two series and refuses here today.
1163
+
1164
+ No multi-measure projection is built (maintainer ruling, 2026-09-02): nothing
1165
+ in-repo authors a two-series scatter, so that capability waits for a real caller.
1166
+ The refusal copy is `chart.scatterOneMeasure` in all ten locale packs.
1167
+ - 3632060: A cartesian chart whose numeric axis has rows but nothing to build a scale from now says so
1168
+ instead of drawing an empty frame (objectui#7195). The tile renders the existing
1169
+ `data-chart-error` placeholder with the new code `no-numeric-value` and a sentence naming the
1170
+ bound key and the row count: "none of the N rows has a numeric value for KEY".
1171
+
1172
+ The answer is keyed on the whole dataset, not on the value's type. A boolean beside one real
1173
+ number still draws (Recharts builds the scale from the number and places the booleans on it), so
1174
+ that mixed chart keeps drawing with no note. What now refuses, each of which drew zero marks
1175
+ before:
1176
+
1177
+ - scatter, when every row's x (or y, or both) is a boolean, or a boolean mixed only with values
1178
+ that cannot be placed. That last shape used to carry a footnote implying one point was drawn;
1179
+ - bar / column / horizontal-bar / line / area / combo with no stacked bar or area series, when
1180
+ no row gives any bound series a value the axis can scale: every value boolean, `null` or an
1181
+ unparseable string. A line's `stack` has no effect in the renderer, so a "stacked" line is
1182
+ judged the same way.
1183
+
1184
+ A chart with ANY stacked bar or stacked area series is never refused, whatever its values: the
1185
+ stack gives its axis a scale on its own, and every series on that axis is placed on it (an
1186
+ all-boolean series beside a stacked all-`null` one draws). Some of those charts draw nothing
1187
+ (a stacked all-`null` bar, a stacked area of unparseable strings); they stay silent, exactly as
1188
+ before, rather than risk a sentence over marks that are on screen.
1189
+
1190
+ What keeps drawing, unchanged: numeric strings, `Number` objects, `Date` values, range values
1191
+ (an array read by its first two elements, both numbers, so `[1, 3]` and `[1, 2, 3]` alike: range
1192
+ bars and range areas), every chart with a STACKED bar or area series (the stack paints booleans
1193
+ as numbers, and unparseable strings, `NaN`, objects and arrays as full-height bars), a series that
1194
+ is all boolean beside a numeric one, a dual-axis chart with one live axis, and any axis whose
1195
+ spec declares both a numeric `min` and a numeric `max` (the chart builds that scale from the spec
1196
+ and places booleans on it), and any axis whose spec declares a `stepSize` (with one declared
1197
+ bound, the chart's tick builder reads booleans as 0 and 1 and supplies the other end; a `stepSize`
1198
+ alone is left silent too, erring toward silence). A `min` or `max` alone, `logarithmic` alone
1199
+ or an annotation does not build a scale, so those tiles are still refused. `''` counts as a value at zero: a line, area or scatter draws it, and a bar,
1200
+ horizontal-bar or combo paints the same zero-height picture as all-zero data, silently, exactly
1201
+ as before. A bound series key that is not a plain property of any row (a dotted path such as
1202
+ `a.b`, which Recharts resolves into nested rows, or a column no row carries) keeps the chart
1203
+ silent: the refusal fires only when every bound key is read from the rows directly. Rows with no placeable pair still get scatter's
1204
+ `no-plottable-points`, a multi-series scatter still gets `scatter-multi-series`, and
1205
+ `missing-category-key` / `no-plottable-series` still take precedence over this one.
1206
+
1207
+ ⚠️ **Dated note, 2026-09-27 — a column no row carries is now refused when every bound key is one — objectui#10396.** Later in this same release, a bar / column / horizontal-bar / line / area / combo chart whose every bound series key names a column no row carries renders its own refusal, `missing-series-key` ("no row has a `KEY` field"), instead of staying silent. A dotted path, and a column no row carries beside a key the rows do carry, still keep the chart silent. The rest of this entry is kept as the reading of this change; the objectui#10396 entry states what that shape now renders.
1208
+ - bb459ea: Name the scatter legend's series, so its swatch stops reading as a stray data point
1209
+ (objectui#7248).
1210
+
1211
+ The Chart Gallery scatter ("Estimate vs Progress") appeared to draw a seventh point
1212
+ below the x-axis, outside the plot area. It was not a point. `ChartLegendContent`
1213
+ resolves a label as `config[nameKey || item.dataKey || 'value']`, and a `<Scatter>`
1214
+ carries **no `dataKey`** — scatter's keys live on the XAxis/YAxis, not on the mark — so
1215
+ the key collapsed to the literal string `'value'`, missed a config keyed by measure
1216
+ name, and the legend entry rendered its colour swatch with no text beside it. An 8x8
1217
+ square in `--chart-1`, the same colour as the marks, sitting under the x-axis.
1218
+
1219
+ Measured on the running showcase in real Chromium: the swatch sat at cy 341 against a
1220
+ plot area ending at cy 295, on a y scale of 4.835 px per unit — y = -9.5, at x ≈ 45.
1221
+ That is the "x≈40, y≈-10" the report described, to the pixel, and all six real marks
1222
+ were inside the plot area at every viewport width swept from 1440 down to 480.
1223
+
1224
+ **The y domain was not the defect and is unchanged.** Clamping it — the fix the report
1225
+ asked for — would have created the bug it described: mixed-sign and all-negative
1226
+ fixtures are pinned here drawing every mark, because recharts already extends the
1227
+ domain to cover negative values.
1228
+
1229
+ Two changes. The scatter now passes `nameKey` so its legend resolves the measure's
1230
+ label, and `ChartLegendContent` falls back to the series `name` recharts itself put on
1231
+ the legend item when the config lookup misses. The second closes the class rather than
1232
+ this one instance: the swatch renders unconditionally, so a config miss must never
1233
+ leave it anonymous. Charts whose config already resolves are unaffected — only a
1234
+ currently-empty label changes.
1235
+ - 47547d0: Localize the server's built-in aggregate measure titles on dataset charts
1236
+ (objectui#7258 — consumer half of the objectstack#14492 contract; maintainer
1237
+ ruling B, 2026-09-02).
1238
+
1239
+ A dataset-bound chart's aggregate axis / legend title read the analytics
1240
+ service's hard-coded English `Count` on a zh console whose category labels were
1241
+ already Chinese. The renderer was passing `fields[].label` through verbatim —
1242
+ correctly, for an author-declared measure (objectui#4106) — and had no way to
1243
+ tell the server's built-in default apart from an author's label.
1244
+
1245
+ The wire now can: `AnalyticsResult.fields[]` gains an OPTIONAL structural
1246
+ discriminator, `builtinAggregate?: 'count' | 'sum' | 'avg' | 'min' | 'max' |
1247
+ 'count_distinct'`, populated only on the server-side built-in defaults
1248
+ (objectstack#14492). This change is the consumer side of that contract:
1249
+
1250
+ - `@object-ui/core`: `buildChartSeries` now accepts `ChartMeasureField[]` —
1251
+ `ChartResultField` plus the optional `builtinAggregate` carrier
1252
+ (`BuiltinAggregateCarrier`), declared beside the renderer shape rather than
1253
+ on it because the spec this release is built against does not carry the key
1254
+ yet; new `BUILTIN_AGGREGATES` / `BuiltinAggregate` / `isBuiltinAggregate` /
1255
+ `resolveMeasureLabel`; `ChartSeriesOptions.builtinAggregateLabels` carries
1256
+ the locale strings in (core stays React-free and i18n-free — the same
1257
+ division as `nullCategoryLabel`). A field carrying a recognised
1258
+ discriminator resolves through that map; every other field keeps its wire
1259
+ `label` verbatim — never by matching the label's text or the field's name
1260
+ (the rejected option A).
1261
+ - `@object-ui/i18n`: `builtinAggregateLabels(tt)` resolves the six strings
1262
+ through the existing `report.aggregate.*` keys (zh already carried 计数 /
1263
+ 求和 / 平均 / …; all ten packs are pinned to cover the vocabulary).
1264
+ - `plugin-charts` (`ObjectChart`), `plugin-dashboard` (`DatasetWidget`),
1265
+ `plugin-report` (`DatasetReportRenderer`): pass the resolved map to
1266
+ `buildChartSeries`.
1267
+
1268
+ Before: 合作中 / 已流失 / 潜在 under an axis titled `Count`. After: the same
1269
+ chart titled `计数`; an `en` session still reads `Count`; an author-labelled
1270
+ measure (`Tasks`) and a measure literally named `count` without the
1271
+ discriminator are byte-for-byte unchanged. Until the upstream field is
1272
+ populated the wire carries no discriminator and every chart renders exactly as
1273
+ before.
1274
+ - acb5797: Fix categorical (bar/line/area/combo) x-axis labels still being dropped above
1275
+ 5 buckets (objectui#7386, follow-up to objectui#7247).
1276
+
1277
+ `xAxisCommonProps` charged every band axis above `X_AXIS_ALL_LABELS_MAX_BUCKETS`
1278
+ a `minTickGap` of 48px (32px mobile) — a time-series budget that recharts adds
1279
+ **on top of** its own measured-overlap check, not instead of it. A 7-status
1280
+ pipeline at a 290px widget (≈33px/band) dropped 4 of its 7 names even though
1281
+ nothing was actually wide enough to collide.
1282
+
1283
+ `minTickGap` is now `0` for that branch: recharts' own measured-width overlap
1284
+ avoidance (`interval: 'preserveStartEnd'`, unchanged) is the only thing
1285
+ governing tick density above the bound, same as it already was for every tick
1286
+ it kept. Verified in real Chromium — this repo's DOM test environment reports
1287
+ zero text metrics, so it cannot exercise this change at all: at 7 and 8
1288
+ buckets and realistic widget widths, every label now draws with zero measured
1289
+ overlap (checked against each rotated label's true rotated rectangle, not an
1290
+ axis-aligned box); a 180-point daily series at 800px still thinned sensibly
1291
+ (11 ticks, zero overlap) — the "hundreds of points" case objectui#7247 guarded
1292
+ against stays protected by the same measured-overlap check, just without the
1293
+ removed extra margin.
1294
+
1295
+ Not changed: `X_AXIS_ALL_LABELS_MAX_BUCKETS` / the ≤5-bucket "draw everything"
1296
+ branch (objectui#7247, out of this issue's scope); the scatter chart's own
1297
+ `minTickGap` (its x axis is `type="number"`, a genuinely continuous measure,
1298
+ not a categorical band — a different axis with a different, still-correct,
1299
+ constant); and recharts' angled-tick collision model, which stays on its more
1300
+ conservative projected-bounding-box approximation rather than the true
1301
+ parallel-line perpendicular-separation constraint — a deliberate,
1302
+ documented-in-code choice, not an oversight, given this change's scale (a
1303
+ handful of buckets, not a rewrite of recharts' geometry).
1304
+ - e859ad0: Reserve a margin at both ends of a scatter's numeric axes, so an extreme mark is drawn
1305
+ wholly inside the plot area (objectui#7396).
1306
+
1307
+ Both scatter axes are numeric and carry no explicit domain, so recharts fits the domain
1308
+ to `[dataMin, dataMax]` and maps it across the whole plot box. A row at either extreme
1309
+ is therefore **centred on the boundary**, and since a mark has a radius, about half of
1310
+ each extreme symbol paints outside the plot area — the half-dots hugging both edges of
1311
+ the Chart Gallery scatter.
1312
+
1313
+ Measured on that scatter ("Estimate vs Progress") in real Chromium — viewport 1440,
1314
+ widget svg 510x350, plot area x 53..505 / y 5..296:
1315
+
1316
+ - before: marks at cx 53, 256.4, 301.6, 414.6, 459.8, 505 with the y-max row at cy 5,
1317
+ radius 4.514px. The first and last sit exactly on the x boundary and the y-max one on
1318
+ the top boundary, each overhanging its edge by a full radius.
1319
+ - after: cx 65, 257.6, 300.4, 407.4, 450.2, 493 with the y-max row at cy 17. The worst
1320
+ case now clears its nearest edge by 7.486px, on both axes.
1321
+
1322
+ The card reported the x axis; the y axis clipped the same way and is fixed with it.
1323
+
1324
+ **The domain is not touched.** The margin is reserved as recharts' axis `padding`, which
1325
+ insets the pixel range the scale maps into and leaves the domain alone, so every tick
1326
+ **value** is unchanged — the axes still read 0/25/50/75/100 and 0/15/30/45/60 — and only
1327
+ the mapping moves. Padding the domain instead would invent unround tick endpoints, and it
1328
+ would write the same recharts prop a spec-declared `min`/`max` needs (objectui#9675), where
1329
+ whichever landed second would shadow the other.
1330
+
1331
+ The margin is sized to the largest radius the scatter's declared symbol-area envelope
1332
+ admits, not to the radius drawn today, so neither a change in recharts' own default mark
1333
+ size nor a future variable-size mark can reopen it.
1334
+
1335
+ Every scatter's marks shift inward by that margin; nothing else about the chart changes.
1336
+ - ed4a2f1: fix(plugin-charts): `pie-chart`, `donut-chart`, `radar-chart` and `scatter-chart` render as the family they name
1337
+
1338
+ A schema written as `type: 'pie-chart'` (or `plugin-charts:pie-chart`, and likewise donut / radar / scatter) drew a **bar chart**. The four registrations declared their family as `defaultProps: { chartType: … }`, and nothing on the SDUI path has ever read a registration's `defaultProps` — so `ChartRenderer` resolved no family and `AdvancedChartImpl` fell to its `'bar'` default. Valid data, a confidently wrong picture, and no `data-chart-error` that could fire.
1339
+
1340
+ `ChartRenderer` now derives the family from the schema's own `type`, through `normalizeChartSchema` — the package's single translation point, so the exported `normalizeChartSchema` answers what the runtime actually draws. An explicit `chartType` still wins, so `plugin-charts:chart` with `chartType: 'scatter'` is unchanged.
1341
+
1342
+ The five inert `defaultProps: { chartType: … }` are removed with it rather than left beside a mechanism that works. Registration `defaultProps` remains unread on the SDUI path repo-wide; activating it generally is a separate, wider change and is not this one.
1343
+
1344
+ ⚠️ `scatter-chart` now genuinely reaches the scatter arm, so a two-series `scatter-chart` now renders the `scatter-multi-series` refusal it was always supposed to.
1345
+
1346
+ ⚠️ **Dated note, 2026-10-02 — `scatter-chart` is retired — objectui#10859.**
1347
+ Later in this same release objectui#10859 batch 8 (phase 2b) unregistered `scatter-chart` and
1348
+ `plugin-charts:scatter-chart`, and its `CHART_TYPE_KEYWORD_FAMILIES` row went with it; the scatter
1349
+ family is authored as `{ type: 'chart', chartType: 'scatter' }`. `pie-chart`, `donut-chart` and
1350
+ `radar-chart` stay registered. The rest of this entry is kept as the reading of this change.
1351
+
1352
+ ⚠️ **Dated note, 2026-10-02 — `pie-chart`, `donut-chart` and `radar-chart` are retired too — objectui#10859.**
1353
+ Later in this same release objectui#10859 batch 8 (phase 2c) unregistered the three keys, with their
1354
+ `plugin-charts:` twins and `CHART_TYPE_KEYWORD_FAMILIES` rows, after moving `examples/chart-examples.ts`
1355
+ to `{ type: 'chart', chartType: 'pie' | 'donut' | 'radar' }`. `chart:bar` is the one family keyword left.
1356
+ The rest of this entry is kept as the reading of this change.
1357
+ - ff79d38: Read the spec-declared lookup spellings in two readers that could not see them at all
1358
+ (objectui#7435).
1359
+
1360
+ `ObjectChart`'s group-by label chain and `resolveActionParams`' picker group both read the
1361
+ object-schema field def in `snake_case` only. That def is what `getObjectSchema` /
1362
+ `useMetadata()` serve, and it carries the spelling `@objectstack/spec`'s `FieldSchema`
1363
+ declares — so `displayField`, `descriptionField` and `lookupFilters` were dropped on the
1364
+ floor. The chart fell through to the generic `name` heuristic and drew a label the author
1365
+ had explicitly overridden; an action param rendered its picker with defaults. Nothing
1366
+ warned, because the readers were simply reading keys that were not there.
1367
+
1368
+ Each of the three keys now has its declared spelling ranked FIRST, with the existing
1369
+ `snake_case` legs kept behind it in their existing order — the shape objectui#7155
1370
+ established. Measured on the pin this tree resolves, `@objectstack/spec@17.4.0`:
1371
+ `FieldSchema.safeParse` over a minimal lookup def accepts all three camelCase keys and
1372
+ refuses every snake twin with `unrecognized_keys`, with the minimal def accepted and a
1373
+ nonsense key refused as controls in the same run.
1374
+
1375
+ The legacy legs are kept rather than retired. A per-site producer sweep found no in-repo
1376
+ producer of any of the three snake spellings and zero key-position occurrences in the
1377
+ producer repo (control lit), but two producers lie outside what that sweep measures: a
1378
+ document stored before the key was tightened, since the serve path runs no parse, and a
1379
+ host `DataSource` that never passes through the adapter's key canonicalisation. Dropping a
1380
+ leg would be a silent regression for already-authored data.
1381
+
1382
+ Two keys deliberately gain nothing. `idField` has no `FieldSchema` spelling in either
1383
+ casing, and `titleFormat` is an object-level key whose canonical target is deprecated in
1384
+ favour of `nameField`; reading either camelCase spelling would fossilise a key no contract
1385
+ declares, so both keep their existing snake-only reads and their absence is now pinned.
1386
+ - c6198c2: **Breaking for authored metadata:** `ComponentInput.label`, `ComponentInput.defaultValue` and
1387
+ `ComponentInput.advanced` are RETIRED on both faces (objectui#7493 item ① and objectui#7781;
1388
+ maintainer ruling A of 2026-09-06, immediate, no deprecation window; ADR-0049 enforce-or-remove).
1389
+ They are the three keys the manifest serializer does not forward, and nothing read them on any
1390
+ publication or consumption path.
1391
+
1392
+ No manifest ever published them, so no consumer could ever have read them. `sdui-parser`'s
1393
+ serializer (`packages/sdui-parser/src/index.ts`) forwards exactly seven keys per input — `name`,
1394
+ `type`, `of`, `required`, `enum`, `binding`, `description` — so a value authored under any of the three
1395
+ never reached `sdui.manifest.json`, the generated JSX `.d.ts`, or a diagnostic; its boundary type
1396
+ has no slot for them; the registry's data-source seam reads `name` only; and neither the designer
1397
+ nor the app-shell inspectors consult registry `inputs` at all. A structural census over every
1398
+ `inputs:` array in the repository (re-measured on this change's merge-base, `name` 951 and `type`
1399
+ 951 as the controls) counted the writes: `label` 908, `defaultValue` 245, `advanced` 9 — written on
1400
+ nearly every registration, read by nothing.
1401
+
1402
+ FROM → TO, per key — all three **TOMBSTONED, not removed**, because the route was measured on
1403
+ the built face before it was chosen: `ComponentInputSchema` is a non-strict `z.object`, and an
1404
+ undeclared key parses GREEN and is silently STRIPPED, so a deletion would have swallowed 1,162
1405
+ authored values in silence. The tombstone is what makes the refusal loud and by name.
1406
+
1407
+ - `label?: string` → `label?: never` on the interface, `retirementTombstone()` on the Zod mirror.
1408
+ Migration: delete the key. An input is identified by its `name` on every path that reaches it;
1409
+ nothing ever rendered a label for it.
1410
+ - `defaultValue?: any` → `defaultValue?: never` / `retirementTombstone()`. Migration: delete the
1411
+ key. The renderer's own fallback read IS the default; tell the author about it in `description`,
1412
+ which IS published. (Tightening the type to `unknown` was ruled out: it closes no error class,
1413
+ since nothing reads the value.)
1414
+ - `advanced?: boolean` → `advanced?: never` / `retirementTombstone()`. Migration: delete the key.
1415
+ No designer surface ever hid an "advanced" input; there is nothing to write instead.
1416
+
1417
+ The retirement kit: `?: never` on `ComponentInput` (`packages/types/src/base.ts`), so authoring one
1418
+ is a `tsc` error at the registration site; `retirementTombstone()` on `ComponentInputSchema`
1419
+ (`packages/types/src/zod/base.zod.ts`), so an authored value is REFUSED at parse time with
1420
+ `code: 'invalid_type'`, the key named in the issue `path`, and the migration note as the message
1421
+ (one string, both channels). Pinned in
1422
+ `packages/types/src/__tests__/component-input-retired-keys-7493.test.ts`, which also holds a
1423
+ tree-scoped absence census over every `inputs:` array under `packages/**` and `apps/**`.
1424
+
1425
+ Accept-set change, stated plainly for reviewers: a document that sets any of the three keys on a
1426
+ `ComponentInput` used to parse GREEN (the value was then dropped by the serializer) and now parses
1427
+ RED. Every in-repo authoring site — 1,199 keys across 110 registration files, the three standalone
1428
+ `ComponentInput[]` arrays and the two named input arrays `tsc` found included — is deleted in the same change, as the ruling's split rule
1429
+ requires; the `WidgetRegistry` seam no longer copies the widget-manifest values onto the synthesized
1430
+ `ComponentInput` (they fed nothing), and the data-source declaration `ELEMENT_DATA_SOURCE_INPUT`
1431
+ drops its `label`. The patch entries on the other packages record exactly that: their registrations
1432
+ stop authoring inert keys, with no runtime or published-manifest change.
1433
+
1434
+ The nine test files that read `defaultValue` off a registration were re-pinned against the
1435
+ renderer's ACTUAL default (its own fallback read, or the `defaultProps` it ships) instead of the
1436
+ declaration that went away; two assertions that only restated the shadow default were dropped with
1437
+ the reason on the line.
1438
+
1439
+ The in-repo zero is what was measured. Whether anything OUTSIDE this repository writes these keys
1440
+ is not measurable from here (the objectui#5674 limit); converting such a write from a silent drop
1441
+ into a named refusal is exactly what the tombstones buy. `WidgetInput`'s own `label` /
1442
+ `defaultValue` / `advanced` (the widget-manifest face) stay declared and writable — nothing has
1443
+ ruled on that face; that it now has no reader either is recorded as objectui#7911.
1444
+ - 1fbf63f: Honour a radar series' declared presentation keys (objectui#8157).
1445
+
1446
+ On `chartType: 'radar'` the mark rendered a bare `fillOpacity={0.6}` and read nothing
1447
+ off the series, so three things `@objectstack/spec` declares for every `ChartSeries`
1448
+ were inert at once — `opacity` and `dashArray` (both read by `normalizeSeries` and then
1449
+ discarded) and the whole `variant: 'comparison'` treatment, which is declared and
1450
+ documented with no family restriction. A radar series carrying any of the three drew
1451
+ identically to one carrying none, while every cartesian mark in the same file resolved
1452
+ its presentation through the shared `seriesStyle` helper.
1453
+
1454
+ The radar arm now calls that helper like the cartesian arms do:
1455
+
1456
+ - an authored `opacity` sets both the polygon's fill and its stroke opacity;
1457
+ - an authored `dashArray` dashes the polygon outline — radar is a stroked mark, so
1458
+ unlike a bar or a scatter this is a family where a dash actually paints;
1459
+ - `variant: 'comparison'` mutes a radar overlay, taking the `area` family's defaults
1460
+ (radar being the other mark this renderer both strokes and fills) rather than any
1461
+ newly invented numbers.
1462
+
1463
+ **The default face does not move.** `0.6` was a default as much as a bug — a radar fill
1464
+ needs some opacity or an overlay is unreadable — so the literal became the fallback
1465
+ (`fillOpacity={pres.fillOpacity ?? 0.6}`) instead of being deleted. A radar series that
1466
+ declares none of the three keys paints exactly what it painted before: `fill-opacity`
1467
+ `0.6`, no stroke fade, no dash. Already-drawn radar charts that author nothing are
1468
+ untouched; only a chart that authors one of the keys changes, which is the repair.
1469
+
1470
+ No cartesian arm changed. The helper gained `'radar'` in its `kind` union and an
1471
+ `areaLike` binding that is exactly `kind === 'area'` for every cartesian kind.
1472
+ - fc32921: Fix a dashboard chart widget with a FIELDLESS `count` aggregate plotting nothing
1473
+ (objectui#8266).
1474
+
1475
+ A widget bound to an object with `aggregate: { function: 'count', groupBy: 'status' }`
1476
+ and no `field` — the normal way to author "how many records per status" — rendered an
1477
+ empty chart. No error, no empty state: a plot frame with the category ticks drawn and
1478
+ not one mark in it, which reads exactly like "this object has no rows yet".
1479
+
1480
+ **Cause.** The two dashboard relays (`DashboardGridLayout`, `DashboardRenderer`) each
1481
+ built the series binding as `aggregate?.field || (options.yField || 'value')`, which for
1482
+ a fieldless count resolves to `'value'`. The rows an object-bound fieldless count
1483
+ returns are keyed `'count'` — the alias the engine projects `COUNT(*)` under, pinned
1484
+ since framework#3701. A `dataKey` naming a column no row carries plots nothing, and
1485
+ neither of the renderer's two guards fires on it: the rows DO carry the category key,
1486
+ and the series array is not empty.
1487
+
1488
+ **Fix.** `chartMeasureKey` is a new `@object-ui/core` export delegating to
1489
+ `chartAggregateValueKey` in `@objectstack/spec/ui` — the contract's own derivation of
1490
+ "the value column an object-bound aggregate produces". Both relays now consult it, and
1491
+ the row-projection side (`aggregateValueKey` in `@object-ui/plugin-charts`) is routed
1492
+ through the same function, so the two halves of the question cannot drift again.
1493
+
1494
+ **What moves on screen.** A chart that was blank now draws. Charts that already drew are
1495
+ unaffected: a field-bearing aggregate resolves to its raw field under both the old and
1496
+ the new reading, and a chart with no `aggregate` at all keeps the author's `yField`.
1497
+ One authored key changes meaning: a `yField` written on an object-bound chart that
1498
+ ALSO declares an aggregate no longer wins over the aggregate's own column — it named a
1499
+ record column that a grouped aggregate never returns, so it plotted nothing before.
1500
+
1501
+ **Not fixed here, and out of scope.** The same widget with no `options.xField` is
1502
+ refused by the category-axis guard naming `name`, a key the author never wrote (they
1503
+ wrote `aggregate.groupBy`). That is the category half of the same relay gap and is
1504
+ filed separately.
1505
+ - 8f9d87a: Fix a dashboard chart widget that declares its category as `aggregate.groupBy` being
1506
+ refused for lacking a `name` column (objectui#8269).
1507
+
1508
+ A widget bound to an object with `aggregate: { function: 'count', groupBy: 'status' }`
1509
+ and no `options.xField` rendered a refusal instead of a chart:
1510
+
1511
+ > This chart cannot plot its category axis: no row has a `name` field.
1512
+
1513
+ The author wrote `groupBy: 'status'`. Nothing on screen said `groupBy` was the key that
1514
+ had been ignored, and `name` appeared nowhere in their metadata — so the diagnostic sent
1515
+ them to debug the wrong layer.
1516
+
1517
+ **Cause.** The two dashboard relays (`DashboardGridLayout`, `DashboardRenderer`) each
1518
+ floored the category binding on a literal — `options.xField || 'name'` — and handed it to
1519
+ the `object-chart` node without ever consulting the aggregate that decides it. An
1520
+ object-bound aggregate returns one row per group keyed by the raw `groupBy` field, so no
1521
+ row carried `name` and the category-axis guard (framework#4033) fired correctly on a
1522
+ binding that was already wrong when it arrived.
1523
+
1524
+ **Fix.** `chartCategoryKey` is a new `@object-ui/core` export delegating to
1525
+ `chartAggregateCategoryKey` in `@objectstack/spec/ui` — the contract's own derivation of
1526
+ "the category column an object-bound aggregate produces", and the published sibling of the
1527
+ `chartAggregateValueKey` that objectui#8266 adopted for the measure axis. Both relays now
1528
+ consult it for the object-provider branch.
1529
+
1530
+ **What moves on screen.** A widget that rendered a refusal now draws. Measured through
1531
+ `ChartRenderer` at 480x320 over the rows a fieldless count returns
1532
+ (`[{status:'open',count:2},{status:'paid',count:5}]`): the composed binding went from
1533
+ `xAxisKey: 'name'` — a `missing-category-key` refusal, 0 marks — to `xAxisKey: 'status'`,
1534
+ 1 series and 2 marks with the category ticks drawn.
1535
+
1536
+ **Unaffected.** A chart with no `aggregate` at all keeps the author's `xField` (its rows
1537
+ are raw records, so that key is the right one), an UNGROUPED aggregate keeps it too (it
1538
+ returns a single row with no category column), and the authored-literal-rows branch — the
1539
+ `chart` node composed after the object-provider check fails — keeps its floor unchanged.
1540
+ One authored key changes meaning, exactly as objectui#8266's `yField` did: an `xField`
1541
+ written on an object-bound chart that ALSO declares a `groupBy` no longer wins over the
1542
+ aggregate's own column — it named a record column a grouped aggregate never returns, so it
1543
+ produced the same refusal before.
1544
+ - 681d3f1: Compose an `ObjectChart` drill-down filter instead of spreading it, so the widget's own
1545
+ filter survives into the drilled query for BOTH arms of `ObjectChartSchema.filter`
1546
+ (objectui#8944).
1547
+
1548
+ **The defect.** `ObjectChartSchema.filter` admits a spec `FilterArray`
1549
+ (`[['region','=','emea']]`) and the ObjectQL `$filter` object (`{ region: 'emea' }`),
1550
+ and both are read — both travel verbatim to `ds.aggregate` / `ds.find`. The drill seam
1551
+ composed them by spreading the widget's filter into an object literal, which is correct
1552
+ for the object arm and silent nonsense for the array arm: spreading an array yields
1553
+ index keys, so an authored `FilterArray` drilled as
1554
+ `{ '0': ['region','=','emea'], stage: 'won' }` — the widget's conditions replaced by a
1555
+ key the query layer ignores. Nothing errored; the drawer opened and looked right.
1556
+
1557
+ **Direction of the failure.** The widget's filter is what narrows. Dropping it made the
1558
+ drilled list a **superset** — it showed records the chart itself was scoped to exclude.
1559
+ Not a security boundary, but the worse direction for a silent bug.
1560
+
1561
+ **The composition rule, named rather than picked.** `widget.filter ∧ drill.filter`. The
1562
+ two are independent filter sources and a drill must satisfy both: the click context only
1563
+ says which bucket of the widget's scope was asked for, so it may narrow that scope and
1564
+ never widen it. This is not a new rule — it is the contract `mergeFilterNodes` already
1565
+ states ("combine filter sources under a single `and`, each as its OWN child"), the sink
1566
+ every other multi-source filter in this repo goes through. A new
1567
+ `composeDrillFilter` helper in `@object-ui/core` applies it at the drill seam and
1568
+ documents it, then lowers the result back to the `FilterCondition` object dialect with
1569
+ `parseFilterAST` — the spec's single lowering sink — because that is the dialect both
1570
+ drill sinks take.
1571
+
1572
+ **Compatibility.** A lone surviving source lowers back to exactly the flat object the
1573
+ spread produced, so a chart with no filter of its own drills byte-identically to before.
1574
+ Only a genuinely composed pair gains the `$and`.
1575
+
1576
+ `serializeDrillFilterParams` (the drill "Open in list" / `target: 'navigate'` URL writer)
1577
+ learns to flatten that `$and` into the flat `filter[...]` params its own read side already
1578
+ ANDs back together. Without that it took the `String(value)` path — `$and` holds an array —
1579
+ and emitted `filter[$and]=[object Object],[object Object]` while both real conditions
1580
+ vanished, which is the outcome that function's contract says it never produces.
1581
+ - 6223a9d: fix(plugin-charts): a scatter chart's grid now honours the spec axis `showGridLines`
1582
+
1583
+ The scatter branch drew its grid from a hard-coded `vertical={false}` instead
1584
+ of the grid config every other cartesian family derives from the spec axes, so
1585
+ `showGridLines` declared on either scatter axis was accepted and then dropped.
1586
+ It now uses that same derivation: `yAxis.showGridLines: false` removes the
1587
+ horizontal lines and `xAxis.showGridLines: true` adds vertical ones.
1588
+
1589
+ A scatter whose axes declare no `showGridLines` renders exactly as before
1590
+ (horizontal lines only).
1591
+ - a78cd37: Dates and numbers across the console and the plugins format in the session's
1592
+ display locale instead of the machine's (objectui#9909).
1593
+
1594
+ Every one of these faces handed `Intl` — directly, or through a formatter called
1595
+ without one — either no locale tag or an explicit `undefined`, which is not "the
1596
+ user's locale": it is the locale of the machine the browser runs on, which is
1597
+ neither of this repository's two locale channels.
1598
+ A German or Spanish session therefore read, beside a translated label, a date or
1599
+ an amount grouped and decimal-marked the machine's way — and an amount with
1600
+ inverted separators does not read as unformatted, it reads as a different
1601
+ number. They now go through `useDisplayLocale()` (tenant regional default, then
1602
+ the active UI language, then `'en'`), and a plain helper takes that tag from its
1603
+ caller. The surfaces:
1604
+
1605
+ - **`@object-ui/fields`** — `GridField`'s numeric and currency cells (list mode
1606
+ and computed columns) and both total cells. The same cell helper's date branch
1607
+ already used the display locale, so a single grid row read two conventions at
1608
+ once.
1609
+ - **`@object-ui/plugin-charts`** — ISO-date x-axis ticks, compact y-axis ticks,
1610
+ the single-value face, spec `format` strings and the tooltip value.
1611
+ - **`@object-ui/app-shell`** — the organization invitations, members and
1612
+ accept-invitation dates; marketplace version dates; the AI conversation row's
1613
+ older-than-a-week date and its tooltip; the build-debug token count; the
1614
+ record approvals timeline; and the metadata-admin audit, history, external
1615
+ datasource snapshot, schema-browser row estimate, flow-run start and job
1616
+ next-fire faces. The audit, history and flow-run panels format these in the
1617
+ DISPLAY locale, not in the `locale` prop they take for their UI strings
1618
+ (`AuditPanel` defaults that prop to `'en-US'`; `HistoryPanel` and
1619
+ `FlowRunsPanel` leave it optional).
1620
+ - **`@object-ui/console`** — the approvals inbox's timestamp tooltips, amounts
1621
+ and payload summary; the audit log's timestamp column; the flow runs table and
1622
+ its run detail.
1623
+ - **`@object-ui/components`** — the export dialog's record counts and the debug
1624
+ panel's event times.
1625
+ - **`@object-ui/plugin-form`** — the analytics submission count, the
1626
+ master-detail subtotal / tax / total stack and the edit-conflict dialog's
1627
+ "their save" time.
1628
+ - **`@object-ui/plugin-chatbot`** — the approvals inbox's past-30-days date and
1629
+ the times stamped on local-mode chat messages (the user's and the
1630
+ auto-response).
1631
+ - **`@object-ui/plugin-dashboard`** — the record-count badge, and the currency
1632
+ and date-format branches of `renderFieldValue`, which was handed the display
1633
+ locale and spent it only on its percent branch.
1634
+ - **`@object-ui/plugin-grid`** — the record-detail panel's inferred currency
1635
+ value and the mobile card's amount line, siblings of date cells already on the
1636
+ display locale.
1637
+ - **`@object-ui/plugin-designer`** — `VersionHistory`'s version times.
1638
+
1639
+ A host that mounts one of the components that newly read the display locale with
1640
+ NO `I18nProvider` now gets react-i18next's once-per-module `NO_I18NEXT_INSTANCE`
1641
+ notice in the console on the first such mount; the faces still render, in the
1642
+ channel's `'en'` last resort, and mounting an `I18nProvider` (as the console
1643
+ does) avoids the notice.
1644
+
1645
+ **Additive API** (nothing that compiled before stops compiling):
1646
+
1647
+ - `@object-ui/types`: `ValidationContext` gains an optional `locale`. At this
1648
+ change, the `@object-ui/core` validation engine prints the `date_min` /
1649
+ `date_max` bound in it; omitted, `Intl` follows the runtime default, as
1650
+ `formatDisplayNumber` already declares for a caller with no locale in hand.
1651
+
1652
+ ⚠️ **Dated note, 2026-09-25 — that engine (`ValidationEngine`) is removed from
1653
+ `@object-ui/core` by a later change; `ValidationContext.locale` stays declared in
1654
+ `@object-ui/types`, and at that change nothing in `@object-ui/core` reads it —
1655
+ objectui#7659.**
1656
+
1657
+ ⚠️ **Dated note, 2026-09-27 — `ValidationContext` itself is removed from
1658
+ `@object-ui/types` by a later change, with the rest of the Phase 3.5
1659
+ validation types it belonged to, so the `locale` member this entry adds no
1660
+ longer exists — objectui#10719.**
1661
+ - `@object-ui/plugin-report`: `exportReport`, `exportAsHTML` and `exportAsPDF`
1662
+ take a trailing optional `locale` for the exported file's "Generated:" time,
1663
+ and `LiveExportOptions` gains an optional `locale` that `exportWithLiveData`
1664
+ forwards. Omitted, the time uses the display channel's own last resort
1665
+ (`'en'`), never the machine's locale. `ReportViewer` passes the session's
1666
+ display locale. The locale is deliberately NOT a `ReportExportConfig` member:
1667
+ that type is authored report metadata, and a display locale belongs to the
1668
+ session.
1669
+
1670
+ **One census, repository-wide.** `plugin-detail`'s machine-locale census pin
1671
+ (objectui#9786) is now a single test, `machineLocaleCensus-9909.test.ts` in
1672
+ `@object-ui/i18n`, covering every workspace package whose manifest depends on
1673
+ `@object-ui/i18n`. It refuses any call site that passes nothing, `undefined`,
1674
+ the `'default'` pseudo-tag (a subtag no locale data answers, so `Intl` resolves
1675
+ it to the machine's locale) or a hard-coded tag, unless the site is declared
1676
+ with its reason — for example the `catch` fallback for a tag `Intl` itself
1677
+ rejected, or an ISO formatter feeding `<input type="date">`. Each declared site
1678
+ is counted exactly, so deleting one without its entry is refused too. The
1679
+ per-package pin it replaces is deleted. The runtime tripwire that observes the
1680
+ argument each locale-taking call (`Intl` constructors, `Date` and `Number`
1681
+ `toLocale*`) actually receives moved into the private `@object-ui/test-support`
1682
+ package, and every surface above is pinned with it: the same data under two
1683
+ declared locales must render differently, NO locale-taking call may receive the
1684
+ machine's locale (nothing, `undefined` or `'default'`), and the surface's own
1685
+ calls must receive the declared tag. A call carrying another declared tag, such
1686
+ as the `'en'` of the session's UI language, is tolerated by design: only the
1687
+ machine's locale is the defect.
1688
+ - 894d103: `ObjectChart`'s wrapper div now carries `h-full`, keeping the height chain intact from a dashboard grid cell's declared height down to the element Recharts measures. Previously the chain died at the plain auto-height wrapper: `height: 100%` on the chart container computed to `auto`, Recharts measured a permanent zero, and only the `CHART_MIN_HEIGHT` floor (`6f017e99c`) kept dashboard charts visible — at a fixed floor height instead of filling the cell (#5451). Under auto-height parents `h-full` resolves to `auto`, so non-dashboard hosts are unchanged.
1689
+ - 5eddeeb: Pie, donut, funnel and treemap now say when rows carry no magnitude they can draw.
1690
+
1691
+ These four families size a mark BY its measure, so a row whose value is zero,
1692
+ negative, `null` or unparseable stays in the data and is given no area. Measured
1693
+ in Chromium across 74 tiles: an all-zero pie put ZERO non-white pixels on the
1694
+ page while its DOM carried 31 descendants and a real `svg`; a treemap handed
1695
+ `40 / null`, `40 / 0` or `40 / -25 / -12` rendered one full-bleed leaf that was
1696
+ byte-identical to a genuinely one-row treemap; and a funnel handed `40` beside a
1697
+ `null` drew no segments at all and labelled the tile with the row that had no
1698
+ value.
1699
+
1700
+ When no row can be sized, these charts now render the file's refusal shell
1701
+ (`no-positive-magnitude`) instead of a blank tile. When only some rows can be
1702
+ sized, the chart still draws and carries a note counting the ones it could not.
1703
+ All-positive charts, charts handed no rows at all, bar charts, and both sankey
1704
+ answers are unchanged.
1705
+ - cef27e2: The value-fallback label prettifier `humanizeLabel` has one implementation instead of two byte-identical copies.
1706
+
1707
+ `humanizeLabel` turns a stored value into a display string when nothing else
1708
+ resolves it — an option with no declared label, an object name, a chart axis
1709
+ member. It existed twice, byte for byte: once in `@object-ui/fields` (read by
1710
+ `plugin-grid`, `plugin-gantt`, `plugin-detail` and by that package's own
1711
+ renderers) and once as a deliberate local copy in `plugin-charts`'
1712
+ `ObjectChart.tsx`, whose comment said it was there "to avoid a dependency on
1713
+ `@object-ui/fields`".
1714
+
1715
+ Two copies of one convention is a live hazard rather than tidiness: one
1716
+ dashboard can hold a chart and a grid over the same stored value, so a change
1717
+ landing on one copy alone would put that value on screen under two spellings at
1718
+ once. The single implementation now lives in `@object-ui/core` — the shared
1719
+ ancestor both packages already depend on, so the dependency the copy existed to
1720
+ avoid is still avoided and no new edge is created, and core takes no React
1721
+ (objectui#4389: core-canonical logic, plugins consume). Both former sites
1722
+ re-export it, so `import { humanizeLabel } from '@object-ui/fields'` keeps
1723
+ working unchanged.
1724
+
1725
+ **Nothing rendered changes.** The surviving implementation is byte-identical to
1726
+ both deleted copies, and each former call site is pinned by identity against the
1727
+ core function — not by a copied output table that someone would have to remember
1728
+ to edit in two places.
1729
+
1730
+ The core module also writes down, for the first time, why this convention stays
1731
+ distinct from `humanizeFieldKey` (the KEY fallback, in `@object-ui/plugin-dashboard`),
1732
+ which additionally splits camelCase:
1733
+
1734
+ ```
1735
+ input humanizeFieldKey humanizeLabel
1736
+ needs_analysis Needs Analysis Needs Analysis
1737
+ NeedsAnalysis Needs Analysis NeedsAnalysis <- differ
1738
+ unitPrice Unit Price UnitPrice <- differ
1739
+ BestCase Best Case BestCase <- differ
1740
+ lost-to-competitor Lost-To-Competitor Lost To Competitor <- differ
1741
+ ```
1742
+
1743
+ A field KEY is authored in the codebase and carries a machine spelling, so
1744
+ splitting camelCase recovers words its author meant. A stored VALUE is arbitrary
1745
+ tenant data, where a mid-token capital is not reliably a word boundary and
1746
+ splitting it rewrites what the tenant wrote (`McDonald` to `Mc Donald`). The two
1747
+ conventions also do not nest — on the last row each leaves alone the separator
1748
+ the other rewrites. Whether they should ever converge is a separate decision
1749
+ that would move rendered output in four packages at once; it is deliberately not
1750
+ made here.
1751
+ - d6fe1e1: Draw every categorical x-axis label on short axes
1752
+
1753
+ A vertical bar chart in a dashboard-width widget dropped most of its x-axis
1754
+ labels — three bars drew one label, five bars drew two — leaving the bars
1755
+ unnamed, with no legend to fall back on because a single-series bar chart has
1756
+ none.
1757
+
1758
+ The x axis applied one tick policy to time and category alike (`preserveStartEnd`
1759
+ with a 48px `minTickGap`), which is right for hundreds of dates and wrong for a
1760
+ band axis, where a dropped tick is an identity the reader cannot recover rather
1761
+ than a sample they can interpolate. It was also keyed to the viewport rather
1762
+ than the widget, so a 200px chart inside an 800px console was treated as a wide
1763
+ one.
1764
+
1765
+ Bar, column, line, area and combo charts now draw every label on a categorical
1766
+ x axis of five buckets or fewer — rotating, and ellipsising an over-long name
1767
+ rather than clipping it. Longer axes keep the existing measured thinning, and
1768
+ horizontal bars are unchanged.
1769
+ - fb336df: Move `lucide-react` from `^1.31.0` to `^1.43.0` in every package that declares it, and
1770
+ repair what the jump breaks, so icons resolved from a STRING keep drawing a glyph.
1771
+
1772
+ Measured once against the installed 1.43.0 artifact when this change was made; nothing in
1773
+ the repository re-derives these readings. Across the jump lucide removes no runtime export,
1774
+ no public type name and no `lucide-react/dynamic.mjs` name, and every name this repository
1775
+ imports from `lucide-react` resolves. Exactly one key leaves the runtime `icons` record:
1776
+ `Trash2`, retired in favour of `trash`.
1777
+
1778
+ What a user sees change:
1779
+
1780
+ - Icons the lazy icon seam draws (`resolveIcon`, objectui#9251) get their path data again.
1781
+ lucide 1.43.0 icon modules export their path data inside `__iconData` and no longer
1782
+ export `__iconNode`; the seam now reads both. Reading only `__iconNode` against 1.43.0
1783
+ leaves every such glyph an empty box, with nothing thrown or logged.
1784
+ - `DetailView`'s delete action and three schema-catalog examples spell their icon
1785
+ `trash`, not `trash-2`, so they keep resolving. The glyph is unchanged: 1.43.0's `trash`
1786
+ path data is byte-identical to the `trash-2` path data of 1.31.0 and 1.35.0. Anything
1787
+ that already authored `trash` now draws that same artwork, because lucide moved it under
1788
+ the `trash` name; a few other glyphs were also redrawn upstream.
1789
+ - Icons imported as COMPONENTS carry lucide 1.43.0's own classes: one class per declared
1790
+ alias (a spinner renders `class="lucide lucide-loader-circle lucide-loader-2 ..."`), and
1791
+ no longer the class lucide used to derive from the PascalCase key where that differs
1792
+ (`ArrowDown01` no longer carries `lucide-arrow-down01`). The canonical
1793
+ `lucide-<icon name>` class is still there, so a `.lucide-loader-circle` selector still
1794
+ matches.
1795
+ - Icons resolved from a STRING through the seam keep the classes they had: `lucide`, the
1796
+ canonical `lucide-<icon name>` class and the key-derived class. They do not carry
1797
+ lucide's per-alias classes. That is a maintainer ruling (objectui#8941, option C): the
1798
+ alias list lives only inside each lazily loaded icon module, and it was not moved into
1799
+ the eager name list. A selector that targets an alias class matches a component-imported
1800
+ icon and not a string-resolved one. This also dates the example in the objectui#9251
1801
+ entry: `trash-2` no longer resolves as a string, so no seam-drawn glyph carries
1802
+ `lucide-trash2 lucide-trash-2`; a digit-bearing name that still resolves, such as
1803
+ `arrow-down-0-1`, carries `lucide-arrow-down01 lucide-arrow-down-0-1`.
1804
+ - 6c5ee71: `ObjectChart` now depends on the `fieldOptionLabel` resolver directly instead of
1805
+ holding it behind a ref, so a chart re-resolves its groupBy option labels when
1806
+ the resolver genuinely changes (objectui#5587).
1807
+
1808
+ The ref existed for a reason that no longer holds. `useSafeFieldLabel()` returned
1809
+ a fresh object on every render outside an i18next provider, so a direct
1810
+ dependency made `fetchData`'s `useCallback` identity fresh on every render, and
1811
+ the effect that depends on `fetchData` refetched on every render — an unbounded
1812
+ loop. `ObjectChart` worked around that locally with `fieldOptionLabelRef` plus a
1813
+ `useEffect` keeping it current. `useObjectLabel`'s memo now holds with or without
1814
+ an i18next instance bound (objectui#5564), so the resolver's identity is stable
1815
+ on both paths and the indirection buys nothing.
1816
+
1817
+ It did cost something, and that is the user-visible half: a ref-hidden dependency
1818
+ meant `fetchData` did NOT re-run when the resolver changed. A chart mounted
1819
+ before its `I18nProvider`, or rendered across a language switch, kept serving
1820
+ groupBy labels resolved by the old resolver until some unrelated dependency
1821
+ (object name, filter, aggregate) happened to move. It now refetches once on that
1822
+ transition and shows labels in the active language.
1823
+
1824
+ Pinned by `ObjectChart.fieldOptionLabelRefetch.test.tsx`, which counts fetches
1825
+ across forced re-renders both outside and inside a provider. Reverting
1826
+ `useObjectLabel.ts` to its pre-objectui#5564 state turns the no-provider case red
1827
+ (2 fetches instead of 1, alongside React's "Maximum update depth exceeded"), so
1828
+ the removal is pinned to the fix that unlocked it rather than to a comment.
1829
+ - 93bbc20: Scatter now says when it cannot place a row, instead of drawing an empty axis.
1830
+
1831
+ Scatter is the only two-measure positional chart in the renderer: `xAxisKey` feeds
1832
+ a numeric X axis and `series[0]` a numeric Y axis, so a point exists only when
1833
+ both are numbers. Measured in real Chromium, rows it could not place produced a
1834
+ tile byte-identical to a scatter handed no rows at all, and six different
1835
+ authoring failures shared one image. A chart with one placeable row among three
1836
+ was 99.75% pixel-identical to a genuinely one-row scatter.
1837
+
1838
+ Handed rows it cannot place any of, a scatter now renders the file's refusal
1839
+ shell under `data-chart-error="no-plottable-points"`, naming both keys. When some
1840
+ rows place and some do not it draws as before with a `data-chart-note="unplotted-points"`
1841
+ footnote carrying the count. Charts whose rows all place are byte-identical to
1842
+ before, and no wrapper element is added to them.
1843
+
1844
+ The predicate is positional, not magnitude-based: zero and negative coordinates
1845
+ are ordinary scatter data and keep drawing.
1846
+ - dd35800: `ObjectChart` now renders a self-describing empty state when its query succeeds
1847
+ and returns no rows, instead of falling through to a bare chart frame.
1848
+
1849
+ The frame was measured in a browser rather than assumed: recharts derives its
1850
+ ticks from the data, so with an empty result the bar and line families emit two
1851
+ hairline axis rules and no `text` nodes at all, and pie/donut emit nothing —
1852
+ there are no labelled axes to tell the reader what would have been plotted.
1853
+ Beside the component's own red "Failed to load chart data" box, a blank tile
1854
+ gives the reader nothing to distinguish a young chart from a broken one.
1855
+
1856
+ The copy is the one `plugin-dashboard` already shows on the dataset-bound path
1857
+ ("No data yet" / the load succeeded / the source name), so the same chart over
1858
+ the same empty result no longer reads two different ways depending on which
1859
+ widget drew it. Charts with inline authored data are unchanged — they ran no
1860
+ query to report on.
1861
+ - Updated dependencies [7b10bef]
1862
+ - Updated dependencies [97abedc]
1863
+ - Updated dependencies [b46c58f]
1864
+ - Updated dependencies [a507334]
1865
+ - Updated dependencies [ad694ac]
1866
+ - Updated dependencies [6f96fca]
1867
+ - Updated dependencies [afb2284]
1868
+ - Updated dependencies [3ac2de8]
1869
+ - Updated dependencies [0aacecc]
1870
+ - Updated dependencies [63f4f92]
1871
+ - Updated dependencies [777fca2]
1872
+ - Updated dependencies [c131d9e]
1873
+ - Updated dependencies [5f00ff4]
1874
+ - Updated dependencies [c9e073a]
1875
+ - Updated dependencies [7b395d8]
1876
+ - Updated dependencies [0879812]
1877
+ - Updated dependencies [8cedb0d]
1878
+ - Updated dependencies [162621b]
1879
+ - Updated dependencies [4c6f549]
1880
+ - Updated dependencies [6cc910b]
1881
+ - Updated dependencies [061f5e8]
1882
+ - Updated dependencies [2dd4d3f]
1883
+ - Updated dependencies [e686f4d]
1884
+ - Updated dependencies [4ab4f1b]
1885
+ - Updated dependencies [b57107d]
1886
+ - Updated dependencies [e3ea4f9]
1887
+ - Updated dependencies [bb5d4ee]
1888
+ - Updated dependencies [dc666f7]
1889
+ - Updated dependencies [3335767]
1890
+ - Updated dependencies [8b1f066]
1891
+ - Updated dependencies [af243c1]
1892
+ - Updated dependencies [cff4b77]
1893
+ - Updated dependencies [961ceaa]
1894
+ - Updated dependencies [f3f4e4c]
1895
+ - Updated dependencies [a05c350]
1896
+ - Updated dependencies [1dbb993]
1897
+ - Updated dependencies [2b5f509]
1898
+ - Updated dependencies [808f339]
1899
+ - Updated dependencies [6cf5999]
1900
+ - Updated dependencies [274e14a]
1901
+ - Updated dependencies [8c10f4f]
1902
+ - Updated dependencies [90dac98]
1903
+ - Updated dependencies [6096f20]
1904
+ - Updated dependencies [544aca2]
1905
+ - Updated dependencies [ea02938]
1906
+ - Updated dependencies [a14fb23]
1907
+ - Updated dependencies [ae98f1d]
1908
+ - Updated dependencies [f98eddf]
1909
+ - Updated dependencies [ce6bd99]
1910
+ - Updated dependencies [a5b08c9]
1911
+ - Updated dependencies [86982ac]
1912
+ - Updated dependencies [cdefa2a]
1913
+ - Updated dependencies [0961d5e]
1914
+ - Updated dependencies [6276478]
1915
+ - Updated dependencies [5e67837]
1916
+ - Updated dependencies [ff14e29]
1917
+ - Updated dependencies [9b28151]
1918
+ - Updated dependencies [64563a9]
1919
+ - Updated dependencies [1a5003f]
1920
+ - Updated dependencies [09ab32b]
1921
+ - Updated dependencies [8acc51b]
1922
+ - Updated dependencies [ea9d17f]
1923
+ - Updated dependencies [45362a3]
1924
+ - Updated dependencies [2a943bf]
1925
+ - Updated dependencies [93a689d]
1926
+ - Updated dependencies [4357a27]
1927
+ - Updated dependencies [e5f4343]
1928
+ - Updated dependencies [3261e64]
1929
+ - Updated dependencies [f5178a2]
1930
+ - Updated dependencies [2ad3671]
1931
+ - Updated dependencies [8740e86]
1932
+ - Updated dependencies [d22b37b]
1933
+ - Updated dependencies [1daf477]
1934
+ - Updated dependencies [3d614ea]
1935
+ - Updated dependencies [6c2f3c5]
1936
+ - Updated dependencies [fb13e85]
1937
+ - Updated dependencies [c2d8659]
1938
+ - Updated dependencies [e0f8202]
1939
+ - Updated dependencies [ac2d6f1]
1940
+ - Updated dependencies [9fbbb17]
1941
+ - Updated dependencies [c3a26cc]
1942
+ - Updated dependencies [a66e58e]
1943
+ - Updated dependencies [d89492c]
1944
+ - Updated dependencies [9a5f998]
1945
+ - Updated dependencies [9327397]
1946
+ - Updated dependencies [17cc3a3]
1947
+ - Updated dependencies [02e6d36]
1948
+ - Updated dependencies [4758b33]
1949
+ - Updated dependencies [4758b33]
1950
+ - Updated dependencies [f905090]
1951
+ - Updated dependencies [9c78ebe]
1952
+ - Updated dependencies [9c78ebe]
1953
+ - Updated dependencies [12809a5]
1954
+ - Updated dependencies [7afc81d]
1955
+ - Updated dependencies [f9c06ef]
1956
+ - Updated dependencies [5ad3b88]
1957
+ - Updated dependencies [f9d772b]
1958
+ - Updated dependencies [97b6c21]
1959
+ - Updated dependencies [26ca2ad]
1960
+ - Updated dependencies [41ae65b]
1961
+ - Updated dependencies [baac95a]
1962
+ - Updated dependencies [13220af]
1963
+ - Updated dependencies [29b45f6]
1964
+ - Updated dependencies [39b8d51]
1965
+ - Updated dependencies [17b323e]
1966
+ - Updated dependencies [b956e69]
1967
+ - Updated dependencies [33e58d8]
1968
+ - Updated dependencies [256b4c9]
1969
+ - Updated dependencies [c5ec15c]
1970
+ - Updated dependencies [fec3b1a]
1971
+ - Updated dependencies [b8e0941]
1972
+ - Updated dependencies [0c50f18]
1973
+ - Updated dependencies [1dae95a]
1974
+ - Updated dependencies [e32dae1]
1975
+ - Updated dependencies [30b11ad]
1976
+ - Updated dependencies [4aebea0]
1977
+ - Updated dependencies [f976774]
1978
+ - Updated dependencies [3d6badf]
1979
+ - Updated dependencies [25cb364]
1980
+ - Updated dependencies [a60539b]
1981
+ - Updated dependencies [de1b879]
1982
+ - Updated dependencies [c6678b1]
1983
+ - Updated dependencies [0638322]
1984
+ - Updated dependencies [e3782d2]
1985
+ - Updated dependencies [db0beb2]
1986
+ - Updated dependencies [997ce38]
1987
+ - Updated dependencies [ae0b9d3]
1988
+ - Updated dependencies [ad1785c]
1989
+ - Updated dependencies [3b469c8]
1990
+ - Updated dependencies [990a2d6]
1991
+ - Updated dependencies [1c84036]
1992
+ - Updated dependencies [1c84036]
1993
+ - Updated dependencies [6650259]
1994
+ - Updated dependencies [4f8b7f8]
1995
+ - Updated dependencies [9e6619f]
1996
+ - Updated dependencies [f6ae5e2]
1997
+ - Updated dependencies [eb97ce6]
1998
+ - Updated dependencies [7343376]
1999
+ - Updated dependencies [b2683a2]
2000
+ - Updated dependencies [dded788]
2001
+ - Updated dependencies [b45d463]
2002
+ - Updated dependencies [54a7830]
2003
+ - Updated dependencies [f3135a4]
2004
+ - Updated dependencies [b5696d3]
2005
+ - Updated dependencies [3f9d926]
2006
+ - Updated dependencies [e978ed5]
2007
+ - Updated dependencies [6a7f24e]
2008
+ - Updated dependencies [b3c96d6]
2009
+ - Updated dependencies [8d0ca91]
2010
+ - Updated dependencies [c30c8dd]
2011
+ - Updated dependencies [244d516]
2012
+ - Updated dependencies [deca847]
2013
+ - Updated dependencies [ac15833]
2014
+ - Updated dependencies [ac15833]
2015
+ - Updated dependencies [e2dffc9]
2016
+ - Updated dependencies [328abeb]
2017
+ - Updated dependencies [dd0d78f]
2018
+ - Updated dependencies [24d3e65]
2019
+ - Updated dependencies [95a7c8d]
2020
+ - Updated dependencies [e227156]
2021
+ - Updated dependencies [cc4e476]
2022
+ - Updated dependencies [92970c4]
2023
+ - Updated dependencies [d570eaa]
2024
+ - Updated dependencies [4b742f4]
2025
+ - Updated dependencies [42687ba]
2026
+ - Updated dependencies [6cd8f66]
2027
+ - Updated dependencies [24a0f14]
2028
+ - Updated dependencies [797a30f]
2029
+ - Updated dependencies [b4075c0]
2030
+ - Updated dependencies [9b85600]
2031
+ - Updated dependencies [e327c89]
2032
+ - Updated dependencies [2eaf5be]
2033
+ - Updated dependencies [bf7ab35]
2034
+ - Updated dependencies [99878d8]
2035
+ - Updated dependencies [3c13675]
2036
+ - Updated dependencies [63ab761]
2037
+ - Updated dependencies [0eb9f36]
2038
+ - Updated dependencies [ae582b7]
2039
+ - Updated dependencies [51c2949]
2040
+ - Updated dependencies [a4b017e]
2041
+ - Updated dependencies [8732846]
2042
+ - Updated dependencies [559a2e2]
2043
+ - Updated dependencies [db11afd]
2044
+ - Updated dependencies [154075a]
2045
+ - Updated dependencies [582edef]
2046
+ - Updated dependencies [19f484f]
2047
+ - Updated dependencies [0a78a20]
2048
+ - Updated dependencies [615346d]
2049
+ - Updated dependencies [75dcc81]
2050
+ - Updated dependencies [55a12a8]
2051
+ - Updated dependencies [edfcf5a]
2052
+ - Updated dependencies [0a3e540]
2053
+ - Updated dependencies [c021b35]
2054
+ - Updated dependencies [f61dab1]
2055
+ - Updated dependencies [b0a05dd]
2056
+ - Updated dependencies [dd5ff19]
2057
+ - Updated dependencies [54997ff]
2058
+ - Updated dependencies [81f8498]
2059
+ - Updated dependencies [a782fa7]
2060
+ - Updated dependencies [1d6a23d]
2061
+ - Updated dependencies [0645133]
2062
+ - Updated dependencies [76e9df0]
2063
+ - Updated dependencies [0ecaa7d]
2064
+ - Updated dependencies [b24f93a]
2065
+ - Updated dependencies [6158e4c]
2066
+ - Updated dependencies [cff8641]
2067
+ - Updated dependencies [c27b575]
2068
+ - Updated dependencies [84b275c]
2069
+ - Updated dependencies [0e6e76b]
2070
+ - Updated dependencies [bf43afa]
2071
+ - Updated dependencies [385ebc5]
2072
+ - Updated dependencies [858eafb]
2073
+ - Updated dependencies [a8c5509]
2074
+ - Updated dependencies [c2a8d23]
2075
+ - Updated dependencies [0ffc423]
2076
+ - Updated dependencies [3cc4fe5]
2077
+ - Updated dependencies [cd5b19a]
2078
+ - Updated dependencies [cd5b19a]
2079
+ - Updated dependencies [17dc167]
2080
+ - Updated dependencies [20d23be]
2081
+ - Updated dependencies [20d23be]
2082
+ - Updated dependencies [2e3da72]
2083
+ - Updated dependencies [1263e40]
2084
+ - Updated dependencies [e6bc087]
2085
+ - Updated dependencies [9419df1]
2086
+ - Updated dependencies [8bab157]
2087
+ - Updated dependencies [a7557a7]
2088
+ - Updated dependencies [7d074ba]
2089
+ - Updated dependencies [6158e4c]
2090
+ - Updated dependencies [6158e4c]
2091
+ - Updated dependencies [52aad5c]
2092
+ - Updated dependencies [18d1a0a]
2093
+ - Updated dependencies [7fed09d]
2094
+ - Updated dependencies [58da8ae]
2095
+ - Updated dependencies [a8b9889]
2096
+ - Updated dependencies [138ad45]
2097
+ - Updated dependencies [138ad45]
2098
+ - Updated dependencies [5262f7d]
2099
+ - Updated dependencies [6aa029b]
2100
+ - Updated dependencies [2124d04]
2101
+ - Updated dependencies [be52115]
2102
+ - Updated dependencies [770cc5b]
2103
+ - Updated dependencies [1a88ce2]
2104
+ - Updated dependencies [a1a44d6]
2105
+ - Updated dependencies [e0a9c67]
2106
+ - Updated dependencies [5638529]
2107
+ - Updated dependencies [5638529]
2108
+ - Updated dependencies [d0fba91]
2109
+ - Updated dependencies [c476be0]
2110
+ - Updated dependencies [c82ff39]
2111
+ - Updated dependencies [6c3da53]
2112
+ - Updated dependencies [31987bd]
2113
+ - Updated dependencies [3c3ce15]
2114
+ - Updated dependencies [063119f]
2115
+ - Updated dependencies [e100589]
2116
+ - Updated dependencies [304f611]
2117
+ - Updated dependencies [e46ee77]
2118
+ - Updated dependencies [f9c8c4e]
2119
+ - Updated dependencies [6e9c8d2]
2120
+ - Updated dependencies [9547063]
2121
+ - Updated dependencies [3f6efd6]
2122
+ - Updated dependencies [55d18c6]
2123
+ - Updated dependencies [c4ab6d0]
2124
+ - Updated dependencies [0e9058b]
2125
+ - Updated dependencies [c4ab6d0]
2126
+ - Updated dependencies [5988b6b]
2127
+ - Updated dependencies [6158e4c]
2128
+ - Updated dependencies [00ccdf7]
2129
+ - Updated dependencies [9d9ed54]
2130
+ - Updated dependencies [4a1adb7]
2131
+ - Updated dependencies [4a1adb7]
2132
+ - Updated dependencies [50c73fe]
2133
+ - Updated dependencies [ca3de72]
2134
+ - Updated dependencies [83e3f83]
2135
+ - Updated dependencies [401611b]
2136
+ - Updated dependencies [2c0ddf2]
2137
+ - Updated dependencies [4abc0aa]
2138
+ - Updated dependencies [f560ded]
2139
+ - Updated dependencies [2b188fa]
2140
+ - Updated dependencies [b654d4e]
2141
+ - Updated dependencies [f68e0a0]
2142
+ - Updated dependencies [aea682a]
2143
+ - Updated dependencies [fcdc8ec]
2144
+ - Updated dependencies [2d576e4]
2145
+ - Updated dependencies [8366acc]
2146
+ - Updated dependencies [95e58a3]
2147
+ - Updated dependencies [9d7419b]
2148
+ - Updated dependencies [fc7db05]
2149
+ - Updated dependencies [9ed8d0f]
2150
+ - Updated dependencies [c73cdb5]
2151
+ - Updated dependencies [6f5719e]
2152
+ - Updated dependencies [d0097af]
2153
+ - Updated dependencies [432882b]
2154
+ - Updated dependencies [64dae8e]
2155
+ - Updated dependencies [b06e374]
2156
+ - Updated dependencies [06a8af5]
2157
+ - Updated dependencies [6a91586]
2158
+ - Updated dependencies [a04d7c6]
2159
+ - Updated dependencies [5ccc500]
2160
+ - Updated dependencies [f3c2bb0]
2161
+ - Updated dependencies [978507b]
2162
+ - Updated dependencies [778138e]
2163
+ - Updated dependencies [9801765]
2164
+ - Updated dependencies [9cebfca]
2165
+ - Updated dependencies [460575f]
2166
+ - Updated dependencies [d796c8d]
2167
+ - Updated dependencies [594704f]
2168
+ - Updated dependencies [d3995fe]
2169
+ - Updated dependencies [1b1d772]
2170
+ - Updated dependencies [d88e20f]
2171
+ - Updated dependencies [2d7304d]
2172
+ - Updated dependencies [636b236]
2173
+ - Updated dependencies [4172589]
2174
+ - Updated dependencies [d6d8fb9]
2175
+ - Updated dependencies [64d624d]
2176
+ - Updated dependencies [053fdc8]
2177
+ - Updated dependencies [41b7ce3]
2178
+ - Updated dependencies [ae476b8]
2179
+ - Updated dependencies [39f4309]
2180
+ - Updated dependencies [95bad12]
2181
+ - Updated dependencies [d2fb6ef]
2182
+ - Updated dependencies [7cd3987]
2183
+ - Updated dependencies [ee3b878]
2184
+ - Updated dependencies [e304a4e]
2185
+ - Updated dependencies [fda49e5]
2186
+ - Updated dependencies [490d9a9]
2187
+ - Updated dependencies [fc62bb4]
2188
+ - Updated dependencies [41df893]
2189
+ - Updated dependencies [0cba1b7]
2190
+ - Updated dependencies [00f3eb5]
2191
+ - Updated dependencies [1ec291c]
2192
+ - Updated dependencies [453dbaa]
2193
+ - Updated dependencies [95f8704]
2194
+ - Updated dependencies [f8cdbf2]
2195
+ - Updated dependencies [69a2163]
2196
+ - Updated dependencies [24e027e]
2197
+ - Updated dependencies [2c3cd1b]
2198
+ - Updated dependencies [e176053]
2199
+ - Updated dependencies [e30ed15]
2200
+ - Updated dependencies [90665e0]
2201
+ - Updated dependencies [8d3a529]
2202
+ - Updated dependencies [5ac2e2c]
2203
+ - Updated dependencies [194fae1]
2204
+ - Updated dependencies [7e19d03]
2205
+ - Updated dependencies [b08b7eb]
2206
+ - Updated dependencies [1e946c9]
2207
+ - Updated dependencies [546ddf7]
2208
+ - Updated dependencies [864154e]
2209
+ - Updated dependencies [b023625]
2210
+ - Updated dependencies [75bd83d]
2211
+ - Updated dependencies [44d075b]
2212
+ - Updated dependencies [40c479a]
2213
+ - Updated dependencies [971d387]
2214
+ - Updated dependencies [ee851c3]
2215
+ - Updated dependencies [6414dfd]
2216
+ - Updated dependencies [a8d5c71]
2217
+ - Updated dependencies [905b21f]
2218
+ - Updated dependencies [88e9109]
2219
+ - Updated dependencies [2c45966]
2220
+ - Updated dependencies [db3a600]
2221
+ - Updated dependencies [3a3db76]
2222
+ - Updated dependencies [0d723a3]
2223
+ - Updated dependencies [0c95d3d]
2224
+ - Updated dependencies [3e4fa2c]
2225
+ - Updated dependencies [b5b928a]
2226
+ - Updated dependencies [6fd2cf7]
2227
+ - Updated dependencies [5fa06c4]
2228
+ - Updated dependencies [52a43de]
2229
+ - Updated dependencies [195052f]
2230
+ - Updated dependencies [e4559d1]
2231
+ - Updated dependencies [2c71482]
2232
+ - Updated dependencies [129bcc5]
2233
+ - Updated dependencies [a26b9e4]
2234
+ - Updated dependencies [5ef9c4f]
2235
+ - Updated dependencies [46f0bb4]
2236
+ - Updated dependencies [06b82b8]
2237
+ - Updated dependencies [8ec11e1]
2238
+ - Updated dependencies [6f81384]
2239
+ - Updated dependencies [22ba927]
2240
+ - Updated dependencies [f8c70f4]
2241
+ - Updated dependencies [5d3a2d1]
2242
+ - Updated dependencies [8f1d995]
2243
+ - Updated dependencies [b362c1b]
2244
+ - Updated dependencies [f9c34df]
2245
+ - Updated dependencies [dddb942]
2246
+ - Updated dependencies [00c665e]
2247
+ - Updated dependencies [29754cf]
2248
+ - Updated dependencies [d7de534]
2249
+ - Updated dependencies [3c2b6f7]
2250
+ - Updated dependencies [6e88630]
2251
+ - Updated dependencies [b84dc18]
2252
+ - Updated dependencies [ac8abb0]
2253
+ - Updated dependencies [9d86e1d]
2254
+ - Updated dependencies [3a5817f]
2255
+ - Updated dependencies [99a3c2d]
2256
+ - Updated dependencies [5961030]
2257
+ - Updated dependencies [f24de8b]
2258
+ - Updated dependencies [c8ea8af]
2259
+ - Updated dependencies [9602dc8]
2260
+ - Updated dependencies [3190414]
2261
+ - Updated dependencies [4e480f5]
2262
+ - Updated dependencies [38a123c]
2263
+ - Updated dependencies [299102e]
2264
+ - Updated dependencies [30c73cd]
2265
+ - Updated dependencies [830ed58]
2266
+ - Updated dependencies [d7acad6]
2267
+ - Updated dependencies [45a9aeb]
2268
+ - Updated dependencies [713db46]
2269
+ - Updated dependencies [c71e14d]
2270
+ - Updated dependencies [bf3a03c]
2271
+ - Updated dependencies [cb55718]
2272
+ - Updated dependencies [748494b]
2273
+ - Updated dependencies [5967be0]
2274
+ - Updated dependencies [831be72]
2275
+ - Updated dependencies [29cb85b]
2276
+ - Updated dependencies [3e028c8]
2277
+ - Updated dependencies [d0889e2]
2278
+ - Updated dependencies [ce503e5]
2279
+ - Updated dependencies [f20dcf0]
2280
+ - Updated dependencies [12402a9]
2281
+ - Updated dependencies [aff3d7a]
2282
+ - Updated dependencies [4ca30d0]
2283
+ - Updated dependencies [7a5da14]
2284
+ - Updated dependencies [fff9645]
2285
+ - Updated dependencies [9c3b7ce]
2286
+ - Updated dependencies [2c1c967]
2287
+ - Updated dependencies [9486ac6]
2288
+ - Updated dependencies [9486ac6]
2289
+ - Updated dependencies [4d5f9b4]
2290
+ - Updated dependencies [d6ceb8d]
2291
+ - Updated dependencies [dc4365c]
2292
+ - Updated dependencies [e321d52]
2293
+ - Updated dependencies [969ba84]
2294
+ - Updated dependencies [4c68077]
2295
+ - Updated dependencies [7977ff9]
2296
+ - Updated dependencies [3beef6d]
2297
+ - Updated dependencies [06b8c42]
2298
+ - Updated dependencies [46b9bc9]
2299
+ - Updated dependencies [b97790a]
2300
+ - Updated dependencies [dbd5194]
2301
+ - Updated dependencies [7c9b044]
2302
+ - Updated dependencies [d47de51]
2303
+ - Updated dependencies [3fe6463]
2304
+ - Updated dependencies [b392674]
2305
+ - Updated dependencies [4f3a1e2]
2306
+ - Updated dependencies [31ab372]
2307
+ - Updated dependencies [846889b]
2308
+ - Updated dependencies [2acd8e1]
2309
+ - Updated dependencies [26896c6]
2310
+ - Updated dependencies [67fc3b0]
2311
+ - Updated dependencies [33a3b3c]
2312
+ - Updated dependencies [b87f15b]
2313
+ - Updated dependencies [045d20b]
2314
+ - Updated dependencies [a2d2515]
2315
+ - Updated dependencies [c18d099]
2316
+ - Updated dependencies [adb2a86]
2317
+ - Updated dependencies [03380aa]
2318
+ - Updated dependencies [4562ea5]
2319
+ - Updated dependencies [3619792]
2320
+ - Updated dependencies [3561bd2]
2321
+ - Updated dependencies [bf97b98]
2322
+ - Updated dependencies [320374d]
2323
+ - Updated dependencies [b0d308d]
2324
+ - Updated dependencies [40f34b4]
2325
+ - Updated dependencies [8063bcb]
2326
+ - Updated dependencies [b74a859]
2327
+ - Updated dependencies [d4493fd]
2328
+ - Updated dependencies [240b80f]
2329
+ - Updated dependencies [77cb489]
2330
+ - Updated dependencies [bfaa158]
2331
+ - Updated dependencies [777e5c6]
2332
+ - Updated dependencies [0c386dd]
2333
+ - Updated dependencies [9e37d9b]
2334
+ - Updated dependencies [5ad86dd]
2335
+ - Updated dependencies [16a725f]
2336
+ - Updated dependencies [4dfdcc3]
2337
+ - Updated dependencies [6a449fc]
2338
+ - Updated dependencies [446d93d]
2339
+ - Updated dependencies [ecd9cb2]
2340
+ - Updated dependencies [98d4108]
2341
+ - Updated dependencies [0e3b3be]
2342
+ - Updated dependencies [a29ae2d]
2343
+ - Updated dependencies [220c18d]
2344
+ - Updated dependencies [00d3f09]
2345
+ - Updated dependencies [4388f71]
2346
+ - Updated dependencies [0b1ac58]
2347
+ - Updated dependencies [c93b4d5]
2348
+ - Updated dependencies [c1fe272]
2349
+ - Updated dependencies [3cab570]
2350
+ - Updated dependencies [8ad218d]
2351
+ - Updated dependencies [3e41187]
2352
+ - Updated dependencies [5f78953]
2353
+ - Updated dependencies [639114c]
2354
+ - Updated dependencies [639114c]
2355
+ - Updated dependencies [1490691]
2356
+ - Updated dependencies [1f31d3a]
2357
+ - Updated dependencies [d1842ab]
2358
+ - Updated dependencies [78ca238]
2359
+ - Updated dependencies [d8ec8d6]
2360
+ - Updated dependencies [351eb31]
2361
+ - Updated dependencies [866cd1d]
2362
+ - Updated dependencies [20c04b2]
2363
+ - Updated dependencies [01c9023]
2364
+ - Updated dependencies [48c19bd]
2365
+ - Updated dependencies [a6d8b8d]
2366
+ - Updated dependencies [4b5bb95]
2367
+ - Updated dependencies [b652514]
2368
+ - Updated dependencies [adbda1b]
2369
+ - Updated dependencies [adbda1b]
2370
+ - Updated dependencies [8952395]
2371
+ - Updated dependencies [e2b3826]
2372
+ - Updated dependencies [0348bc9]
2373
+ - Updated dependencies [e8c553b]
2374
+ - Updated dependencies [2e32ed4]
2375
+ - Updated dependencies [3ed3eec]
2376
+ - Updated dependencies [7c3df8f]
2377
+ - Updated dependencies [a4514e8]
2378
+ - Updated dependencies [db3896c]
2379
+ - Updated dependencies [b9f5ff1]
2380
+ - Updated dependencies [e75f4c9]
2381
+ - Updated dependencies [19f1639]
2382
+ - Updated dependencies [4704aa4]
2383
+ - Updated dependencies [47547d0]
2384
+ - Updated dependencies [1bee5d0]
2385
+ - Updated dependencies [858cd72]
2386
+ - Updated dependencies [cfc9b6d]
2387
+ - Updated dependencies [554f2b6]
2388
+ - Updated dependencies [72f55c9]
2389
+ - Updated dependencies [26e06d7]
2390
+ - Updated dependencies [669d71b]
2391
+ - Updated dependencies [ed27d7c]
2392
+ - Updated dependencies [52c8cf7]
2393
+ - Updated dependencies [2ceb43a]
2394
+ - Updated dependencies [7cdd2b9]
2395
+ - Updated dependencies [52c8cf7]
2396
+ - Updated dependencies [3399704]
2397
+ - Updated dependencies [71a4a53]
2398
+ - Updated dependencies [7bf244b]
2399
+ - Updated dependencies [f0bb9fa]
2400
+ - Updated dependencies [81a2eb1]
2401
+ - Updated dependencies [caa0cd3]
2402
+ - Updated dependencies [caa0cd3]
2403
+ - Updated dependencies [20cb8db]
2404
+ - Updated dependencies [25c7d58]
2405
+ - Updated dependencies [00d2fa6]
2406
+ - Updated dependencies [77b2a18]
2407
+ - Updated dependencies [c6198c2]
2408
+ - Updated dependencies [721d1e0]
2409
+ - Updated dependencies [c2f0f48]
2410
+ - Updated dependencies [1237ae4]
2411
+ - Updated dependencies [2f61238]
2412
+ - Updated dependencies [51eb515]
2413
+ - Updated dependencies [c354ce5]
2414
+ - Updated dependencies [8fe8e5c]
2415
+ - Updated dependencies [feac439]
2416
+ - Updated dependencies [9ae871d]
2417
+ - Updated dependencies [efbd566]
2418
+ - Updated dependencies [2a5bf45]
2419
+ - Updated dependencies [9587fc9]
2420
+ - Updated dependencies [e62c44e]
2421
+ - Updated dependencies [daf9d57]
2422
+ - Updated dependencies [23b9958]
2423
+ - Updated dependencies [c15d7ec]
2424
+ - Updated dependencies [5d0876c]
2425
+ - Updated dependencies [f7ace0a]
2426
+ - Updated dependencies [b041b9c]
2427
+ - Updated dependencies [ce2aaef]
2428
+ - Updated dependencies [544ecba]
2429
+ - Updated dependencies [2ce2612]
2430
+ - Updated dependencies [bc640ec]
2431
+ - Updated dependencies [1e215c4]
2432
+ - Updated dependencies [da6e191]
2433
+ - Updated dependencies [3e377c9]
2434
+ - Updated dependencies [a3eb5d0]
2435
+ - Updated dependencies [4ce14f1]
2436
+ - Updated dependencies [aef97e5]
2437
+ - Updated dependencies [2af1fa7]
2438
+ - Updated dependencies [c14d3a0]
2439
+ - Updated dependencies [a137d0c]
2440
+ - Updated dependencies [caf477f]
2441
+ - Updated dependencies [f6375da]
2442
+ - Updated dependencies [967e5d8]
2443
+ - Updated dependencies [c907a9c]
2444
+ - Updated dependencies [a4611b3]
2445
+ - Updated dependencies [20316ba]
2446
+ - Updated dependencies [d3499b3]
2447
+ - Updated dependencies [91f9276]
2448
+ - Updated dependencies [309c75e]
2449
+ - Updated dependencies [c9f9bae]
2450
+ - Updated dependencies [18897a4]
2451
+ - Updated dependencies [8b7ea39]
2452
+ - Updated dependencies [a915064]
2453
+ - Updated dependencies [dcbf0b2]
2454
+ - Updated dependencies [52cac38]
2455
+ - Updated dependencies [93fea2e]
2456
+ - Updated dependencies [1422a92]
2457
+ - Updated dependencies [d05fe17]
2458
+ - Updated dependencies [a480f79]
2459
+ - Updated dependencies [f08d1a8]
2460
+ - Updated dependencies [64a252d]
2461
+ - Updated dependencies [786bc91]
2462
+ - Updated dependencies [75fca96]
2463
+ - Updated dependencies [7ca6ddd]
2464
+ - Updated dependencies [f1cd290]
2465
+ - Updated dependencies [5a41ce7]
2466
+ - Updated dependencies [8d50bc2]
2467
+ - Updated dependencies [604476d]
2468
+ - Updated dependencies [d1bebb0]
2469
+ - Updated dependencies [95bf128]
2470
+ - Updated dependencies [335abea]
2471
+ - Updated dependencies [edea22a]
2472
+ - Updated dependencies [0f5cadf]
2473
+ - Updated dependencies [4f9f1ee]
2474
+ - Updated dependencies [66e8b2a]
2475
+ - Updated dependencies [aa083cd]
2476
+ - Updated dependencies [12b5992]
2477
+ - Updated dependencies [b93e245]
2478
+ - Updated dependencies [c842594]
2479
+ - Updated dependencies [290de37]
2480
+ - Updated dependencies [e1c27e4]
2481
+ - Updated dependencies [8c8da45]
2482
+ - Updated dependencies [8cd8eb5]
2483
+ - Updated dependencies [cf1d29e]
2484
+ - Updated dependencies [1bd79c8]
2485
+ - Updated dependencies [af9e957]
2486
+ - Updated dependencies [b1030c7]
2487
+ - Updated dependencies [c974edf]
2488
+ - Updated dependencies [ad852b6]
2489
+ - Updated dependencies [6778809]
2490
+ - Updated dependencies [7fb22a1]
2491
+ - Updated dependencies [ad66d79]
2492
+ - Updated dependencies [0758bd8]
2493
+ - Updated dependencies [ee4d19f]
2494
+ - Updated dependencies [496d31d]
2495
+ - Updated dependencies [0ea7054]
2496
+ - Updated dependencies [9a853f2]
2497
+ - Updated dependencies [cb847fd]
2498
+ - Updated dependencies [ee70287]
2499
+ - Updated dependencies [3e98e13]
2500
+ - Updated dependencies [a695f50]
2501
+ - Updated dependencies [fc32921]
2502
+ - Updated dependencies [4eaa835]
2503
+ - Updated dependencies [8f9d87a]
2504
+ - Updated dependencies [b1777ae]
2505
+ - Updated dependencies [24845c4]
2506
+ - Updated dependencies [6f864cf]
2507
+ - Updated dependencies [24d1edd]
2508
+ - Updated dependencies [645087c]
2509
+ - Updated dependencies [33f4a19]
2510
+ - Updated dependencies [6e9a3d4]
2511
+ - Updated dependencies [4a292d2]
2512
+ - Updated dependencies [5323168]
2513
+ - Updated dependencies [841dd2b]
2514
+ - Updated dependencies [3014fc0]
2515
+ - Updated dependencies [dacb402]
2516
+ - Updated dependencies [846cec0]
2517
+ - Updated dependencies [91facae]
2518
+ - Updated dependencies [b38014e]
2519
+ - Updated dependencies [474797d]
2520
+ - Updated dependencies [704e695]
2521
+ - Updated dependencies [a407bd6]
2522
+ - Updated dependencies [317dbce]
2523
+ - Updated dependencies [309728c]
2524
+ - Updated dependencies [aa08d7e]
2525
+ - Updated dependencies [3a43a15]
2526
+ - Updated dependencies [868e825]
2527
+ - Updated dependencies [f76f436]
2528
+ - Updated dependencies [ce45a03]
2529
+ - Updated dependencies [421544b]
2530
+ - Updated dependencies [fb01022]
2531
+ - Updated dependencies [e9d9212]
2532
+ - Updated dependencies [ecfb693]
2533
+ - Updated dependencies [abc1b18]
2534
+ - Updated dependencies [81a51db]
2535
+ - Updated dependencies [67749c7]
2536
+ - Updated dependencies [507b61b]
2537
+ - Updated dependencies [512c84b]
2538
+ - Updated dependencies [c300267]
2539
+ - Updated dependencies [fb3a101]
2540
+ - Updated dependencies [d4733f2]
2541
+ - Updated dependencies [7c9145f]
2542
+ - Updated dependencies [1570eac]
2543
+ - Updated dependencies [f391ede]
2544
+ - Updated dependencies [f5cfbbd]
2545
+ - Updated dependencies [f5cfbbd]
2546
+ - Updated dependencies [8b532cb]
2547
+ - Updated dependencies [64c3cdd]
2548
+ - Updated dependencies [4d65991]
2549
+ - Updated dependencies [c42554e]
2550
+ - Updated dependencies [555b4ec]
2551
+ - Updated dependencies [1ccfc23]
2552
+ - Updated dependencies [542718f]
2553
+ - Updated dependencies [7f27bc5]
2554
+ - Updated dependencies [0a174f3]
2555
+ - Updated dependencies [676f677]
2556
+ - Updated dependencies [f95b140]
2557
+ - Updated dependencies [541ce4e]
2558
+ - Updated dependencies [6479086]
2559
+ - Updated dependencies [d79f525]
2560
+ - Updated dependencies [d1865d2]
2561
+ - Updated dependencies [55ba3ff]
2562
+ - Updated dependencies [f1190b0]
2563
+ - Updated dependencies [561abef]
2564
+ - Updated dependencies [ef52001]
2565
+ - Updated dependencies [6a4680b]
2566
+ - Updated dependencies [c3a4273]
2567
+ - Updated dependencies [abf710d]
2568
+ - Updated dependencies [093af32]
2569
+ - Updated dependencies [1bd1be7]
2570
+ - Updated dependencies [d234fa9]
2571
+ - Updated dependencies [adf5812]
2572
+ - Updated dependencies [e36acd4]
2573
+ - Updated dependencies [5058336]
2574
+ - Updated dependencies [2f6b2bf]
2575
+ - Updated dependencies [2028b31]
2576
+ - Updated dependencies [63601ab]
2577
+ - Updated dependencies [c372b29]
2578
+ - Updated dependencies [152f0a7]
2579
+ - Updated dependencies [8693b85]
2580
+ - Updated dependencies [e82dad1]
2581
+ - Updated dependencies [58b7b3d]
2582
+ - Updated dependencies [84defab]
2583
+ - Updated dependencies [681d3f1]
2584
+ - Updated dependencies [969d4f2]
2585
+ - Updated dependencies [f3bc481]
2586
+ - Updated dependencies [b79aac2]
2587
+ - Updated dependencies [93fc0e7]
2588
+ - Updated dependencies [a4b723f]
2589
+ - Updated dependencies [2b10ca0]
2590
+ - Updated dependencies [7db4a81]
2591
+ - Updated dependencies [19a0b0e]
2592
+ - Updated dependencies [526fc11]
2593
+ - Updated dependencies [f8e3e9a]
2594
+ - Updated dependencies [b9d47ec]
2595
+ - Updated dependencies [3b6bc69]
2596
+ - Updated dependencies [6214db6]
2597
+ - Updated dependencies [6732df4]
2598
+ - Updated dependencies [fe9e0d0]
2599
+ - Updated dependencies [63fb72c]
2600
+ - Updated dependencies [804831c]
2601
+ - Updated dependencies [279e48e]
2602
+ - Updated dependencies [8700d6d]
2603
+ - Updated dependencies [8db2a0f]
2604
+ - Updated dependencies [689953a]
2605
+ - Updated dependencies [30443fb]
2606
+ - Updated dependencies [8d3dbb2]
2607
+ - Updated dependencies [efc1c9c]
2608
+ - Updated dependencies [da45e6b]
2609
+ - Updated dependencies [7533465]
2610
+ - Updated dependencies [835f0f3]
2611
+ - Updated dependencies [a9d97be]
2612
+ - Updated dependencies [ed35b44]
2613
+ - Updated dependencies [9ba7e9c]
2614
+ - Updated dependencies [729e851]
2615
+ - Updated dependencies [96919a4]
2616
+ - Updated dependencies [345e24a]
2617
+ - Updated dependencies [20b507a]
2618
+ - Updated dependencies [2e471dc]
2619
+ - Updated dependencies [6748587]
2620
+ - Updated dependencies [be50942]
2621
+ - Updated dependencies [775e079]
2622
+ - Updated dependencies [7e8b3c0]
2623
+ - Updated dependencies [53374dc]
2624
+ - Updated dependencies [f6fb83f]
2625
+ - Updated dependencies [2049b03]
2626
+ - Updated dependencies [2bf34f7]
2627
+ - Updated dependencies [15b33ae]
2628
+ - Updated dependencies [7cbc724]
2629
+ - Updated dependencies [7098eed]
2630
+ - Updated dependencies [3df7c5c]
2631
+ - Updated dependencies [fb91ac9]
2632
+ - Updated dependencies [fb91ac9]
2633
+ - Updated dependencies [641fb55]
2634
+ - Updated dependencies [8524372]
2635
+ - Updated dependencies [7cbefa5]
2636
+ - Updated dependencies [72d6587]
2637
+ - Updated dependencies [a272a4f]
2638
+ - Updated dependencies [55f39ee]
2639
+ - Updated dependencies [0ce32d5]
2640
+ - Updated dependencies [0970a0e]
2641
+ - Updated dependencies [e427e9c]
2642
+ - Updated dependencies [bbc9dc3]
2643
+ - Updated dependencies [02f1813]
2644
+ - Updated dependencies [1ef89c0]
2645
+ - Updated dependencies [ba0b61a]
2646
+ - Updated dependencies [ba0b61a]
2647
+ - Updated dependencies [ac716ff]
2648
+ - Updated dependencies [f0f3cd5]
2649
+ - Updated dependencies [ab856ed]
2650
+ - Updated dependencies [20f3e65]
2651
+ - Updated dependencies [bbba098]
2652
+ - Updated dependencies [87af769]
2653
+ - Updated dependencies [3be720e]
2654
+ - Updated dependencies [43c0d17]
2655
+ - Updated dependencies [c3df43a]
2656
+ - Updated dependencies [d16d0e9]
2657
+ - Updated dependencies [bbe57fd]
2658
+ - Updated dependencies [272a530]
2659
+ - Updated dependencies [1779e8d]
2660
+ - Updated dependencies [f7fcc2c]
2661
+ - Updated dependencies [f0f4d6c]
2662
+ - Updated dependencies [4128188]
2663
+ - Updated dependencies [b253c4e]
2664
+ - Updated dependencies [78a9c67]
2665
+ - Updated dependencies [4a7ef0d]
2666
+ - Updated dependencies [4598f6d]
2667
+ - Updated dependencies [dea17b4]
2668
+ - Updated dependencies [89bb77a]
2669
+ - Updated dependencies [06611e4]
2670
+ - Updated dependencies [44152c4]
2671
+ - Updated dependencies [3939545]
2672
+ - Updated dependencies [66abbde]
2673
+ - Updated dependencies [dc3893d]
2674
+ - Updated dependencies [1bbaa16]
2675
+ - Updated dependencies [6ee259a]
2676
+ - Updated dependencies [7649f43]
2677
+ - Updated dependencies [e708426]
2678
+ - Updated dependencies [3b6d53b]
2679
+ - Updated dependencies [276d174]
2680
+ - Updated dependencies [2982ed9]
2681
+ - Updated dependencies [a8198de]
2682
+ - Updated dependencies [0c789a4]
2683
+ - Updated dependencies [05a49f2]
2684
+ - Updated dependencies [b234a84]
2685
+ - Updated dependencies [a78cd37]
2686
+ - Updated dependencies [5ea623e]
2687
+ - Updated dependencies [5eabe86]
2688
+ - Updated dependencies [ca5d671]
2689
+ - Updated dependencies [32bf2d6]
2690
+ - Updated dependencies [ff0c384]
2691
+ - Updated dependencies [af4fb29]
2692
+ - Updated dependencies [c698a81]
2693
+ - Updated dependencies [ff5ef1c]
2694
+ - Updated dependencies [befd40c]
2695
+ - Updated dependencies [9a97800]
2696
+ - Updated dependencies [6bca0e4]
2697
+ - Updated dependencies [81c0bc4]
2698
+ - Updated dependencies [3c76801]
2699
+ - Updated dependencies [60500cb]
2700
+ - Updated dependencies [2fcefb9]
2701
+ - Updated dependencies [77f846a]
2702
+ - Updated dependencies [bc5870c]
2703
+ - Updated dependencies [b55a346]
2704
+ - Updated dependencies [065bba7]
2705
+ - Updated dependencies [f760064]
2706
+ - Updated dependencies [dd19463]
2707
+ - Updated dependencies [6791717]
2708
+ - Updated dependencies [8ea3bee]
2709
+ - Updated dependencies [100547e]
2710
+ - Updated dependencies [3a58149]
2711
+ - Updated dependencies [6d1c155]
2712
+ - Updated dependencies [d7573b3]
2713
+ - Updated dependencies [bf3edfe]
2714
+ - Updated dependencies [2c8474c]
2715
+ - Updated dependencies [6ce89da]
2716
+ - Updated dependencies [0e05aac]
2717
+ - Updated dependencies [ae61ad4]
2718
+ - Updated dependencies [5aed9e4]
2719
+ - Updated dependencies [83c77dc]
2720
+ - Updated dependencies [18a8e7d]
2721
+ - Updated dependencies [e7957ab]
2722
+ - Updated dependencies [f7e34ca]
2723
+ - Updated dependencies [e719ebd]
2724
+ - Updated dependencies [516583b]
2725
+ - Updated dependencies [f9e4f91]
2726
+ - Updated dependencies [6ef48b1]
2727
+ - Updated dependencies [fa429cf]
2728
+ - Updated dependencies [ed8df3e]
2729
+ - Updated dependencies [fe76ece]
2730
+ - Updated dependencies [8b446f5]
2731
+ - Updated dependencies [8e74b27]
2732
+ - Updated dependencies [7102b20]
2733
+ - Updated dependencies [8ebd57f]
2734
+ - Updated dependencies [617707a]
2735
+ - Updated dependencies [c40f3b8]
2736
+ - Updated dependencies [58770f3]
2737
+ - Updated dependencies [aefe428]
2738
+ - Updated dependencies [485f096]
2739
+ - Updated dependencies [7357447]
2740
+ - Updated dependencies [199d31b]
2741
+ - Updated dependencies [b655a9d]
2742
+ - Updated dependencies [3e01cb5]
2743
+ - Updated dependencies [7138bc1]
2744
+ - Updated dependencies [cef27e2]
2745
+ - Updated dependencies [4e8622b]
2746
+ - Updated dependencies [dffd752]
2747
+ - Updated dependencies [06973aa]
2748
+ - Updated dependencies [50798f3]
2749
+ - Updated dependencies [6a576c9]
2750
+ - Updated dependencies [105f3c5]
2751
+ - Updated dependencies [3ccd9e8]
2752
+ - Updated dependencies [689b979]
2753
+ - Updated dependencies [c70f865]
2754
+ - Updated dependencies [e546222]
2755
+ - Updated dependencies [fd13f52]
2756
+ - Updated dependencies [d7bd274]
2757
+ - Updated dependencies [98c3a74]
2758
+ - Updated dependencies [fffa30d]
2759
+ - Updated dependencies [ebce5a3]
2760
+ - Updated dependencies [fb336df]
2761
+ - Updated dependencies [9d9040d]
2762
+ - Updated dependencies [20e317c]
2763
+ - Updated dependencies [0fce2ef]
2764
+ - Updated dependencies [42df928]
2765
+ - Updated dependencies [0e2ddd4]
2766
+ - Updated dependencies [b7479ab]
2767
+ - Updated dependencies [9850c6e]
2768
+ - Updated dependencies [de570cc]
2769
+ - Updated dependencies [b2ea297]
2770
+ - Updated dependencies [5b5a5c3]
2771
+ - Updated dependencies [14582b8]
2772
+ - Updated dependencies [51e144e]
2773
+ - Updated dependencies [19cbf10]
2774
+ - Updated dependencies [ab92940]
2775
+ - Updated dependencies [a691c0b]
2776
+ - Updated dependencies [0b1326d]
2777
+ - Updated dependencies [1e66879]
2778
+ - Updated dependencies [c5200f0]
2779
+ - Updated dependencies [af3861f]
2780
+ - Updated dependencies [515f171]
2781
+ - Updated dependencies [1f4e029]
2782
+ - Updated dependencies [4f14ad7]
2783
+ - Updated dependencies [258d264]
2784
+ - Updated dependencies [cac64b3]
2785
+ - Updated dependencies [8033ad1]
2786
+ - Updated dependencies [fa140b8]
2787
+ - Updated dependencies [71cba28]
2788
+ - Updated dependencies [190fbd0]
2789
+ - Updated dependencies [c00bf28]
2790
+ - Updated dependencies [93127bd]
2791
+ - Updated dependencies [f2158ec]
2792
+ - Updated dependencies [759606e]
2793
+ - Updated dependencies [fd8dace]
2794
+ - Updated dependencies [72ffc34]
2795
+ - Updated dependencies [a51fa0c]
2796
+ - Updated dependencies [51f3d8d]
2797
+ - Updated dependencies [bf28341]
2798
+ - Updated dependencies [78cbdb5]
2799
+ - Updated dependencies [b7543a9]
2800
+ - Updated dependencies [6c6cee7]
2801
+ - Updated dependencies [42887e0]
2802
+ - Updated dependencies [83fe6e7]
2803
+ - Updated dependencies [d1ab06f]
2804
+ - Updated dependencies [38a9568]
2805
+ - Updated dependencies [f90b8fb]
2806
+ - Updated dependencies [91783c4]
2807
+ - Updated dependencies [982885d]
2808
+ - Updated dependencies [dba7d84]
2809
+ - Updated dependencies [ca39427]
2810
+ - Updated dependencies [bd09957]
2811
+ - Updated dependencies [5a07e67]
2812
+ - Updated dependencies [2d36552]
2813
+ - Updated dependencies [45d8288]
2814
+ - Updated dependencies [b2437a7]
2815
+ - Updated dependencies [f157423]
2816
+ - Updated dependencies [7a90afd]
2817
+ - Updated dependencies [eddc1dd]
2818
+ - Updated dependencies [490f482]
2819
+ - Updated dependencies [27308c5]
2820
+ - Updated dependencies [8689166]
2821
+ - Updated dependencies [c9327c9]
2822
+ - Updated dependencies [920165d]
2823
+ - Updated dependencies [9101be5]
2824
+ - Updated dependencies [f53a8d0]
2825
+ - Updated dependencies [968dc1e]
2826
+ - Updated dependencies [57f9b07]
2827
+ - Updated dependencies [3c73d99]
2828
+ - Updated dependencies [d91aed9]
2829
+ - Updated dependencies [ed71d9e]
2830
+ - Updated dependencies [7776fc2]
2831
+ - Updated dependencies [e76634c]
2832
+ - Updated dependencies [c86185e]
2833
+ - Updated dependencies [fb96ecb]
2834
+ - Updated dependencies [1170ed1]
2835
+ - Updated dependencies [92814db]
2836
+ - Updated dependencies [4d73b07]
2837
+ - @object-ui/react@17.7.0
2838
+ - @object-ui/core@17.7.0
2839
+ - @object-ui/types@17.7.0
2840
+ - @object-ui/i18n@17.7.0
2841
+ - @object-ui/components@17.7.0
2842
+
3
2843
  ## 17.6.0
4
2844
 
5
2845
  ### Minor Changes