@object-ui/plugin-list 17.5.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 +4550 -0
- package/README.md +115 -38
- package/dist/ListView.d.ts +140 -5
- package/dist/ListView.d.ts.map +1 -1
- package/dist/ListViewBlock.d.ts +1 -1
- package/dist/ListViewBlock.d.ts.map +1 -1
- package/dist/ObjectGallery.d.ts +35 -23
- package/dist/ObjectGallery.d.ts.map +1 -1
- package/dist/UserFilters.d.ts.map +1 -1
- package/dist/ViewSwitcher.d.ts +23 -1
- package/dist/ViewSwitcher.d.ts.map +1 -1
- package/dist/components/TabBar.d.ts.map +1 -1
- package/dist/components/ViewSettingsPopover.d.ts.map +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1420 -1326
- package/dist/index.umd.cjs +2 -2
- package/package.json +23 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,4555 @@
|
|
|
1
1
|
# @object-ui/plugin-list
|
|
2
2
|
|
|
3
|
+
## 17.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 57a2bc2: Retire the undeclared timeline `metaFields` reads in both packages (objectui#10222).
|
|
8
|
+
|
|
9
|
+
The spec's `TimelineConfigSchema` is a strict object that declares no `metaFields` member and
|
|
10
|
+
refuses one, yet `ObjectTimeline` read it (through an `as any` cast) to pick the chip fields
|
|
11
|
+
beside each title, and `ListView` read it three times: two field collectors put the listed
|
|
12
|
+
fields into `$expand` / `$select`, and the status / priority auto-projection was skipped when
|
|
13
|
+
the list was present. All four reads are deleted.
|
|
14
|
+
|
|
15
|
+
**Behaviour change:** a stored interface-page view carrying `options.timeline.metaFields` falls
|
|
16
|
+
back to the default card labels (the built-in `status` / `priority` chips, limited to the fields
|
|
17
|
+
the object declares), and the fields that list named are no longer fetched for it. No such view
|
|
18
|
+
has been found; the stored rows are not censused. A timeline without the key renders and fetches
|
|
19
|
+
exactly what it did before, because `ListView` now projects `status` / `priority` for every
|
|
20
|
+
timeline view.
|
|
21
|
+
|
|
22
|
+
`cardFields` stays the reserved spelling if authored timeline chip fields are ever asked for;
|
|
23
|
+
nothing is declared.
|
|
24
|
+
- ae98f1d: A `chart` list view bound to a semantic `dataset` takes its scope from the dataset:
|
|
25
|
+
a view filter on it is now refused at authoring, and its toolbar no longer offers
|
|
26
|
+
filter controls (objectui#10327).
|
|
27
|
+
|
|
28
|
+
The dataset shape of the list view's chart builds a node with no filter: the chart
|
|
29
|
+
queries the dataset through `queryDataset` with its dimensions and measures, and a
|
|
30
|
+
dataset's fields need not be the list object's. So a view filter written on such a
|
|
31
|
+
view was accepted and silently dropped, and the chart drew unfiltered totals that
|
|
32
|
+
read as filtered ones. The toolbar Filter builder and the filter chips reached only
|
|
33
|
+
the list's own fetch of the object's rows, never the chart.
|
|
34
|
+
|
|
35
|
+
**New refusal (`@object-ui/types`).** `ListViewSchema` refuses `filter` — and the
|
|
36
|
+
legacy `filters` alias — on a view with `viewType: 'chart'` whose chart block (`chart`,
|
|
37
|
+
or the legacy `options.chart` bag when no `chart` is declared) names a `dataset`. The
|
|
38
|
+
issue is a `custom` one at the written key, and its message ends with the remedy: a
|
|
39
|
+
dataset chart's scope is written in the dataset. An empty filter array, a chart bound
|
|
40
|
+
to the list object, and a view of another kind that only offers a switch to a dataset
|
|
41
|
+
chart are all still accepted.
|
|
42
|
+
|
|
43
|
+
**Toolbar (`@object-ui/plugin-list`).** While a dataset-bound chart is on screen, the
|
|
44
|
+
Filter builder and the `userFilters` chips are not rendered. A chart bound to the list
|
|
45
|
+
object keeps both, because its node carries the effective filter.
|
|
46
|
+
|
|
47
|
+
What an author sees change: a dataset chart view that declared a `filter` now fails
|
|
48
|
+
validation with the remedy above instead of drawing unfiltered numbers. Put the scope
|
|
49
|
+
in the dataset. No example, app or doc in this repository declared such a view; the
|
|
50
|
+
search is on the pull request.
|
|
51
|
+
- 974760a: fix(plugin-list): a map list view requests the fields its markers are drawn from
|
|
52
|
+
|
|
53
|
+
`ListView` builds its `$select` projection from the authored columns plus each
|
|
54
|
+
view kind's per-row bindings. Kanban, calendar, gallery, timeline and gantt each
|
|
55
|
+
had an arm there; the map had none, and the candidate keys named none of its
|
|
56
|
+
bindings. A map view whose columns omit the location field therefore asked for
|
|
57
|
+
`id` plus those columns, and on a backend that honours `$select` every row
|
|
58
|
+
arrived without coordinates: a map with no markers, and no error.
|
|
59
|
+
|
|
60
|
+
The request shape changes: a map view now also requests the fields `ObjectMap`
|
|
61
|
+
reads off each row — `locationField`, or the `latitudeField` + `longitudeField`
|
|
62
|
+
pair, plus `titleField` and `descriptionField`. They are read through the same
|
|
63
|
+
resolver the map render branch and the view switcher already use (the spec's
|
|
64
|
+
view-level `map` block merged per key over the `options.map` bag), so a bag value
|
|
65
|
+
the view-level block shadows is not requested. A coordinate or title binding that
|
|
66
|
+
names a lookup is also expanded, as the map's own fetch already does; the
|
|
67
|
+
description binding is requested but not expanded, because the marker popup
|
|
68
|
+
renders it as text and cannot draw an expanded record there.
|
|
69
|
+
|
|
70
|
+
Every one of these fields passes the same checks as the other view bindings: a
|
|
71
|
+
field the current user may not read is never requested, and a name the object
|
|
72
|
+
does not declare is never sent.
|
|
73
|
+
- d0c0c7f: The list view's live query sends "Is empty" / "Is not empty" as the spec's `isempty` / `isnotempty` instead of an equality to `null` (objectui#10813). `@objectstack/spec` 17.6.0 and later lower that pair to `$empty`; an earlier reader lowers it to `$null`, as it does the saved view's `is_empty`.
|
|
74
|
+
|
|
75
|
+
`convertFilterGroupToAST` resolved the pair to `[FIELD, '=', null]` / `[FIELD, '!=', null]`, a null-only test, before `mapOperator` was consulted. The same rule saved into the view is persisted as `is_empty`, which `@objectstack/spec` 17.6.0 lowers to `$empty` (objectstack#20446): so one filter panel answered two record sets, depending on whether the view had been saved. The pair now takes the value-less path like `is_null`, and `mapOperator` gains `isempty` / `isnotempty` arms. The node is `[FIELD, 'isempty', null]`; the spec discards the third slot.
|
|
76
|
+
|
|
77
|
+
**What moves.** Nothing is stored by this path. Against a 17.6.0 or later server, on a text-like column a row holding `''` is now "empty", and on a multi-value column a row holding `[]` is, matching the server's per-type answer for the saved view. On a `provider: 'value'` list a row with no key at all is now "empty" too: the equality to `null` did not select it there.
|
|
78
|
+
- 244d516: A grouped grid over a data source that declares no `queryGroupHeaders` now
|
|
79
|
+
**refuses grouping** instead of grouping a page of rows (objectui#10881,
|
|
80
|
+
maintainer ruling F). Grouping is a property of the query (ruling A on
|
|
81
|
+
objectui#7189): a data source that cannot answer the group header query cannot
|
|
82
|
+
group, so the grid names the member it is missing rather than drawing counts it
|
|
83
|
+
cannot know.
|
|
84
|
+
|
|
85
|
+
- **`@object-ui/plugin-grid`** — a grouped `object-grid` that fetches its own
|
|
86
|
+
rows over such a source renders an error panel naming `queryGroupHeaders`
|
|
87
|
+
and issues no row query. It used to fetch one window and group it in the
|
|
88
|
+
browser, so every group count was a page slice and a group whose rows all
|
|
89
|
+
fell past the window was missing. Rows handed in WHOLE — a
|
|
90
|
+
`data: { provider: 'value', items }` block, or a `data` prop from a parent
|
|
91
|
+
view that declares no larger `rowCount` — are still grouped in the browser,
|
|
92
|
+
exactly. A host that hands rows in while declaring them one page of more
|
|
93
|
+
(the external-pagination props: `manualPagination`, `onPageChange` and a
|
|
94
|
+
`rowCount` above the rows it handed) is now refused as well, with a sentence
|
|
95
|
+
of its own: grouping needs every record. It used to have that page grouped as
|
|
96
|
+
if it were whole.
|
|
97
|
+
- **`@object-ui/plugin-list`** — `ListView` makes the same refusal, with the
|
|
98
|
+
same sentence, in place of a grouped grid over such a source, and fetches no
|
|
99
|
+
window for it. It used to fetch one window and hand it to the grid as
|
|
100
|
+
`data`, which the grid grouped as if the rows were whole. Rows handed to
|
|
101
|
+
`ListView` in whole are still handed to the grid.
|
|
102
|
+
- **`@object-ui/i18n`** — two new keys, `grid.grouping.needsHeaderQuery` and
|
|
103
|
+
`grid.grouping.needsWholeRows`, in all ten locale packs.
|
|
104
|
+
|
|
105
|
+
The `Partial` marker described by the pending
|
|
106
|
+
`7189-grouped-grid-partial-disclosure` entry is retired everywhere before any
|
|
107
|
+
release carried it: its three `grid.grouping.partial*` strings, `GroupRow`'s
|
|
108
|
+
`partialLabel` / `partialTitle` props and the notice above the group list are
|
|
109
|
+
gone.
|
|
110
|
+
|
|
111
|
+
**Breaking semantics (declared `minor` per this repo's version policy):** a
|
|
112
|
+
grouped grid that fetches its own rows over a data source without
|
|
113
|
+
`queryGroupHeaders` — `ApiDataSource`, `ValueDataSource`, or a host adapter —
|
|
114
|
+
grouped the page it fetched in the last release, and now renders an error panel
|
|
115
|
+
instead. So does a grouped grid view in `ListView` over such a source. So does
|
|
116
|
+
a grouped grid handed rows by a host that declares them one page of more
|
|
117
|
+
(`manualPagination`, `onPageChange` and a `rowCount` above the rows handed),
|
|
118
|
+
whatever its data source. To keep grouping, either:
|
|
119
|
+
|
|
120
|
+
- implement `queryGroupHeaders` on the data source (the ObjectStack adapter
|
|
121
|
+
does), and let the grid fetch its own rows, or
|
|
122
|
+
- hand the rows in whole (`data: { provider: 'value', items }`, or a `data`
|
|
123
|
+
prop with no `rowCount` above it).
|
|
124
|
+
|
|
125
|
+
Not changed here: while a toolbar search is active, `ListView` over a data
|
|
126
|
+
source that answers the header query still hands a grouped grid its window,
|
|
127
|
+
whose group counts are the window's, because the header query carries no
|
|
128
|
+
search (objectstack#20358).
|
|
129
|
+
- e176053: Consolidate the seven lucide icon-name resolvers into one seam (objectui#5935).
|
|
130
|
+
|
|
131
|
+
Seven modules resolved authored icon names into lucide's runtime `icons` record, each
|
|
132
|
+
with its own copy of the logic: **three different tokenisers** (`split('-')` on five of
|
|
133
|
+
them, `split(/[-_\s]/)` on one, `split(/[-_\s]+/)` on one) and the `Home` -> `House`
|
|
134
|
+
rename on only **four** of the seven. The same authored name therefore rendered on one
|
|
135
|
+
surface and not another — the sidebar-vs-action-bar disagreement objectui#5633 opened
|
|
136
|
+
with. There is now one resolver, `resolveIcon`, exported from `@object-ui/components`,
|
|
137
|
+
and the other six call it.
|
|
138
|
+
|
|
139
|
+
**The tokeniser is `split(/[-_\s]+/)` with `Home` -> `House` applied universally, and it
|
|
140
|
+
was measured rather than chosen.** Its regression set is empty three independent ways:
|
|
141
|
+
against the authored population, against a maximally-pessimistic every-authored-name x
|
|
142
|
+
every-surface cross-product, and against a bound-free differential over 8,298 spellings
|
|
143
|
+
derived from all 1,767 live record keys — each with a discrimination control that fired
|
|
144
|
+
in the same run. `split('-')` was **not** adoptable: it regresses 4,748 name-surface
|
|
145
|
+
pairs in that last reading, stripping two surfaces of every snake_case and
|
|
146
|
+
space-separated spelling they resolve today.
|
|
147
|
+
|
|
148
|
+
**What changes for you — all of it widening, none of it removal.** No name that resolved
|
|
149
|
+
before stops resolving: no key of lucide's record contains `_`, whitespace or `-`
|
|
150
|
+
(measured: 0 of 1,767), so whenever the old narrow tokeniser produced a live key the
|
|
151
|
+
wider one produces the same key. Sixteen name-surface pairs start resolving where they
|
|
152
|
+
rendered a fallback or nothing before:
|
|
153
|
+
|
|
154
|
+
- `layout_dashboard` and `building_2` (and every other snake_case or space-separated
|
|
155
|
+
spelling) now resolve on the shared resolver, `ui:icon`, `ListView`'s empty state,
|
|
156
|
+
`TabBar` and `ViewSwitcher` — they previously resolved only on the action preview and
|
|
157
|
+
the related list.
|
|
158
|
+
- `home` / `Home` now resolves on `RelatedList`, `ListView` and `TabBar`, which carried
|
|
159
|
+
no rename map. `Home` is not a live record key, so this could only ever be a widening.
|
|
160
|
+
|
|
161
|
+
**What does NOT change: what each surface draws when a name does not resolve.** The seam
|
|
162
|
+
answers `name -> component`, returning `null`, and decides nothing else (maintainer
|
|
163
|
+
ruling 2026-09-03 on objectui#5935). Every call site keeps its own fallback, visibly, at
|
|
164
|
+
the call site: `ui:icon` keeps its `SquareDashed` placeholder and its warning
|
|
165
|
+
(objectui#5631, untouched), `RelatedList` and `ListView` keep their `Inbox` glyph,
|
|
166
|
+
`ActionPreview` keeps its three-character name chip, and the shared resolver, `TabBar`
|
|
167
|
+
and `ViewSwitcher` keep `null`. A two-valued `onUnresolvable` parameter was ruled on and
|
|
168
|
+
then dropped once the tree was measured to have four such behaviours rather than two: a
|
|
169
|
+
lookup function is the wrong place to publish a presentation decision.
|
|
170
|
+
|
|
171
|
+
`resolveIcon` is newly exported from `@object-ui/components`, which is the only surface
|
|
172
|
+
this adds. `scripts/check-lucide-icon-record-names.mjs` is simplified in the same change:
|
|
173
|
+
its census goes from seven sites to one, and its normalisation stops being a
|
|
174
|
+
widest-common approximation of three disagreeing resolvers — so the under-reporting that
|
|
175
|
+
gate disclosed at objectui#5932 is closed rather than merely bounded.
|
|
176
|
+
- 4ca30d0: Two widget prop types anchor their `schema` to exported schema types that extend
|
|
177
|
+
`BaseSchema`, instead of hand-rolled inline literals with no `BaseSchema` in
|
|
178
|
+
their ancestry (objectui#6576, maintainer ruling 2026-08-31 option A; folds
|
|
179
|
+
objectui#6914).
|
|
180
|
+
|
|
181
|
+
- `@object-ui/types` exports `ObjectGallerySchema` (`type: 'object-gallery'`)
|
|
182
|
+
and `ObjectDataTableSchema` (`type: 'object-data-table'`), each `extends
|
|
183
|
+
BaseSchema`, beside the other `Object*Schema` declarations, with zod mirrors
|
|
184
|
+
of the same names under `@object-ui/types/zod`. `ObjectDataTableSchema`
|
|
185
|
+
declares the two keys the widget was reading behind casts — `drillDown`
|
|
186
|
+
(`DrillDownConfig`, at this change) and `onRowClick` — which no declaration
|
|
187
|
+
carried before.
|
|
188
|
+
|
|
189
|
+
⚠️ **Dated note, 2026-09-25 — `drillDown` now takes the table's own shape —
|
|
190
|
+
objectui#10685.** `ObjectDataTableSchema.drillDown` is
|
|
191
|
+
`ObjectDataTableDrillDownConfig`, on both faces: `filter`, `maxRows` and
|
|
192
|
+
`report` are refused by name, and `target` is `'drawer'` or `'dialog'`. None of
|
|
193
|
+
the four was ever read by this block. The rest of this entry is kept as the
|
|
194
|
+
reading of this change.
|
|
195
|
+
- `@object-ui/plugin-list`: the published `ObjectGalleryProps.schema` is
|
|
196
|
+
`ObjectGallerySchema`. Its accept set WIDENS — every `BaseSchema` member is
|
|
197
|
+
writable (`visibleWhen`, a real base member, was a compile error on the
|
|
198
|
+
literal) — and NARROWS in one place: `type` is now required and pinned to
|
|
199
|
+
`'object-gallery'`. `data` stays `Record<string, unknown>[]`.
|
|
200
|
+
- `@object-ui/plugin-dashboard`: `ObjectDataTableProps.schema` (not exported
|
|
201
|
+
from the plugin index) is `ObjectDataTableSchema`. The literal's own
|
|
202
|
+
`[key: string]: any` is gone, so a wrong-typed base member (`visible: 42`) and
|
|
203
|
+
a wrong-shaped `drillDown` are refused, and `type` is pinned to
|
|
204
|
+
`'object-data-table'` instead of bare `string`.
|
|
205
|
+
|
|
206
|
+
Unchanged on both, stated plainly: an UNKNOWN key still compiles, because
|
|
207
|
+
`BaseSchema`'s index signature is inherited (objectui#5155, open). No runtime
|
|
208
|
+
behaviour changes; the widgets render exactly as before.
|
|
209
|
+
- 045d20b: Relationship-target readers resolve a lookup's target from `reference` alone,
|
|
210
|
+
dropping the `reference_to` fallback arm (objectui#6837, half 2).
|
|
211
|
+
|
|
212
|
+
Maintainer ruling, 2026-08-31, 原文照录: 「objectui不是前端的项目吗?后端的元数据只要
|
|
213
|
+
对,前端按协议执行就行了呀」. Protocol normalization belongs on the SERVER; the front
|
|
214
|
+
end just executes the protocol. objectstack#13847 landed the server half — a
|
|
215
|
+
`field-reference-to-alias` conversion rewrites stored `reference_to` to
|
|
216
|
+
`reference` on the serve path and in `os migrate meta`.
|
|
217
|
+
|
|
218
|
+
`reference` is the only target spelling `@objectstack/spec`'s `FieldSchema`
|
|
219
|
+
declares. Measured on the installed 17.2.0: it refuses `reference_to`,
|
|
220
|
+
`referenceTo` and `target` with `unrecognized_keys`, each carrying its own
|
|
221
|
+
"Did you mean -> `reference`?" rename, while a nonsense key gets the same
|
|
222
|
+
refusal with NO rename hint and `reference` parses clean.
|
|
223
|
+
|
|
224
|
+
## ⚠️ BREAKING for a hand-written schema that spells `reference_to` — read this
|
|
225
|
+
|
|
226
|
+
**This is a behaviour change for BYO consumers, and it is being stated rather
|
|
227
|
+
than shipped silently.** ObjectUI is usable without an ObjectStack backend
|
|
228
|
+
(`examples/byo-backend-console`), and a hand-written TypeScript schema passes
|
|
229
|
+
through no zod door, so nothing rejects the legacy spelling at authoring time.
|
|
230
|
+
|
|
231
|
+
**The break surface is narrower than "all BYO consumers", and this is the
|
|
232
|
+
measurement rather than a blanket claim.** Two ingestion choke points stamp both
|
|
233
|
+
snake_case keys from whichever spelling arrived — `MetadataProvider`'s type
|
|
234
|
+
cache for metadata type `object`, and `ObjectStackAdapter.getObjectSchema`. Any
|
|
235
|
+
def that passed either one already carries `reference` and is **completely
|
|
236
|
+
unaffected**. What is affected is exactly:
|
|
237
|
+
|
|
238
|
+
- **A `DataSource` implementation other than `ObjectStackAdapter`.**
|
|
239
|
+
`getObjectSchema` is a required member of the published `DataSource`
|
|
240
|
+
interface, and the readers call it on the generic `dataSource` (through
|
|
241
|
+
`useSettledSchema` and directly), so a host adapter's object schema reaches
|
|
242
|
+
them raw. Every in-repo example of one is on this path:
|
|
243
|
+
`ApiDataSource`, `ValueDataSource`, `packages/types/examples/rest-data-source.ts`,
|
|
244
|
+
`examples/byo-backend-console/src/mockDataSource.ts`,
|
|
245
|
+
`packages/runner/src/lib/mockDataSource.ts`,
|
|
246
|
+
`apps/site/app/components/galleryDataSource.ts`,
|
|
247
|
+
`apps/console/src/sdui-workbench-preview.tsx`,
|
|
248
|
+
`packages/plugin-grid/demo/bulk-actions.tsx`.
|
|
249
|
+
|
|
250
|
+
**Measured on this tree, none of those eight emits a relationship target at all** —
|
|
251
|
+
`reference_to` and `reference` are both zero in each, and
|
|
252
|
+
`examples/byo-backend-console` carries no lookup or master_detail field
|
|
253
|
+
anywhere (its only `reference` hits are a vite triple-slash directive and a
|
|
254
|
+
tsconfig `references` array). The single in-repo producer that WAS on this
|
|
255
|
+
surface, `packages/plugin-gantt/demo/main.tsx`, is fixed here at the producer.
|
|
256
|
+
|
|
257
|
+
⇒ **If you author object metadata by hand and spell a lookup's target
|
|
258
|
+
`reference_to`, rename that key to `reference`.** Symptom if you do not: the
|
|
259
|
+
target silently fails to resolve, and the affected surface degrades rather than
|
|
260
|
+
erroring — a related list is not derived, a gantt quick filter falls back to the
|
|
261
|
+
distinct values in the loaded rows instead of the referenced object's full
|
|
262
|
+
domain, a tree stops auto-detecting its parent pointer, a lookup cell shows a
|
|
263
|
+
raw id, a chart's group-by labels stay unresolved.
|
|
264
|
+
|
|
265
|
+
The ingestion choke point now emits a **dev-mode warning** when a def arrives
|
|
266
|
+
carrying only `reference_to` or `referenceTo` and no `reference`. It names the
|
|
267
|
+
object, the field and the offending key, and points at this ruling. Stamping is
|
|
268
|
+
deliberately unchanged, so nothing that worked stops working. It is memoised
|
|
269
|
+
once per **(object name, field name, spelling, target value)** — every segment
|
|
270
|
+
of that key is pinned, in both directions, in
|
|
271
|
+
`reference-keys.legacyWarning-6837.test.ts`.
|
|
272
|
+
|
|
273
|
+
⛔ **This warning does NOT cover the break described above, and it is worth being
|
|
274
|
+
exact about that rather than letting it read as mitigation.** It lives in
|
|
275
|
+
`normalizeFieldReferenceKeys`, reachable only through
|
|
276
|
+
`normalizeSchemaReferenceKeys`, which has exactly two production call sites —
|
|
277
|
+
`MetadataProvider` (metadata type `object`) and
|
|
278
|
+
`ObjectStackAdapter.getObjectSchema`. Both of those also STAMP the def, so the
|
|
279
|
+
warning fires precisely where the def still resolves and nothing is broken. A
|
|
280
|
+
hand-written schema served through any OTHER `DataSource` — the break surface —
|
|
281
|
+
reaches a reader raw: it never passes through this code and produces **no
|
|
282
|
+
warning at all**. On that path the failure is exactly as silent as before.
|
|
283
|
+
A reader-side or shared-resolver diagnostic, which would cover it, remains open
|
|
284
|
+
on objectui#6837.
|
|
285
|
+
|
|
286
|
+
⚠️ **Dated note, 2026-09-29 — the call-site count above has since moved —
|
|
287
|
+
objectui#7650.** "exactly two production call sites" above held when this change landed
|
|
288
|
+
(`045d20ba8`). Later in this same release objectui#7650 (PR objectui#8868) added a call
|
|
289
|
+
on `MetadataProvider`'s by-name object-schema serve path. Re-measured for objectui#10979
|
|
290
|
+
on `main` at `2eaf5be27`, `normalizeSchemaReferenceKeys` had three production call sites:
|
|
291
|
+
two in `MetadataProvider`, on its list path and on its by-name path, and one in
|
|
292
|
+
`ObjectStackAdapter.getObjectSchema`.
|
|
293
|
+
`.changeset/7650-metadata-item-reference-canonicalisation.md` (PR objectui#8868) states
|
|
294
|
+
what ships; the text above is kept as the reading of this change.
|
|
295
|
+
|
|
296
|
+
## What did NOT change
|
|
297
|
+
|
|
298
|
+
**Every key these readers EMIT is byte-identical**, and that was verified
|
|
299
|
+
mechanically over the whole diff rather than asserted. Eleven of the sixteen
|
|
300
|
+
sites write a target onto a bag whose own contract spells it `reference_to` (or
|
|
301
|
+
camelCase `referenceTo`): the six whose read and write share a line —
|
|
302
|
+
`RecordDetailDrawer`, `RelatedList`, `buildDefaultPageSchema`, `ListView`,
|
|
303
|
+
`FilterConditionField`, `resolveActionParams` — plus five more that read on one
|
|
304
|
+
line and emit on another, and so are just as much emitters: `RecordDetailView`,
|
|
305
|
+
`RecordMetaFooter`, `ObjectGallery`, `fieldEnrichment` (all `reference_to`) and
|
|
306
|
+
`UserFilters` (`referenceTo`). Only the right-hand read narrowed anywhere; the
|
|
307
|
+
emitted key is what its target contract declares, and renaming it would be a
|
|
308
|
+
separate change.
|
|
309
|
+
|
|
310
|
+
**Three readers were deliberately left alone.** `LookupCellRenderer`
|
|
311
|
+
(`fields/src/index.tsx`), `LookupField` and `UserField` read `FieldMetadata` —
|
|
312
|
+
ObjectUI's OWN contract, whose `LookupFieldMetadata` declares `reference_to` and
|
|
313
|
+
never declares `reference`. They are fed by the emitters above and by published
|
|
314
|
+
example schemas (`examples/schema-catalog/src/schemas/fields-lookup/*.json`), so
|
|
315
|
+
narrowing them would break in-repo producers, and `plugin-grid`'s
|
|
316
|
+
`relationalMetaCopySet.derivation.test.ts` re-derives its read set from exactly
|
|
317
|
+
those three sources — where `reference_to` is recorded with verdict
|
|
318
|
+
`adapter-stamped`. `DetailViewFieldSchema` is likewise untouched.
|
|
319
|
+
|
|
320
|
+
⚠️ **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.
|
|
321
|
+
- bd0376d: Read `count` / `value` answers as the contract declares them at six more seams
|
|
322
|
+
(objectui#6917, following objectui#5945 / #6726 / #6840 / #6839).
|
|
323
|
+
|
|
324
|
+
**One precedence inversion, repaired without deleting the arm.**
|
|
325
|
+
`@object-ui/fields`' lookup chip resolved fetch-on-demand rows with
|
|
326
|
+
`result?.value || result?.data || []` — `value` AHEAD of `data`, the one rows
|
|
327
|
+
member `QueryResult` (`@object-ui/types`) declares. A producer emitting both was
|
|
328
|
+
resolved to the undeclared key. It now reads through `@object-ui/core`'s
|
|
329
|
+
`extractRecords`, whose accepted set is identical (bare array, `data`, `value`)
|
|
330
|
+
and whose order is the contract's. The `value` arm is **kept**: its own producer
|
|
331
|
+
census measured eight live `find()` doubles emitting `{ value: [...] }` at this
|
|
332
|
+
seam (3 plugin-kanban, 3 plugin-calendar, 2 plugin-grid), so deleting it would
|
|
333
|
+
break them. Only the RANK was wrong.
|
|
334
|
+
|
|
335
|
+
**Five dead arms deleted, each on its own measured zero.** Every module got its
|
|
336
|
+
own census with the control sitting on the producer→consumer join, because
|
|
337
|
+
objectui#6840's zero is seam-local and is not transferable — the same sweep read
|
|
338
|
+
0 producers for `value` at one seam and 5 at another in a single pass.
|
|
339
|
+
|
|
340
|
+
- `count` at the `DataSource.find()` seam — `plugin-detail`'s reference rail and
|
|
341
|
+
`plugin-list`'s ListView. 0 of 592 `find()` producers emit `count`; controls
|
|
342
|
+
`data` (312) and `total` (150) lit on the same pass. Both adapters'
|
|
343
|
+
`normalizeQueryResult` already fold `count` into `total` below every consumer.
|
|
344
|
+
- `value` at the `client.meta.getItems()` seam — `app-shell`'s help menu and the
|
|
345
|
+
console's Public Forms and Flow Runs pages. **These three do not sit on the
|
|
346
|
+
`DataSource.find()` seam at all**, so they were measured on their own join: 0
|
|
347
|
+
of 28 `meta.getItems` producers emit `value`; control `items` (18) lit. The
|
|
348
|
+
canonical readers of that envelope (`MetadataProvider.extractItems`,
|
|
349
|
+
`MetadataService.getItems`) have never had a `value` arm either.
|
|
350
|
+
|
|
351
|
+
No producer changes behaviour, because at these five sites there is no producer;
|
|
352
|
+
what changes is that a non-conforming one is refused rather than silently
|
|
353
|
+
absorbed (AGENTS.md #0.1).
|
|
354
|
+
|
|
355
|
+
`QueryResult` is **not** widened to bless `count` or `value` — a published-type
|
|
356
|
+
change and the maintainer's call, the floor objectui#6726, #6840 and #6839 all
|
|
357
|
+
held. A producer that really speaks either belongs behind an adapter that folds
|
|
358
|
+
it, which is what both adapters already do.
|
|
359
|
+
|
|
360
|
+
One refusal pin per module, each keeping the live arms green beside the deleted
|
|
361
|
+
one, and — where an inversion actually existed — a case feeding both members with
|
|
362
|
+
different contents, the only input that can tell the two orders apart.
|
|
363
|
+
|
|
364
|
+
Also repaired: two `plugin-grid` test doubles answered `{ value: [],
|
|
365
|
+
'@odata.count': 0 }` while `ObjectGrid` reads `result.data` / `result.total`.
|
|
366
|
+
Inert only while the arrays were empty; the first row put in one would have been
|
|
367
|
+
silently dropped. Test-only.
|
|
368
|
+
- 2a7ac32: Calendar views no longer render on invented field names (objectui#7029; ruled on
|
|
369
|
+
objectstack#13748, director batch #19, option A).
|
|
370
|
+
|
|
371
|
+
A view that carried no `calendar:` block used to have a complete-looking calendar
|
|
372
|
+
configuration synthesized for it. `ObjectCalendar` has always decided whether it
|
|
373
|
+
has a usable configuration by asking whether a start-date binding is PRESENT, so
|
|
374
|
+
the fabrication short-circuited its own refusal screen — "Calendar configuration
|
|
375
|
+
required" — which existed all
|
|
376
|
+
along and was simply unreachable. Measured on a leave-request object whose real
|
|
377
|
+
fields are `start_date` / `end_date`: every record piled onto today's cell under
|
|
378
|
+
titles resolved through the display-name chain. A plausible, fully wrong screen,
|
|
379
|
+
with zero signal to the author.
|
|
380
|
+
|
|
381
|
+
Three faces were fabricating, on two independent routes to the same renderer:
|
|
382
|
+
|
|
383
|
+
- `app-shell/ObjectView` emitted `startDateField: 'due_date'` and
|
|
384
|
+
`titleField: 'name'` into `options.calendar` for every object view;
|
|
385
|
+
- `plugin-list/ListView`'s calendar branch floored the same two bindings at
|
|
386
|
+
`'start_date'` / `'end_date'` one layer down;
|
|
387
|
+
- `plugin-view/ObjectView.generateViewSchema` — the authored `object-view`
|
|
388
|
+
element route, which bypasses `ListView` entirely — carried its own copy.
|
|
389
|
+
|
|
390
|
+
All three now forward only what the author declared. This converges the calendar
|
|
391
|
+
on the shape its siblings already had: `timelineViewOptions` (objectui#3129
|
|
392
|
+
retired this very literal from the timeline axis), the kanban lane detector
|
|
393
|
+
(ADR-0085, "never invents a field the object doesn't have"), and
|
|
394
|
+
`defaultCalendarFromObject` (a binding, or nothing).
|
|
395
|
+
|
|
396
|
+
**Behaviour change, loud over silent.** With no binding to forward, ADR-0047's
|
|
397
|
+
capability gate stops offering the Calendar toggle to views that configured
|
|
398
|
+
none, and a view forced onto the calendar renderer reaches the refusal screen
|
|
399
|
+
instead of a wrong one. A view that happened to sit on an object carrying a real
|
|
400
|
+
`due_date` field was rendering by luck; it now refuses until its `calendar:`
|
|
401
|
+
block is written. Correctly configured calendars are unaffected — same fields,
|
|
402
|
+
same render. The same deletion also stops the fabricated name from answering for
|
|
403
|
+
the Timeline switcher, which accepts a calendar binding as a legitimate axis.
|
|
404
|
+
|
|
405
|
+
The spec half — cross-field validation rejecting a half-written declaration at
|
|
406
|
+
authoring time — is objectstack#13817. This half makes the runtime honest
|
|
407
|
+
independent of which spec version the host pins.
|
|
408
|
+
- 5f4514f: Gantt views no longer render on invented date field names.
|
|
409
|
+
|
|
410
|
+
The half PR #7062 fenced out and reported separately. A view that carried no
|
|
411
|
+
`gantt:` block used to have a complete-looking date axis synthesized for it:
|
|
412
|
+
all three faces floored `startDateField` at `'start_date'` and `endDateField`
|
|
413
|
+
at `'end_date'` — field names no view had written and most objects do not
|
|
414
|
+
carry.
|
|
415
|
+
|
|
416
|
+
`ObjectGantt.getGanttConfig` takes its flat branch as soon as BOTH date props
|
|
417
|
+
are present, so the fabricated pair short-circuited the renderer's own refusal
|
|
418
|
+
screen — "Gantt configuration required. Please specify startDateField,
|
|
419
|
+
endDateField, and titleField." — which existed all along and was simply
|
|
420
|
+
unreachable from every route. The same fabrication answered ADR-0047's
|
|
421
|
+
capability gate in `ListView.availableViews`, so the Gantt toggle was live on
|
|
422
|
+
every object view in the product.
|
|
423
|
+
|
|
424
|
+
⚠️ The premise was MEASURED before anything was deleted, because #7029's
|
|
425
|
+
mechanic is only correct where a refusal path exists and that had never been
|
|
426
|
+
established for this renderer: on the unmodified tree, `ObjectGantt` REFUSES an
|
|
427
|
+
absent binding — it does not render empty, and it does not throw.
|
|
428
|
+
|
|
429
|
+
Three faces were fabricating, on two independent routes to the same renderer:
|
|
430
|
+
|
|
431
|
+
- `app-shell/src/views/ObjectView.tsx` — the console object page. The inline
|
|
432
|
+
branch becomes `ganttViewOptions`, the sibling of `calendarViewOptions` and
|
|
433
|
+
`timelineViewOptions`: the declared block spread whole, title floored at
|
|
434
|
+
`'name'`, no date field invented.
|
|
435
|
+
- `plugin-list/src/ListView.tsx` — the render branch AND the capability gate.
|
|
436
|
+
- `plugin-view/src/ObjectView.tsx` — `generateViewSchema`, the authored
|
|
437
|
+
`object-view` element route, which bypasses `ListView` entirely.
|
|
438
|
+
|
|
439
|
+
**What changes for an author.** A view that declared no gantt configuration is
|
|
440
|
+
no longer offered the Gantt toggle, and one forced onto the renderer reaches
|
|
441
|
+
the refusal screen instead of a plausible, fully wrong chart. A view that
|
|
442
|
+
declared a binding is unaffected — the declared block is forwarded exactly as
|
|
443
|
+
before, every spec key included.
|
|
444
|
+
|
|
445
|
+
Also corrected: the objectui#3129 note at the top of `app-shell/ObjectView.tsx`
|
|
446
|
+
certified the gantt branch below it as already using the safe two-rung shape.
|
|
447
|
+
It did not. The note now states each sibling branch as measured, and says
|
|
448
|
+
explicitly which fabrication REMAINS — the timeline `'created_at'` floor at the
|
|
449
|
+
two plugin faces, which is routed to a ruling rather than settled
|
|
450
|
+
per-face.
|
|
451
|
+
|
|
452
|
+
Deliberately out of scope, and left in place: `progressField` / `dependenciesField`
|
|
453
|
+
(not date axes, different absent-value semantics) and the timeline `'created_at'`
|
|
454
|
+
posture conflict.
|
|
455
|
+
- 04a67b9: Retire the `'created_at'` timeline date-axis floors at both plugin faces
|
|
456
|
+
(step ③ of the maintainer ruling 2026-09-01, 总监批 #28).
|
|
457
|
+
|
|
458
|
+
**Breaking, deliberately.** A timeline view that declares **no** date axis anywhere no
|
|
459
|
+
longer renders. `ListView`'s and `ObjectView`'s timeline branches used to hand
|
|
460
|
+
`ObjectTimeline` a `startDateField` of `'created_at'` for such a view; both now forward
|
|
461
|
+
a declared axis or no key at all, and the renderer shows its "declare a date axis"
|
|
462
|
+
refusal instead.
|
|
463
|
+
|
|
464
|
+
House posture, entered with the ruling: **日期轴永不虚构** — a date axis is never
|
|
465
|
+
fabricated. This is the third and last step of a sequence the ruling ordered and forbade
|
|
466
|
+
reordering: `ObjectTimeline` gained the refusal screen and lost its own internal
|
|
467
|
+
`|| 'date'` floor first (objectui#7459), which by its own measurement changed nothing a
|
|
468
|
+
user could see — precisely because these two faces still supplied a name. They are the
|
|
469
|
+
supply.
|
|
470
|
+
|
|
471
|
+
The floor was not a harmless default. `'created_at'` is a column nearly every object
|
|
472
|
+
carries, so downstream it was indistinguishable from a real binding and could never
|
|
473
|
+
resolve to nothing — while the `$select` projection is collected from the **declared**
|
|
474
|
+
`timeline` / `options.timeline` blocks and never from this prop. An undeclared view was
|
|
475
|
+
therefore given a timeline bound to a column the query had not requested, and every
|
|
476
|
+
record bucketed into "No date": a screen that looks built, is wrong, and gives the
|
|
477
|
+
author no signal. The ruling also explicitly replaced the written decision that stood on
|
|
478
|
+
the deleted `ListView` line ("`created_at` stays the last resort for a view that
|
|
479
|
+
declares no date axis anywhere") — it was a second, de-facto contract held at one face,
|
|
480
|
+
on the very literal objectui#3129 had retired at the app-shell face.
|
|
481
|
+
|
|
482
|
+
**Migration.** Declare the axis on the view: `timeline.startDateField` (spec-canonical),
|
|
483
|
+
`timeline.dateField` (legacy alias), or a `calendar.startDateField` — objectui#3129
|
|
484
|
+
established that a calendar binding is a legitimate timeline axis, and it still is. All
|
|
485
|
+
three keep rendering exactly as before; only the *undeclared* case changes. A view that
|
|
486
|
+
really did want records laid out by creation time says so in one key:
|
|
487
|
+
`timeline: { startDateField: 'created_at' }`. The refusal names the accepted keys on
|
|
488
|
+
screen, so an affected view reports its own fix.
|
|
489
|
+
|
|
490
|
+
`titleField` is unaffected and keeps its `'name'` floor at both faces — it is not a date
|
|
491
|
+
axis. So do gantt's `progressField` / `dependenciesField`, which the ruling scoped out
|
|
492
|
+
for separate evaluation.
|
|
493
|
+
- 78ca238: `DataErrorState` accepts the icon props `DataEmptyState` already had, and `ListView`'s
|
|
494
|
+
load-failure panel is now rendered by the error state instead of the empty state
|
|
495
|
+
(objectui#7143; maintainer ruling 2026-09-01, director decision batch #27).
|
|
496
|
+
|
|
497
|
+
`ListView` rendered its load FAILURE through `DataEmptyState` — the component named for
|
|
498
|
+
the *empty* case — passing it a destructive icon, error copy and a retry action, while
|
|
499
|
+
`DataErrorState`, in the same file and with the same layout, had no consumer anywhere in
|
|
500
|
+
the repo. objectui#7132 closed the accessibility half of that collision (the panel now
|
|
501
|
+
declares `role="alert"` over the empty state's `role="status"` default) and deliberately
|
|
502
|
+
left the structural half alone: `DataErrorState` hardcoded its icon, so the swap was a
|
|
503
|
+
props-surface question plus a visual change rather than a rename.
|
|
504
|
+
|
|
505
|
+
**`@object-ui/components` — three additive optional props on `DataErrorState`**, mirrored
|
|
506
|
+
from `DataEmptyState` in the same file rather than spelled a second way:
|
|
507
|
+
|
|
508
|
+
- `icon?: React.ReactNode` — rendered above the title; falls back to the `AlertCircle`
|
|
509
|
+
glyph the component has always drawn.
|
|
510
|
+
- `showIcon?: boolean` (default `true`) — `false` omits the icon container entirely.
|
|
511
|
+
- `iconWrapperClassName?: string` — REPLACES the wrapper's default class rather than
|
|
512
|
+
merging with it, so `""` renders the icon raw. `DataEmptyState` resolves it with `??`
|
|
513
|
+
against its own default and this does the same, against
|
|
514
|
+
`flex size-10 items-center justify-center rounded-lg bg-destructive/10` — the destructive
|
|
515
|
+
square `DataErrorState` already drew.
|
|
516
|
+
|
|
517
|
+
Same names, same types, same default semantics as the empty state's; nothing existing on
|
|
518
|
+
`DataErrorState` changed, and a call site that passes none of the three renders exactly
|
|
519
|
+
what it rendered before. `illustration` and `action` were deliberately NOT mirrored — the
|
|
520
|
+
ruling pins three props, and this component's retry affordance is already spelled
|
|
521
|
+
`onRetry` / `retryLabel` (plus `children` for a call site that needs its own control).
|
|
522
|
+
|
|
523
|
+
One non-prop addition rides along, called out rather than folded in: the icon wrapper now
|
|
524
|
+
carries `data-slot="data-error-state-icon"`, mirroring the empty state's
|
|
525
|
+
`data-empty-state-icon`. Without it the wrapper `iconWrapperClassName` governs has no
|
|
526
|
+
name — untestable and unstylable — and migrating a call site off `DataEmptyState` would
|
|
527
|
+
DROP that identifier rather than rename it.
|
|
528
|
+
|
|
529
|
+
**`@object-ui/plugin-list` — the panel changes component identity, not pixels.** The call
|
|
530
|
+
site passes the same custom icon through the new `icon` prop, the same
|
|
531
|
+
`iconWrapperClassName="mb-3"`, the same title, and the same copy through `message` (the
|
|
532
|
+
error state's spelling of `description`); its retry `<Button>` moves from `action` to
|
|
533
|
+
`children`, which renders at the identical position. `role="alert"`, the
|
|
534
|
+
`data-testid="list-error-state"` hook and `data-error-kind` are untouched. The whole
|
|
535
|
+
rendered delta is two attributes:
|
|
536
|
+
|
|
537
|
+
- the panel root's `data-slot` becomes `data-error-state` (was `data-empty-state`);
|
|
538
|
+
- the icon wrapper's becomes `data-error-state-icon` (was `data-empty-state-icon`).
|
|
539
|
+
|
|
540
|
+
Both are renames, not removals. Nothing in this repo styles or selects on either — no CSS
|
|
541
|
+
rule and no test read them — so a stylesheet in a host app targeting
|
|
542
|
+
`[data-slot="data-empty-state"]` to reach *this* panel is the only way to notice, and it
|
|
543
|
+
should be reading `data-error-state` now. Every class on every node, and the glyphs
|
|
544
|
+
themselves, are byte-identical: this is a visual no-op, deliberately, so the review the
|
|
545
|
+
ruling asks for has a small thing to look at rather than a redesign.
|
|
546
|
+
- e2b3826: Grid grouping is now **server-side** (objectui#7189, maintainer ruling A): the
|
|
547
|
+
set of groups and every number in a group header — the count and any per-group
|
|
548
|
+
aggregation — come from the query, and the rows inside a group are paged by the
|
|
549
|
+
server.
|
|
550
|
+
|
|
551
|
+
Before, a grouped grid fetched one window and bucketed it in the browser, so
|
|
552
|
+
both the group set and every header number were properties of the page. On a
|
|
553
|
+
store of 186 records over five business units sized 86 / 61 / 31 / 7 / 1 with a
|
|
554
|
+
100-row page it rendered **two** headers (86, 14) when the rows were stored
|
|
555
|
+
contiguously and five page slices (31 / 31 / 30 / 7 / 1) when interleaved — and
|
|
556
|
+
the records past the window were not on a second page, they were unreachable.
|
|
557
|
+
The same store now renders five headers reading 86 / 61 / 31 / 7 / 1 in either
|
|
558
|
+
order, and every record is reachable through its group's own pager.
|
|
559
|
+
|
|
560
|
+
- **`@object-ui/types`** — `DataSource` gains an optional
|
|
561
|
+
`queryGroupHeaders(resource, query)`: it answers the group header query
|
|
562
|
+
`@objectstack/spec/ui`'s `compileListViewGroupQuery` compiles (an
|
|
563
|
+
`EngineAggregateOptions`) with one header row per group. Presence is the
|
|
564
|
+
capability; a source that cannot answer it leaves it undeclared.
|
|
565
|
+
- **`@object-ui/data-objectstack`** — `ObjectStackAdapter.queryGroupHeaders`
|
|
566
|
+
posts the compiled query verbatim to the existing `POST /data/:object/query`
|
|
567
|
+
door and answers its `records`. It never degrades: a refusal throws, and a
|
|
568
|
+
body without `records` is refused rather than read as "no groups".
|
|
569
|
+
- **`@object-ui/plugin-grid`** — a grouped grid that fetches its own rows from a
|
|
570
|
+
source declaring `queryGroupHeaders` asks for its headers (one compiled query
|
|
571
|
+
per grouping level) and pages each **open** group's rows with the group's
|
|
572
|
+
own compiled row query (`compileListViewGroupRowsQuery`: the view's filter AND
|
|
573
|
+
the group key, `limit` / `offset` per group); a collapsed group costs no row
|
|
574
|
+
query, `aggregations` are the header query's numbers, and a reference-typed
|
|
575
|
+
grouping key is labelled from the referenced record. Such a grid's column
|
|
576
|
+
sort is the server's, and it no longer shows the `Partial` marker: its counts
|
|
577
|
+
are the query's own. Rows handed in whole are still grouped in the browser
|
|
578
|
+
(exact), and a source with no header query still groups the page it fetched
|
|
579
|
+
and still marks it partial.
|
|
580
|
+
- **`@object-ui/plugin-list`** — `ListView` hands a grouped grid its own fetch,
|
|
581
|
+
with the view's effective filter, when the data source can answer the header
|
|
582
|
+
query, and draws no record-count bar over it. While a toolbar search is
|
|
583
|
+
active it keeps hosting the rows as before: `$search` has no counterpart on
|
|
584
|
+
the header query.
|
|
585
|
+
|
|
586
|
+
**Breaking semantics (declared `minor` per this repo's version policy):**
|
|
587
|
+
|
|
588
|
+
- A grouped grid over a capable source sends different requests: one header
|
|
589
|
+
query per grouping level plus one row query per open group, instead of one
|
|
590
|
+
window. A host that counted or mocked the single window sees the new shape.
|
|
591
|
+
- `useGroupedData` takes an optional fifth argument (the server's header rows).
|
|
592
|
+
- `GroupEntry` gains two REQUIRED members: `count` (the group's size — use it
|
|
593
|
+
instead of `rows.length`, which is empty on a server-grouped grid) and
|
|
594
|
+
`keyValues`. Code that only READS a `GroupEntry` is unaffected; code that
|
|
595
|
+
CONSTRUCTS a `GroupEntry` literal no longer type-checks until it supplies
|
|
596
|
+
both.
|
|
597
|
+
- `AggregationResult.value` is `number | null`, `null` being what the server's
|
|
598
|
+
aggregate answered over no values (rendered as a dash, never invented as 0).
|
|
599
|
+
- `@object-ui/plugin-grid` and `@object-ui/data-objectstack` now require
|
|
600
|
+
`@objectstack/spec` `^17.4.0`, the first release that ships the group-query
|
|
601
|
+
compilers.
|
|
602
|
+
|
|
603
|
+
This supersedes the last paragraph of the pending
|
|
604
|
+
`7189-grouped-grid-partial-disclosure` changeset, which said server-side
|
|
605
|
+
grouping was not built: it now is, and the `Partial` marker described there
|
|
606
|
+
remains only for the data sources that cannot answer the header query.
|
|
607
|
+
|
|
608
|
+
⚠️ **Dated note, 2026-09-28 — a source with no header query now refuses
|
|
609
|
+
grouping — objectui#10881.** Later in this same release two sentences above
|
|
610
|
+
stopped describing the grid: that a source with no header query still groups
|
|
611
|
+
the page it fetched and still marks it partial, and that the `Partial` marker
|
|
612
|
+
remains for the data sources that cannot answer the header query. Maintainer
|
|
613
|
+
ruling F retired both. Over such a source a grouped grid that fetches its own
|
|
614
|
+
rows, and a `ListView` hosting a grouped grid, refuse grouping with an error
|
|
615
|
+
naming `queryGroupHeaders`, and the marker is gone everywhere. Rows handed in
|
|
616
|
+
whole are still grouped in the browser. The rest of this entry is kept as the
|
|
617
|
+
reading of this change; the `10881-grouping-needs-header-query` entry states
|
|
618
|
+
what ships.
|
|
619
|
+
- 4c2e1d7: Gantt views no longer hand the renderer invented `progress` / `dependencies`
|
|
620
|
+
field names (objectui#7499).
|
|
621
|
+
|
|
622
|
+
Flavour 3 of the card whose date-axis half landed as `5f4514f7b` — the half its 2026-09-01 ruling ordered carded
|
|
623
|
+
separately and judged on its own terms, ⛔ explicitly forbidding the date-axis
|
|
624
|
+
conclusion from being imported. Two faces floored the pair, and app-shell
|
|
625
|
+
carries neither key (measured: `|| 'progress'` counts 0 there; the control that
|
|
626
|
+
the zero is not a dead probe is that app-shell *does* floor `titleField` at
|
|
627
|
+
`'name'`):
|
|
628
|
+
|
|
629
|
+
- `plugin-list/src/ListView.tsx` — the `object-gantt` render branch.
|
|
630
|
+
- `plugin-view/src/ObjectView.tsx` — `generateViewSchema`, the authored
|
|
631
|
+
`object-view` element route, which bypasses `ListView` entirely.
|
|
632
|
+
|
|
633
|
+
**The remedy is OMIT, not refuse — and the asymmetry against the date axis is
|
|
634
|
+
exactly why the date-axis conclusion must not be imported.** A fabricated date
|
|
635
|
+
axis has no legitimate twin: every bar lands on a column nobody declared, a
|
|
636
|
+
whole-chart error, so refusal is right there. A fabricated `progress` /
|
|
637
|
+
`dependencies` name yields a per-row `undefined`, which is indistinguishable
|
|
638
|
+
from the legitimate and common case — most gantt rows have neither. Refusing
|
|
639
|
+
would break that common case; fabricating manufactured a binding the author
|
|
640
|
+
never wrote, whose failure was silent. Omission does neither.
|
|
641
|
+
|
|
642
|
+
**What changes for an author.** A view that declares neither `progressField`
|
|
643
|
+
nor `dependenciesField` now passes a config carrying neither key, instead of
|
|
644
|
+
`'progress'` / `'dependencies'`. A view that declares one passes the author's
|
|
645
|
+
value verbatim, exactly as before, through both the spec-canonical `gantt`
|
|
646
|
+
block and the legacy `options.gantt` nesting.
|
|
647
|
+
|
|
648
|
+
The only population whose behaviour actually changes is a schema carrying a
|
|
649
|
+
column literally named `progress` or `dependencies` while NOT declaring the
|
|
650
|
+
corresponding field key — those worked by accident, because the floor happened
|
|
651
|
+
to match the column name. That population was measured across `examples/`,
|
|
652
|
+
`content/docs/**`, the `packages/*/src` fixtures and all 528 JSON/YAML metadata
|
|
653
|
+
files before anything was deleted: it is **empty**. The census and its
|
|
654
|
+
blind-spot reading are in the PR.
|
|
655
|
+
|
|
656
|
+
Nothing about the refusal screen moves: `ObjectGantt.getGanttConfig` gates on
|
|
657
|
+
the two date fields alone, so deleting this pair cannot resurrect a config —
|
|
658
|
+
`plugin-gantt/src/ObjectGantt.unconfiguredRefusal-7070.test.tsx` is the
|
|
659
|
+
non-regression control and stays green.
|
|
660
|
+
|
|
661
|
+
⚠️ One consequence worth recording for objectui#6470, which is NOT touched
|
|
662
|
+
here. `getGanttConfig`'s flat branch reads
|
|
663
|
+
`schema.dependenciesField || schema.dependencyField`. While the floor was in
|
|
664
|
+
place, `dependenciesField` was always truthy through these two faces, so the
|
|
665
|
+
declared legacy singular alias `dependencyField` was unreachable via them —
|
|
666
|
+
shadowed, not read. With the floor gone, a view that wrote `dependencyField`
|
|
667
|
+
now resolves through the alias limb as its `@deprecated` contract already
|
|
668
|
+
promises. No in-repo view declares it (measured: zero sites at either routed
|
|
669
|
+
face), and the alias's own deprecation and retirement remain objectui#6470's
|
|
670
|
+
question, deliberately left alone.
|
|
671
|
+
- d80447d: Stop inventing a gallery cover binding, and offer the view switcher only when it
|
|
672
|
+
can switch (objectui#7547).
|
|
673
|
+
|
|
674
|
+
**The object page no longer floors `gallery.imageField` at `'image'`.** It used
|
|
675
|
+
to supply that key for every object view, declared or not. The screen damage was
|
|
676
|
+
small — `ObjectGallery` collapses the cover area when no record yields a cover —
|
|
677
|
+
but the key fed `ListView`'s ADR-0047 capability gate, which reads
|
|
678
|
+
`options.gallery.imageField`, so **Gallery was offered on every object view that
|
|
679
|
+
whitelisted it**, with nothing behind the toggle. `galleryViewOptions` now
|
|
680
|
+
forwards the view's own declared block (both legacy cover spellings still
|
|
681
|
+
cross-fill each other, and `titleField` keeps its `'name'` display floor) and
|
|
682
|
+
emits no cover key when the view declared none. Same class and same route as
|
|
683
|
+
objectui#7029 (calendar) and `5f4514f7b` (gantt dates).
|
|
684
|
+
|
|
685
|
+
⚠️ A view that whitelisted `gallery` without a `gallery:` block loses the Gallery
|
|
686
|
+
toggle. That is the ADR-0047 rule working: it was only ever offered because this
|
|
687
|
+
relay answered the gate on the author's behalf.
|
|
688
|
+
|
|
689
|
+
**The visualization switcher is drawn for the resolved list, not the whitelist.**
|
|
690
|
+
`showViewSwitcher` was computed from the LENGTH of
|
|
691
|
+
`appearance.allowedVisualizations` — the whitelist BEFORE `ListView` intersects
|
|
692
|
+
it with the capability gate — so a view whitelisting `['grid', 'timeline']` with
|
|
693
|
+
no timeline block drew switcher chrome around a single Grid entry. The predicate
|
|
694
|
+
now lives at the one site that holds both halves. Views whose whitelisted types
|
|
695
|
+
all resolve are unaffected.
|
|
696
|
+
- 6712930: The two kanban adapters stop writing the retired `groupField` onto the
|
|
697
|
+
`object-kanban` node they generate (objectui#7773). `groupBy` — the key the
|
|
698
|
+
renderer actually reads — is unchanged and is now the only lane key emitted.
|
|
699
|
+
|
|
700
|
+
**What was measured, on this branch's base (`a915064e`).** Both adapters emitted
|
|
701
|
+
the key twice:
|
|
702
|
+
|
|
703
|
+
```
|
|
704
|
+
packages/plugin-view/src/ObjectView.tsx:1362 groupField: groupBy,
|
|
705
|
+
packages/plugin-list/src/ListView.tsx:2500 groupField: laneField,
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
The `object-kanban` renderer never read it: `groupField` has ZERO hits anywhere
|
|
709
|
+
under `packages/plugin-kanban/`, against a control of thirteen `schema.groupBy`
|
|
710
|
+
read sites in `ObjectKanban.tsx` from the same query — so the zero is a reading,
|
|
711
|
+
not a blind grep. The write was inert; the board grouped by `groupBy` and
|
|
712
|
+
`groupField` rode along unread.
|
|
713
|
+
|
|
714
|
+
**Why it is removed rather than tolerated.** objectui#7322 RETIRED
|
|
715
|
+
`groupField` on this node on both published faces — `groupField?: never` on the
|
|
716
|
+
TypeScript interface and a `retirementTombstone()` in the Zod mirror, which
|
|
717
|
+
refuses an authored value BY NAME. So the adapters were producers emitting a
|
|
718
|
+
node their own published contract rejects. That was harmless only because a
|
|
719
|
+
generated node never reaches the mirror at runtime (`SchemaRenderer` runs the
|
|
720
|
+
structural `validateSchema`, not `safeValidateSchema`) — but the CLI's
|
|
721
|
+
`os check` / `os validate` DO run the mirror, so the identical node was already
|
|
722
|
+
refused when authored by hand and admitted when generated. This closes that
|
|
723
|
+
split.
|
|
724
|
+
|
|
725
|
+
**What changes for a consumer.** Nothing on any documented path: the renderer's
|
|
726
|
+
behaviour is byte-identical, because it never read the key. A host that
|
|
727
|
+
registers its own `object-kanban` component and reads `props.schema.groupField`
|
|
728
|
+
off the generated node now reads `undefined` — read `groupBy` instead, which
|
|
729
|
+
carries the same value and always did. Graded `minor` rather than `patch` for
|
|
730
|
+
exactly that narrowing, following the repo convention that objectui's own
|
|
731
|
+
breaking changes ship as `minor` (AGENTS.md 版本号策略, mechanically enforced by
|
|
732
|
+
`scripts/check-changeset-no-major.mjs`).
|
|
733
|
+
|
|
734
|
+
**Who is NOT affected — the boundary is node-local.** Every VIEW-LEVEL
|
|
735
|
+
`groupField` read is untouched and still live: it is a legacy alias of the
|
|
736
|
+
spec's `groupByField` on the kanban *view config*, mapped by
|
|
737
|
+
`normalize-list-view.ts`, and both adapters still resolve lanes through it
|
|
738
|
+
(`ObjectView.tsx`'s `kanbanCfg.groupField ||`, `ListView.tsx`'s
|
|
739
|
+
`groupByField || groupField`). Authoring `options.kanban.groupField` on a
|
|
740
|
+
`list-view` or `object-view` keeps working exactly as documented in
|
|
741
|
+
`packages/plugin-list/README.md`. `groupField` is dead only on the generated
|
|
742
|
+
`object-kanban` NODE.
|
|
743
|
+
|
|
744
|
+
The two tests that pinned the duplicate write are TURNED, not deleted — they now
|
|
745
|
+
assert the key is absent, so restoring the write reddens them instead of being
|
|
746
|
+
silently re-blessed by a missing assertion.
|
|
747
|
+
- edea22a: **BREAKING** — `SchemaRendererProvider`'s `dataSource` prop, and the context
|
|
748
|
+
type every `useSchemaContext()` consumer reads back, are the published
|
|
749
|
+
`DataSource` contract instead of `any`.
|
|
750
|
+
|
|
751
|
+
**FROM** `dataSource={anything}` **TO** `dataSource={adapter}` — a `DataSource`
|
|
752
|
+
from `@object-ui/types`, or `null` / `undefined` when the host has no adapter
|
|
753
|
+
bound.
|
|
754
|
+
|
|
755
|
+
```ts
|
|
756
|
+
// before — compiled, and failed at runtime on the first find()
|
|
757
|
+
<SchemaRendererProvider dataSource={'not-an-adapter'}>
|
|
758
|
+
// before — compiled, and no reader can do anything with it
|
|
759
|
+
<SchemaRendererProvider dataSource={{}}>
|
|
760
|
+
// after
|
|
761
|
+
<SchemaRendererProvider dataSource={adapter}> // a DataSource
|
|
762
|
+
<SchemaRendererProvider dataSource={undefined}> // "I have no adapter"
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Both sites are typed `DataSource | null | undefined` — the spelling
|
|
766
|
+
`useSettledSchema` in this same package already used. The two absences are part
|
|
767
|
+
of the contract, not a weakening of it: a Studio preview, a `kind:'react'` page
|
|
768
|
+
rendered before the host's adapter connects, and a widget probe driving
|
|
769
|
+
`apiFetch` alone all render with nothing bound, and every reader in the tree
|
|
770
|
+
already guards for it. What the union refuses is everything that is not an
|
|
771
|
+
adapter: a string, an empty object, a plain data bag, a partial adapter missing
|
|
772
|
+
a required member.
|
|
773
|
+
|
|
774
|
+
The measured cost, both halves, because the two `any`s have different blast
|
|
775
|
+
radii (measured separately on `origin/main`, whole-repo type-check over the 33
|
|
776
|
+
packages that depend on `@object-ui/react`):
|
|
777
|
+
|
|
778
|
+
- the **context type** — the `any` that reaches every `useSchemaContext()`
|
|
779
|
+
reader — reds **7 diagnostics at 7 sites in 4 packages**, all of them
|
|
780
|
+
production code or a mocked module factory.
|
|
781
|
+
- the **provider prop** — the injection points — reds **52 diagnostics at 27
|
|
782
|
+
sites in 11 packages**, all but one of them test doubles.
|
|
783
|
+
|
|
784
|
+
That ordering is the reverse of the prediction on the card: the context `any`
|
|
785
|
+
was expected to be the expensive one because it infects the whole tree, and it
|
|
786
|
+
is the cheap one, because every reader in the tree already guarded and none of
|
|
787
|
+
them ever reached past `find` / `getObjectSchema`. The prop is the expensive
|
|
788
|
+
one, because the injection points are overwhelmingly test doubles that were
|
|
789
|
+
never complete adapters. The full accounting is on objectui#7912.
|
|
790
|
+
|
|
791
|
+
Two runtime behaviours change, both in the "no adapter" direction and both
|
|
792
|
+
strictly closer to what the surrounding code already intended:
|
|
793
|
+
|
|
794
|
+
- `@object-ui/components`' `kind:'react'` page passed an empty object as its
|
|
795
|
+
"no adapter yet" stand-in. An empty object is TRUTHY, so it walked past every
|
|
796
|
+
`if (!dataSource)` guard written to catch exactly that state and failed later,
|
|
797
|
+
at the call. It now passes the absent adapter itself, so the guard fires where
|
|
798
|
+
it was meant to. The module-constant identity that stand-in existed for is
|
|
799
|
+
preserved: `null` is a primitive, so the provider's memo is unaffected.
|
|
800
|
+
- `@object-ui/plugin-calendar`, `@object-ui/plugin-gantt` and
|
|
801
|
+
`@object-ui/plugin-kanban` collapse a `null` adapter from the context to
|
|
802
|
+
`undefined` before handing it to their widget, whose prop declares the single
|
|
803
|
+
spelling `dataSource?: DataSource`.
|
|
804
|
+
|
|
805
|
+
Nothing else moves at runtime: no value flowing through this key changes, and
|
|
806
|
+
no data path is touched. A TypeScript consumer outside this repo that handed
|
|
807
|
+
this prop something other than an adapter now gets a compile error naming the
|
|
808
|
+
key (TS2322 / TS2739 / TS2740), which is why the FROM/TO is spelled out above.
|
|
809
|
+
|
|
810
|
+
Five `as any` reads of this context in `@object-ui/fields` are gone — they were
|
|
811
|
+
redundant the moment the seam became honest — and `LookupField`'s local
|
|
812
|
+
re-declaration of the imported context as a `Context` of `any`, which laundered
|
|
813
|
+
its `dataSource` read while looking typed, is gone with them. Both directions of
|
|
814
|
+
the contract are pinned against the real compiler in
|
|
815
|
+
`SchemaRendererContext.dataSourceType.pin.test.ts`, and the card's planted
|
|
816
|
+
documentation probe (a bare string in `packages/react/README.md`'s provider
|
|
817
|
+
example) now fails `pnpm check:doc-snippets`, where it used to exit 0 with zero
|
|
818
|
+
diagnostics.
|
|
819
|
+
|
|
820
|
+
objectui#7912.
|
|
821
|
+
- fb82877: `ListViewProps.onSortChange` and `onFilterChange` are declared at the types they
|
|
822
|
+
actually fire with, instead of `any`.
|
|
823
|
+
|
|
824
|
+
Both were left `any` by the objectui#4528 sweep that named every other prop on
|
|
825
|
+
this interface "at the type each one actually lands on". `dataSource?: any`
|
|
826
|
+
carries its own written justification right there; these two carried none, which
|
|
827
|
+
is what made this an oversight rather than a decision. The visible cost was that
|
|
828
|
+
the package README's documented "With Callbacks" example compiled green on all
|
|
829
|
+
four callbacks while only two of them were constrained by anything: assigning
|
|
830
|
+
`view` or `search` to a `number` inside the block raised TS2322, and assigning
|
|
831
|
+
`sort` or `filters` raised nothing at all.
|
|
832
|
+
|
|
833
|
+
Each parameter was measured at the emit site rather than read off the
|
|
834
|
+
declaration, and the two are asymmetric:
|
|
835
|
+
|
|
836
|
+
- `onSortChange` now takes `SortItem[]`. Every emit in the component crosses one
|
|
837
|
+
boundary, `emitSortChange`, and both of its legs carry that element type: the
|
|
838
|
+
array handed in, and `filterPlatformSortableSort`'s return, which is generic in
|
|
839
|
+
the element and so preserves whatever it is given. Normalized-vs-raw therefore
|
|
840
|
+
does not move the type here, only whether platform-unsortable entries are still
|
|
841
|
+
present.
|
|
842
|
+
- `onFilterChange` now takes `FilterGroup`. Its one call site passes the toolbar
|
|
843
|
+
`FilterBuilder`'s own `onChange` value straight through, beside a
|
|
844
|
+
`setCurrentFilters` that is itself state of that type. It is deliberately NOT
|
|
845
|
+
the filter AST `normalizeFilters` / `buildEffectiveFilter` speak — those run
|
|
846
|
+
later on the query-building path and nothing they produce reaches this
|
|
847
|
+
callback. A host receives the builder's group verbatim, which is what lets it
|
|
848
|
+
round-trip back in through `initialFilters`.
|
|
849
|
+
|
|
850
|
+
Both types were already exported from `@object-ui/components` and already
|
|
851
|
+
imported by this file; nothing new is minted.
|
|
852
|
+
|
|
853
|
+
Narrowing a callback parameter is contravariant, so the cost falls only on
|
|
854
|
+
handlers that did something with the parameter that `any` alone allowed — a
|
|
855
|
+
handler written `(sort: any) => ...` still compiles unchanged. The cost was
|
|
856
|
+
measured across every in-repo consumer that passes either callback
|
|
857
|
+
(`ObjectView`, in two places, and `InterfaceListPage`) plus every package that
|
|
858
|
+
imports `ListView` at all (`@object-ui/app-shell`, `@object-ui/plugin-map`,
|
|
859
|
+
`@object-ui/console`): all of them still type-check, and no handler needed
|
|
860
|
+
touching. Out-of-repo hosts that destructure or index the parameter in ways only
|
|
861
|
+
`any` permitted will now see a real error, which is the point.
|
|
862
|
+
- cb725e7: An unbound map now REFUSES; coordinates are never guessed (objectui#8169, maintainer
|
|
863
|
+
ruling 2026-09-07, decision batch #67, option B).
|
|
864
|
+
|
|
865
|
+
**Behaviour change, deliberately, with no staged window.** A map with no coordinate
|
|
866
|
+
binding renders
|
|
867
|
+
|
|
868
|
+
> Map configuration required — declare `map.locationField` or `map.latitudeField` + `map.longitudeField`
|
|
869
|
+
|
|
870
|
+
in place of the map, instead of painting an empty one. The principle behind
|
|
871
|
+
the 2026-09-01 date-axis ruling (date axes are never invented) and objectui#5953 (a marker title is never
|
|
872
|
+
forged) now covers coordinates as well: bindings are never fabricated, and an unbound
|
|
873
|
+
surface refuses.
|
|
874
|
+
|
|
875
|
+
Three things moved together, because moving any one of them alone makes the tree worse:
|
|
876
|
+
|
|
877
|
+
- `@object-ui/plugin-map` — `getMapConfig` loses its default branch, the one that
|
|
878
|
+
returned the field names `latitude` / `longitude` / `location` / `description` when
|
|
879
|
+
the author declared nothing, and `ObjectMap` gains the refusal state above. The
|
|
880
|
+
refusal covers every branch, so a declared block that binds no coordinate field
|
|
881
|
+
(`map: { titleField: 'name' }`) and a half pair (`latitudeField` with no
|
|
882
|
+
`longitudeField`) refuse too — those used to render an empty map under the
|
|
883
|
+
excluded-records notice.
|
|
884
|
+
- `@object-ui/plugin-list` and `@object-ui/plugin-view` — the `locationField: … ||
|
|
885
|
+
'location'` floor each flattener added on the way into the map is deleted. The
|
|
886
|
+
flattened schema now carries exactly what the view declared.
|
|
887
|
+
|
|
888
|
+
⚠️ **Deleting the relay floors alone would have widened the guess, not closed it** —
|
|
889
|
+
measured on the card. The floor forced `getMapConfig`'s flat branch, which returns
|
|
890
|
+
`locationField` and no `latitudeField` / `longitudeField`, so dropping it on its own
|
|
891
|
+
would have handed undeclared views the component's three-name default branch instead of
|
|
892
|
+
one name, and records carrying real `latitude` / `longitude` columns would have *started*
|
|
893
|
+
plotting on views that declared nothing.
|
|
894
|
+
|
|
895
|
+
**Migration.** Declare the binding on any map view that relied on the old defaults:
|
|
896
|
+
`map: { locationField: 'your_field' }`, or `map: { latitudeField, longitudeField }`. A
|
|
897
|
+
record set carrying `latitude` / `longitude` (or `location`) columns no longer plots on a
|
|
898
|
+
view that declared no map block — it shows the refusal, which names what to write.
|
|
899
|
+
Interface pages are unaffected where the object actually has a location-typed field:
|
|
900
|
+
`defaultMapFromObject` derives `locationField` from the object's own field list, which is
|
|
901
|
+
a reading of declared metadata rather than a guess, and is unchanged.
|
|
902
|
+
- 31a0d05: Emit the spec-canonical kanban lane key from the object page, and teach the
|
|
903
|
+
capability gate to recognize it (objectui#8193).
|
|
904
|
+
|
|
905
|
+
`ObjectView` built the view-level kanban config it hands to `list-view` and wrote
|
|
906
|
+
the deprecated alias `groupField` — never `groupByField`, the key
|
|
907
|
+
`@objectstack/spec`'s `KanbanConfigSchema` actually declares — even though it
|
|
908
|
+
already READ the canonical key first. Its sibling producer in the same package,
|
|
909
|
+
`defaultKanbanFromObject` in `InterfaceListPage`, had migrated long before and
|
|
910
|
+
left the reasoning next to itself ("that read-site now prefers the spec key, so
|
|
911
|
+
one key is enough"); the twin was not carried along, so one producer surface
|
|
912
|
+
spoke two vocabularies for one concept depending on which entry point you
|
|
913
|
+
arrived through. `ObjectView` writes into `options.kanban`, which
|
|
914
|
+
`normalizeListViewSchema`'s alias fold deliberately does not reach, so nothing
|
|
915
|
+
corrected it downstream and the legacy spelling was what drove the lanes.
|
|
916
|
+
|
|
917
|
+
The expression is now the exported `kanbanViewOptions`, the fifth member of the
|
|
918
|
+
`timelineViewOptions` / `calendarViewOptions` / `ganttViewOptions` /
|
|
919
|
+
`galleryViewOptions` family it had been the odd one out of.
|
|
920
|
+
|
|
921
|
+
**`ListView`'s kanban capability gate changed with it, and had to.** The gate
|
|
922
|
+
consulted the `options.kanban` bag for the LEGACY spelling only, so a producer
|
|
923
|
+
writing the spec key into the bag was invisible to it while still rendering
|
|
924
|
+
correctly — the render branch merges the bag and resolves
|
|
925
|
+
`groupByField || groupField`, so the gate recognized strictly less than what
|
|
926
|
+
renders. Measured before and after: a bag of `{groupBy, groupField}` offered
|
|
927
|
+
Kanban and `{groupBy, groupByField}` did not, so shipping the producer change
|
|
928
|
+
alone would have removed the Kanban toggle from every object view. The gate now
|
|
929
|
+
reads both spellings out of the bag — the same one-question-two-sites repair
|
|
930
|
+
objectui#5042 made for `map` and objectui#7544 for `chart`.
|
|
931
|
+
|
|
932
|
+
**No alias READ was removed, and the alias is not retired.** Stored metadata
|
|
933
|
+
still authors `groupField`, and every read site still resolves it; `ListView`
|
|
934
|
+
already preferring the canonical key is precisely why the sibling producer could
|
|
935
|
+
drop the alias write. What changed is only what this one face WRITES, plus one
|
|
936
|
+
added rung on the gate.
|
|
937
|
+
|
|
938
|
+
**Migration.** Nothing authored has to change. If you read
|
|
939
|
+
`options.kanban.groupField` off the schema `ObjectView` produces, read
|
|
940
|
+
`groupByField` (or both) instead — that bag now carries the spec spelling.
|
|
941
|
+
|
|
942
|
+
The view-level `groupBy` in the same bag is untouched. It is not a spec key
|
|
943
|
+
either — measured against the strict `KanbanConfigSchema`, which refuses it by
|
|
944
|
+
name — but `ListView`'s projection collectors read it, so retiring it needs its
|
|
945
|
+
own producer census and is filed as objectui#8213.
|
|
946
|
+
- 474797d: **BREAKING** — the calendar date aliases `dateField` and `endField` are retired
|
|
947
|
+
at both faces (objectui#8355). They are now **declared refusals**: an authored
|
|
948
|
+
value is rejected **by name**, at its own key path, pointed at `startDateField` /
|
|
949
|
+
`endDateField`.
|
|
950
|
+
|
|
951
|
+
**FROM** `calendar: { dateField, endField }` — accepted in silence, read by
|
|
952
|
+
`ObjectCalendar`'s alias ladder — **TO** a by-name refusal at validation, with
|
|
953
|
+
`startDateField` / `endDateField` as the only spellings that bind the axis.
|
|
954
|
+
|
|
955
|
+
```ts
|
|
956
|
+
// before — parsed green, drew a calendar
|
|
957
|
+
{ type: 'list-view', objectName: 'task', calendar: { dateField: 'kickoff', endField: 'wrapup' } }
|
|
958
|
+
// after — refused at validation, naming the canonical keys
|
|
959
|
+
{ type: 'list-view', objectName: 'task', calendar: { startDateField: 'kickoff', endDateField: 'wrapup' } }
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
**The refusal an author now meets**, verbatim from the arms' own messages (one
|
|
963
|
+
string per arm feeds both the parse-time issue and the `.describe()` metadata):
|
|
964
|
+
|
|
965
|
+
```text
|
|
966
|
+
Unrecognized key(s) on this calendar configuration: `dateField`. Did you mean
|
|
967
|
+
`dateField` → `startDateField`? `dateField` and `endField` are the pre-#2231
|
|
968
|
+
objectui spellings of the calendar date axis, retired at both faces by
|
|
969
|
+
objectui#8355. `@objectstack/spec` spells the axis `startDateField` and
|
|
970
|
+
`endDateField` only. ⚠️ This package accepted BOTH legacy spellings green until
|
|
971
|
+
that retirement — they rode a `.passthrough()` straight through
|
|
972
|
+
`safeValidateSchema` into `ListView`'s calendar branch, which flattened them onto
|
|
973
|
+
the generated `object-calendar` node where the renderer's alias ladder read them.
|
|
974
|
+
That ladder is gone, so the key is refused here instead of being kept and then
|
|
975
|
+
ignored. Write `startDateField` for the event start. Kept rather than refused, an
|
|
976
|
+
authored `dateField` binds nothing: the calendar falls through to "Calendar
|
|
977
|
+
configuration required", a screen that names the canonical keys and never the key
|
|
978
|
+
you wrote.
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
⚠️ **The consequence clause is per key, because the two spellings fail
|
|
982
|
+
differently.** `endField` gets the same stem and this tail instead — measured on
|
|
983
|
+
the renderer, not assumed:
|
|
984
|
+
|
|
985
|
+
```text
|
|
986
|
+
…Write `endDateField` for the event end. Kept rather than refused, an authored
|
|
987
|
+
`endField` fails even more quietly than its sibling: a calendar that also carries
|
|
988
|
+
a start binding still DRAWS, and only the end of every event is silently dropped.
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
On the flat node face the surface noun is `this object-calendar node`. ⚠️ **The
|
|
992
|
+
element's own `calendar` container carries a different tail for `dateField`**,
|
|
993
|
+
because it fails differently and was measured doing so: `getCalendarConfig` reads
|
|
994
|
+
that container WHOLE, so a retired spelling there never reaches the refusal
|
|
995
|
+
screen — the calendar mounts and simply places nothing, every record landing
|
|
996
|
+
under "Unscheduled". `endField` keeps one clause, because it reads the same on
|
|
997
|
+
both.
|
|
998
|
+
|
|
999
|
+
```text
|
|
1000
|
+
…Write `startDateField` for the event start. Kept rather than refused, an
|
|
1001
|
+
authored `dateField` fails without even reaching that refusal screen: this
|
|
1002
|
+
container is read WHOLE, so the calendar still mounts and simply places nothing —
|
|
1003
|
+
every record ends up under "Unscheduled" with no date to draw it on.
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
**ADR-0087 disposition — D2 tombstone, no conversion entry and no migration
|
|
1007
|
+
prescription.** The keys stay DECLARED and unwritable rather than being deleted,
|
|
1008
|
+
which is the whole of the ruling: every calendar block here ends `.passthrough()`
|
|
1009
|
+
and `BaseSchema` does too, so a deleted key is not refused — it is KEPT
|
|
1010
|
+
unexamined and then ignored. Nothing in `@objectstack/spec` converts these two
|
|
1011
|
+
(the protocol never declared either: `CalendarConfigSchema` is a strict object of
|
|
1012
|
+
`startDateField` / `endDateField` / `titleField` / `colorField`, measured on the
|
|
1013
|
+
installed pin 17.4.0 with a nonsense-key control on the same call), so there is
|
|
1014
|
+
no ledger entry to register and no `objectstack migrate meta` step to ship. The
|
|
1015
|
+
channel that reaches an author is the validator, and it now names the key and the
|
|
1016
|
+
remedy on all five authoring surfaces this vocabulary has: a list view's
|
|
1017
|
+
`calendar` block and its legacy `options.calendar` twin, a named view's two
|
|
1018
|
+
nestings under `listViews`, the flat `object-calendar` node face, and that
|
|
1019
|
+
element's own `calendar` container. ⚠️ Stated as five surfaces rather than
|
|
1020
|
+
"every author" on purpose: a host that builds the node in TypeScript and hands it
|
|
1021
|
+
to `SchemaRenderer` never parses the mirror at all — `SchemaRenderer` runs the
|
|
1022
|
+
structural core validator, which is dev-only and names no key — so the compiler
|
|
1023
|
+
is that population's channel, not this one.
|
|
1024
|
+
|
|
1025
|
+
⚠️ **Dated note, 2026-09-28 — a named view's `options.calendar` nesting is no longer
|
|
1026
|
+
judged inside — objectui#7928.** Later in this same release `ObjectViewSchema.listViews`
|
|
1027
|
+
became the protocol's strict `ObjectListViewSchema` record, by reference, and that
|
|
1028
|
+
record refuses a named view's `options` bag whole (`unrecognized_keys` at
|
|
1029
|
+
`listViews.KEY`, naming `options`). So an alias written under
|
|
1030
|
+
`listViews.KEY.options.calendar` no longer draws an issue naming the key or the remedy.
|
|
1031
|
+
The named view's `calendar` block still does: the protocol refuses the alias there as an
|
|
1032
|
+
unrecognized key, and this entry's check adds the pointer at the key beside it. A stored
|
|
1033
|
+
body that `@object-ui/app-shell`'s `ViewPreview` relays has its `options.calendar` folded
|
|
1034
|
+
onto the `calendar` block first (`foldStoredListOptions`). The list view's two nestings
|
|
1035
|
+
and both `object-calendar` surfaces are unchanged; `calendar-date-alias-refusal-8355.test.ts`
|
|
1036
|
+
in `@object-ui/types` re-derives each surface. `.changeset/7928-listviews-by-reference-fold.md`
|
|
1037
|
+
(PR objectui#10821) states what ships; the text above is kept as the reading of this change.
|
|
1038
|
+
|
|
1039
|
+
⚠️ **Why this is `minor` and not `major`.** This repository forbids `major` in a
|
|
1040
|
+
changeset: the fixed release group's major tracks `@objectstack`'s, so any
|
|
1041
|
+
`major` here would push all of it off that cadence
|
|
1042
|
+
(`scripts/check-changeset-no-major.mjs` enforces it). objectui's own breaking
|
|
1043
|
+
changes ship as `minor` with the break spelled out, which is what this entry is.
|
|
1044
|
+
|
|
1045
|
+
**What broke silently before, and why the two halves ship together.** An earlier
|
|
1046
|
+
attempt removed the renderer's alias ladder on its own. The producer kept
|
|
1047
|
+
spreading the authored block flat onto the generated `object-calendar` node, so
|
|
1048
|
+
the alias still arrived, nothing read it, and the author met a generic "Calendar
|
|
1049
|
+
configuration required" screen naming keys they had not written. A live authoring
|
|
1050
|
+
path stopped rendering with every gate green (recorded on objectui#8651). The
|
|
1051
|
+
refusal at the declaration is what turns that silence into a loud, by-name
|
|
1052
|
+
failure — so the ladder removal, the producer strip and the tombstones are one
|
|
1053
|
+
change and must stay one.
|
|
1054
|
+
|
|
1055
|
+
**The four moving parts:**
|
|
1056
|
+
|
|
1057
|
+
- `@object-ui/types` — `aliasKeyRefusal()` arms on the view-level `calendar`
|
|
1058
|
+
block, on the legacy `options.calendar` nesting (a check, since an open record
|
|
1059
|
+
declares no member — `custom` there, `invalid_type` at the declared slot), on
|
|
1060
|
+
the flat `object-calendar` node face, and on that element's own `calendar`
|
|
1061
|
+
container; plus a `.check()` on `ObjectViewSchema` for a named view's two
|
|
1062
|
+
nestings. `z.input` of each arm is `undefined`, so the TypeScript face carries
|
|
1063
|
+
`dateField?: never` / `endField?: never` and `tsc` refuses the key at the
|
|
1064
|
+
authoring site as well.
|
|
1065
|
+
- `@object-ui/plugin-calendar` — `getCalendarConfig` reads the declared
|
|
1066
|
+
spellings only; the `CalendarAliasRungs` cast target and both read sites are
|
|
1067
|
+
gone, and the config memo's dependency list loses the two entries with them.
|
|
1068
|
+
- `@object-ui/plugin-list` — the `calendar` branch destructures the two
|
|
1069
|
+
spellings out of the merged block and spreads only the remainder onto the node,
|
|
1070
|
+
exactly as the kanban branch strips its own stray `groupBy` (objectui#8365).
|
|
1071
|
+
- `@object-ui/plugin-view` — **the SECOND route**, and the reason this entry
|
|
1072
|
+
moved four packages rather than three. `generateViewSchema` runs when no host
|
|
1073
|
+
supplied `renderListView` — the authored `object-view` element — so a named
|
|
1074
|
+
view never passes through `ListView`. Its calendar branch spread the authored
|
|
1075
|
+
block raw, so removing the renderer's ladder turned a document that DREW into
|
|
1076
|
+
one that shows "Calendar configuration required" and nothing else. Measured end
|
|
1077
|
+
to end before the fix was written. It now strips the two spellings like its
|
|
1078
|
+
sibling, and the `ObjectViewSchema` check is what makes an authored alias loud
|
|
1079
|
+
there instead of mute.
|
|
1080
|
+
|
|
1081
|
+
⚠️ **`ObjectViewSchema.listViews` stays UNMIRRORED, and this entry does not
|
|
1082
|
+
change that.** That key waits on a maintainer decision about its VALUE TYPE —
|
|
1083
|
+
neither the spec's strict `ObjectListViewSchema` nor the local `NamedListView`
|
|
1084
|
+
can be the declared value without losing documented behaviour or enforcing 43
|
|
1085
|
+
unread members. A `.check()` decides none of it: it declares no value type, puts
|
|
1086
|
+
no key in the object's `shape`, requires no `columns`, and refuses neither an
|
|
1087
|
+
undeclared key nor the legacy `options` bag. It judges exactly the two spellings
|
|
1088
|
+
this entry retires, at the two nestings the producer merges — with pins on each
|
|
1089
|
+
of those non-effects.
|
|
1090
|
+
|
|
1091
|
+
⚠️ **Dated note, 2026-09-28 — `listViews` has since been mirrored — objectui#7928.** Later
|
|
1092
|
+
in this same release the maintainer's ruling A chose the value type this paragraph waits
|
|
1093
|
+
on: `ObjectViewSchema.listViews` is now `@objectstack/spec`'s own
|
|
1094
|
+
`ViewSchema.shape.listViews`, a record of the strict `ObjectListViewSchema`, by reference
|
|
1095
|
+
on both faces. It is in the object's `shape`, a named view needs `columns`, and the
|
|
1096
|
+
record refuses an undeclared key and the legacy `options` bag whole. The named-view check
|
|
1097
|
+
described here and in the `@object-ui/types` bullet above now reads the protocol's own
|
|
1098
|
+
refusal at `listViews.KEY.calendar` and adds the pointer at the key; it no longer walks
|
|
1099
|
+
the `options.calendar` nesting, so "a named view's two nestings" and "at the two nestings
|
|
1100
|
+
the producer merges" now hold for the `calendar` block only.
|
|
1101
|
+
`.changeset/7928-listviews-by-reference-fold.md` (PR objectui#10821) states what ships;
|
|
1102
|
+
the text above is kept as the reading of this change.
|
|
1103
|
+
|
|
1104
|
+
⛔ **Not a producer-side fold.** Normalising `dateField` to `startDateField` in
|
|
1105
|
+
`ListView` (option A) was put to the director seat and refused as the end state:
|
|
1106
|
+
it keeps a second spelling alive at the producer, which is the lenient alias
|
|
1107
|
+
AGENTS.md #0.1 names. The aliased view therefore produces **no** date binding,
|
|
1108
|
+
and the author is told why at the door instead.
|
|
1109
|
+
|
|
1110
|
+
⚠️ **Upstream's refusal suggests the wrong canonical key for `dateField`, and it
|
|
1111
|
+
is a typo-distance suggester rather than a declaration.** Re-derived by running
|
|
1112
|
+
the installed pin (`@objectstack/spec` 17.4.0), with controls in the same pass:
|
|
1113
|
+
`CalendarConfigSchema`'s `strictObject` options carry `surface` and `history` and
|
|
1114
|
+
no `aliases` entry, so the protocol declares nothing about either spelling. The
|
|
1115
|
+
"Did you mean `dateField` → `endDateField`?" an author meets is a fallback
|
|
1116
|
+
`findClosestMatches` Levenshtein suggestion budgeted `max(2, floor(len/3))`:
|
|
1117
|
+
`endDateField` is 3 edits from `dateField` and inside its budget of 3, while
|
|
1118
|
+
`startDateField` is 5 and outside it — and `endField`, budgeted 2, reaches
|
|
1119
|
+
nothing, which is why it and a nonsense control key both draw no suggestion at
|
|
1120
|
+
all. A one-character typo of the canonical key does resolve to `startDateField`,
|
|
1121
|
+
so the suggester is working as designed; it simply has no opinion to offer about
|
|
1122
|
+
a legacy alias.
|
|
1123
|
+
|
|
1124
|
+
⇒ **nothing upstream says `dateField` means the end of an event.** The hazard is
|
|
1125
|
+
author-facing rather than contractual: someone who copies that suggestion writes
|
|
1126
|
+
`endDateField` and binds the END of an event to the date they meant as the START,
|
|
1127
|
+
which every layer accepts. Every objectui read site folds this spelling onto the
|
|
1128
|
+
**start** — the retired ladder did, `normalizeListViewSchema`'s `timeline` fold
|
|
1129
|
+
does, `resolveTimelineDateBinding` documents it as "the pre-#2231 alias for
|
|
1130
|
+
`startDateField`", and this package has published "Deprecated alias for
|
|
1131
|
+
startDateField" for releases. So these arms name `startDateField` and a control
|
|
1132
|
+
pin holds that line. The remedy upstream needs is an explicit `aliases` (or
|
|
1133
|
+
`guidance`) entry for the two spellings so the suggester never answers for them;
|
|
1134
|
+
that is upstream's formatter, on upstream's card, and is not fixed here.
|
|
1135
|
+
|
|
1136
|
+
⛔ **The timeline alias is NOT retired by this change.** `timeline.dateField`
|
|
1137
|
+
stays a live, accepted alias with live consumers (`normalizeListViewSchema` folds
|
|
1138
|
+
it onto `startDateField`, `ObjectView` reads it, and app-shell pins that it still
|
|
1139
|
+
renders). The card's own boundaries put the map / gantt / timeline / kanban
|
|
1140
|
+
ladders on their own cards; a pin in `@object-ui/types` asserts the timeline
|
|
1141
|
+
spelling still parses green, so a later sweep has to say so rather than carrying
|
|
1142
|
+
it in silently.
|
|
1143
|
+
|
|
1144
|
+
**Pinned** by `calendar-date-alias-refusal-8355.test.ts` (`@object-ui/types`, all
|
|
1145
|
+
five surfaces, the per-surface consequence split, the compile-time face, and the
|
|
1146
|
+
structural guard that `listViews` stays out of the object's `shape`),
|
|
1147
|
+
`ListView.calendarAliasRefused-8355.test.tsx` (`@object-ui/plugin-list`) and
|
|
1148
|
+
`ObjectView.calendarAliasRefused-8355.test.tsx` (`@object-ui/plugin-view`) — both
|
|
1149
|
+
reading the node their producer really emits through a spy registration, which is
|
|
1150
|
+
the only census that can see a key arriving through a spread — and the inverted
|
|
1151
|
+
ledger rows in `calendarUnionReads-8651.test.tsx` (`@object-ui/plugin-calendar`).
|
|
1152
|
+
|
|
1153
|
+
⚠️ **Dated note, 2026-09-28 — a named view now refuses `timeline.dateField`, and the
|
|
1154
|
+
shape guard is inverted — objectui#7928.** Later in this same release the protocol's
|
|
1155
|
+
strict named-view record refuses `timeline.dateField` on a named view as an unrecognized
|
|
1156
|
+
key; the `list-view` route still accepts it, and that route is the one the timeline pin
|
|
1157
|
+
reads. The structural guard in `calendar-date-alias-refusal-8355.test.ts` now asserts
|
|
1158
|
+
the opposite of "`listViews` stays out of the object's shape": `listViews` is a member
|
|
1159
|
+
of `ObjectViewSchema.shape`. `.changeset/7928-listviews-by-reference-fold.md` (PR
|
|
1160
|
+
objectui#10821) states what ships; the text above is kept as the reading of this change.
|
|
1161
|
+
- 704e695: **A stray `groupBy` in a view's kanban config no longer overrides the lane, and on a `list-view` document is now refused by name.**
|
|
1162
|
+
|
|
1163
|
+
`ListView`'s kanban branch destructured
|
|
1164
|
+
`columns`/`groupByField`/`groupField`/`cardFields`/`titleField` out of the merged
|
|
1165
|
+
kanban config and spread the **rest** *after* its own `groupBy: laneField`. A
|
|
1166
|
+
`groupBy` surviving in that bag therefore **overrode the lane the branch had just
|
|
1167
|
+
resolved**. Measured on a distinguishing fixture, not reasoned: with
|
|
1168
|
+
`options.kanban = { groupBy: 'LANE_FROM_STRAY_GROUPBY' }` against
|
|
1169
|
+
`kanban = { groupByField: 'LANE_FROM_CANONICAL' }`, the generated `object-kanban`
|
|
1170
|
+
node carried `groupBy: 'LANE_FROM_STRAY_GROUPBY'`. It read as latent only because
|
|
1171
|
+
the one producer that fed it wrote both spellings with the same value by
|
|
1172
|
+
construction; that producer was retired separately, and this closes the override
|
|
1173
|
+
itself.
|
|
1174
|
+
|
|
1175
|
+
Two halves, per the maintainer's ruling (option B — a silent re-grouping was the
|
|
1176
|
+
fallback and was **not** taken):
|
|
1177
|
+
|
|
1178
|
+
1. **The canonical lane wins.** `groupBy` joins the destructure, so the stray key
|
|
1179
|
+
can no longer reach the passthrough spread. A view that authored it now groups
|
|
1180
|
+
by whatever `groupByField` / `groupField` / the declared lifecycle field
|
|
1181
|
+
resolves.
|
|
1182
|
+
2. **The stray key is refused loudly, at the read door of the view.** This repo's
|
|
1183
|
+
`.passthrough()` `KanbanConfig` mirror (`@object-ui/types`) declares `groupBy`
|
|
1184
|
+
as a named alias refusal pointing at `groupByField`, in the same sentence
|
|
1185
|
+
shape `@objectstack/spec` already answers the sibling alias with — "Unrecognized
|
|
1186
|
+
key(s) on this kanban configuration: `groupBy`. Did you mean `groupBy` →
|
|
1187
|
+
`groupByField`?". The refusal lands on the `list-view` route: wherever
|
|
1188
|
+
`safeValidateSchema` validates a `list-view` document (objectui's own
|
|
1189
|
+
`objectui validate` runs it and prints the issue), and at the authoring site,
|
|
1190
|
+
where `tsc` refuses the declared `kanban.groupBy` (the inferred authoring face
|
|
1191
|
+
now carries `groupBy?: never`). The legacy `options.kanban` nesting — where the
|
|
1192
|
+
retired producer wrote, and so where stored views carry the key — takes the
|
|
1193
|
+
identical message through a check on that untyped bag. A named view's
|
|
1194
|
+
`listViews.KEY.kanban.groupBy` on an `object-view` document is not reached by
|
|
1195
|
+
this arm; objectui#10321 gives that route its own door, which refuses the key
|
|
1196
|
+
in either nesting with the same message.
|
|
1197
|
+
|
|
1198
|
+
⚠️ **Dated note, 2026-09-28 — a named view's `options.kanban` bag is refused whole —
|
|
1199
|
+
objectui#7928.** Later in this same release a named view became the protocol's strict
|
|
1200
|
+
`ObjectListViewSchema` record, by reference, which refuses its `options` bag by name
|
|
1201
|
+
(`unrecognized_keys` at `listViews.KEY`, naming `options`). So the objectui#10321 door no
|
|
1202
|
+
longer refuses the key "in either nesting with the same message": it still adds that
|
|
1203
|
+
message at `listViews.KEY.kanban.groupBy`, beside the protocol's own refusal of the key,
|
|
1204
|
+
and nothing reports `listViews.KEY.options.kanban.groupBy`. A stored body that
|
|
1205
|
+
`@object-ui/app-shell`'s `ViewPreview` relays has its `options.kanban` folded onto the
|
|
1206
|
+
`kanban` block first (`foldStoredListOptions`). The `list-view` route described above is
|
|
1207
|
+
unchanged, its `options.kanban` check included.
|
|
1208
|
+
`.changeset/7928-listviews-by-reference-fold.md` (PR objectui#10821) states what ships;
|
|
1209
|
+
the text above is kept as the reading of this change.
|
|
1210
|
+
|
|
1211
|
+
**Breaking, in the sense worth stating explicitly** (shipped `minor`: this repo
|
|
1212
|
+
never declares `major`, and `.changeset/config.json` puts every package in one
|
|
1213
|
+
`fixed` group, so levels cannot be split). Two behaviours change for **stored
|
|
1214
|
+
data**, which is why this is not a patch:
|
|
1215
|
+
|
|
1216
|
+
- a stored view authoring `kanban.groupBy` (either nesting) **re-points its lane**
|
|
1217
|
+
— it used to group by the stray key and now groups by the canonical binding, so
|
|
1218
|
+
its board may show different columns;
|
|
1219
|
+
- the same document, on the `list-view` route, now **fails validation** where it
|
|
1220
|
+
used to pass: any pipeline running `safeValidateSchema` over it (objectui's own
|
|
1221
|
+
`objectui validate` does) reports one issue naming the key and the replacement.
|
|
1222
|
+
|
|
1223
|
+
Honouring `groupBy` as a declared alias was never an option here:
|
|
1224
|
+
`@objectstack/spec`'s `KanbanConfigSchema` is a strict object of
|
|
1225
|
+
`columns` / `groupByField` / `summarizeField` and refuses it by name — re-measured
|
|
1226
|
+
on the pinned 17.4.0 with both controls firing — so legalising it would be a spec
|
|
1227
|
+
change, not a renderer widening (AGENTS.md #0.1).
|
|
1228
|
+
|
|
1229
|
+
`groupBy` on the generated `object-kanban` **node** is untouched: that is the live,
|
|
1230
|
+
canonical lane key `ObjectKanban` reads. Only the **view-level** kanban config
|
|
1231
|
+
spelling is refused. The `.passthrough()` itself is kept — an undeclared sibling
|
|
1232
|
+
key still rides through, which is pinned as a control.
|
|
1233
|
+
- 3a9ab02: `list-view`: retire the legacy `title` alias from the export-filename read, and pin the
|
|
1234
|
+
`rowActionDefs` exemption at both of `ListView`'s read sites (objectui#8653, the
|
|
1235
|
+
objectui#8327 family's `plugin-list` card).
|
|
1236
|
+
|
|
1237
|
+
**Two keys, two opposite exits — and the contract chose each one.**
|
|
1238
|
+
|
|
1239
|
+
`title` — **retired.** `ListView` resolved its export filename through
|
|
1240
|
+
`schema.label || (schema as any).title`. `@objectstack/spec/ui`'s `ListViewSchema`
|
|
1241
|
+
refuses `title` **by name** (`unrecognized_keys: ['title']`) while
|
|
1242
|
+
`ObjectGridPropsSchema` **accepts** it; `packages/types` mirrors the platform contract
|
|
1243
|
+
rather than ruling over it, so declaring `title` on `ListViewSchema` would have made
|
|
1244
|
+
this repo accept what the platform save gate rejects. That asymmetry is also why
|
|
1245
|
+
objectui#6639 could take the *declare* branch for `ObjectGridSchema.title` one package
|
|
1246
|
+
over and this site could not. A parse-based census of `apps/ examples/ content/` and
|
|
1247
|
+
`packages/` found **zero** `list-view` nodes authoring `title`, so the retirement costs
|
|
1248
|
+
no author a filename. Over that same corpus the instrument reports **three**
|
|
1249
|
+
`object-grid` nodes carrying the key: **two authored** ones, both in
|
|
1250
|
+
`content/docs/api/schema-reference.md`, plus one that is not authored at all —
|
|
1251
|
+
`packages/plugin-view/src/ObjectView.tsx` composes `title: schema.table?.title` onto a
|
|
1252
|
+
grid node it builds, so it is a producer writing the key rather than an author
|
|
1253
|
+
declaring it. `ObjectGrid`'s own `title` reads are untouched — they remain declared,
|
|
1254
|
+
ruled and read.
|
|
1255
|
+
|
|
1256
|
+
**Behaviour change, deliberate.** A list view authoring only `title` (no `label`) no
|
|
1257
|
+
longer contributes a view segment to the export filename; it exports as
|
|
1258
|
+
`<objectLabel|objectName>-<timestamp>.<ext>`. Migration: write `label`, which is the
|
|
1259
|
+
declared slot on both this repo's `ListViewSchema` and the platform's.
|
|
1260
|
+
|
|
1261
|
+
**Bug fixed in the same expression.** The `as any` was laundering a second defect:
|
|
1262
|
+
`X || any` collapses the whole expression to `any`, so an inline locale-map `label`
|
|
1263
|
+
(`{ en: 'Quarterly Review', zh: '季度复盘' }` — the shape `BaseSchema.label` has
|
|
1264
|
+
published since objectui#4580's revised Q1) reached `sanitizeFileNameBase` **unresolved**
|
|
1265
|
+
and exported as `account-[object Object]-….csv`. The read now goes through the spec's
|
|
1266
|
+
own `resolveI18nLabel` against the display locale, the same resolver and argument order
|
|
1267
|
+
`ObjectGrid` already used at its twin site.
|
|
1268
|
+
|
|
1269
|
+
`rowActionDefs` — **exempt, still read, and now pinned.** objectui#5091 ruled this
|
|
1270
|
+
producer-derived key NON-AUTHOR SURFACE for `object-grid` on 2026-08-19. The exemption
|
|
1271
|
+
extends to `ListView`: the platform refuses the key by name on **both** surfaces,
|
|
1272
|
+
`@object-ui/types` declares it on neither mirror, and the producer is the same one
|
|
1273
|
+
(`app-shell`'s `ObjectView` derives it from `objectDef.actions` filtered by
|
|
1274
|
+
`locations.includes('list_item')`; `plugin-view`'s composes the same key onto a
|
|
1275
|
+
`list-view` node). Both read sites now carry the ruling in a docblock, and a new pin
|
|
1276
|
+
asserts — at runtime, never by source grep — that the defs still reach the child
|
|
1277
|
+
`object-grid` node **and** that a field named only by a row action's `visible` CEL still
|
|
1278
|
+
reaches `$select`. Without that second half, deleting the read would have returned rows
|
|
1279
|
+
whose predicate operand was never selected: objectui#3501's fail-closed CEL fault
|
|
1280
|
+
arriving with a success receipt.
|
|
1281
|
+
|
|
1282
|
+
No published type changed: neither key is declared on any face by this change.
|
|
1283
|
+
- 502eb58: The row/card click props on the view components now declare the modifier
|
|
1284
|
+
payload they have always been invoked with (objectui#9357).
|
|
1285
|
+
|
|
1286
|
+
`useNavigationOverlay`'s `handleClick` invokes the handler it is given with two
|
|
1287
|
+
arguments — the record, and an optional modifier payload (`metaKey` / `ctrlKey`
|
|
1288
|
+
/ `button`) a host needs to implement Cmd/Ctrl/middle-click. objectui#9360 made
|
|
1289
|
+
the hook's own option say so. These components pass a host-supplied prop
|
|
1290
|
+
straight into that option, so the value a host writes against them is invoked
|
|
1291
|
+
with two arguments too — and every one of these props declared only the record.
|
|
1292
|
+
The payload was therefore invisible on the one line a host reads, exactly as it
|
|
1293
|
+
had been on the hook.
|
|
1294
|
+
|
|
1295
|
+
Thirteen declarations across nine packages now name both parameters:
|
|
1296
|
+
|
|
1297
|
+
- `ObjectGridComponentProps.onRowClick`
|
|
1298
|
+
- `ObjectKanbanComponentProps.onRowClick` (its sibling `onCardClick`, the other
|
|
1299
|
+
arm of the same fallback, already declared both)
|
|
1300
|
+
- `KanbanRendererProps.schema.onCardClick`
|
|
1301
|
+
- `ObjectCalendar`, `ObjectGantt`, `ObjectMap`, `ObjectTree`: `onRowClick`
|
|
1302
|
+
- `ObjectTimeline`: `onRowClick` and `onItemClick`, the two arms of one fallback
|
|
1303
|
+
- `ListView.onRowClick`
|
|
1304
|
+
- `ObjectGallery`: `onRowClick` and `onCardClick`, likewise two arms of one
|
|
1305
|
+
- `RelatedList.onRowClick`
|
|
1306
|
+
|
|
1307
|
+
**Source-compatible, and with no refused class.** A one-parameter handler is
|
|
1308
|
+
still assignable to the widened signature, and a handler written against the
|
|
1309
|
+
widened signature was already assignable to the narrow one — its minimum
|
|
1310
|
+
argument count is still one. The second parameter is spelled `any` rather than
|
|
1311
|
+
`HandleClickModifiers`, which is the difference that matters for callers: the
|
|
1312
|
+
hook's own option names that interface and therefore refuses a handler whose
|
|
1313
|
+
second parameter is annotated narrower (objectui#9360 documents that class and
|
|
1314
|
+
the one-line remedy). These faces refuse nothing. A host that discovered the
|
|
1315
|
+
payload from the implementation and annotated it `React.MouseEvent` — which is
|
|
1316
|
+
what actually arrives — keeps compiling. `any` is also the spelling
|
|
1317
|
+
`ObjectKanbanSchema.onCardClick` already carries for this same payload
|
|
1318
|
+
(objectui#9341) and the one `BaseSchema`'s own `onClick` / `onChange` /
|
|
1319
|
+
`onSubmit` use, and it is forced on the published twins in `@object-ui/types`,
|
|
1320
|
+
which may not name a type that lives in a package depending on them.
|
|
1321
|
+
|
|
1322
|
+
**Runtime behaviour is unchanged.** Nothing is newly called and nothing newly
|
|
1323
|
+
passes an argument; only the declarations move.
|
|
1324
|
+
|
|
1325
|
+
Three declarations that share the name and the shape are deliberately NOT
|
|
1326
|
+
widened, because the value flowing through them is not invoked with the
|
|
1327
|
+
payload: `VirtualGrid.onRowClick`, whose second parameter is a row index;
|
|
1328
|
+
`ManageViewsDialog.onRowClick`, which receives a view id; and the `data-table`
|
|
1329
|
+
family (`DataTableSchema.onRowClick`, `ObjectDataTableSchema.onRowClick`),
|
|
1330
|
+
whose renderer invokes with one argument. Widening those would have declared a
|
|
1331
|
+
payload that never arrives.
|
|
1332
|
+
- de5d400: A page size with nothing declared is now the one `@objectstack/spec` declares for
|
|
1333
|
+
`pagination.pageSize`, on every surface that has a pager; a fetch that has no
|
|
1334
|
+
pager keeps its own fetch batch, which does not follow it (objectui#9853).
|
|
1335
|
+
|
|
1336
|
+
"Page size" now means one page, and only where there is a pager. The grid,
|
|
1337
|
+
the list and the board read the display default from the spec (`PaginationConfigSchema`)
|
|
1338
|
+
instead of keeping numbers of their own, so it is declared once, in the protocol.
|
|
1339
|
+
`@objectstack/spec` 17.5.0 declares **50**. The views that fetch one window and
|
|
1340
|
+
page nothing keep a fetch batch of their own, with the value they had, so no
|
|
1341
|
+
view loses records it could reach before.
|
|
1342
|
+
|
|
1343
|
+
⚠️ **Visible change where no page size is declared.** Measured on the renderer
|
|
1344
|
+
with `@objectstack/spec` 17.5.0. Declare `pagination: { pageSize: N }` (or, on a
|
|
1345
|
+
board, `limit`) to keep an old count:
|
|
1346
|
+
|
|
1347
|
+
| surface (nothing declared) | before | after |
|
|
1348
|
+
|:--|--:|--:|
|
|
1349
|
+
| `object-grid` that fetches its own rows: rows per page, and the `$top` of each page fetch | 50 | 50 (unchanged) |
|
|
1350
|
+
| `object-grid` over inline `data`: rows per page | 10 | **50** |
|
|
1351
|
+
| grouped `object-grid`: groups per page | 10 | **50** |
|
|
1352
|
+
| grouped `object-grid` over a data source that groups on the server: rows per group page (`$top`) | 50 | 50 (unchanged) |
|
|
1353
|
+
| `list-view`, grid view (paged on the server): rows per page, and `$top` | 100 | **50** |
|
|
1354
|
+
| `list-view`, every view it does not page (kanban, calendar, gallery, timeline and the rest, and a grouped grid): the one fetch's `$top` | 100 | 100 (unchanged) |
|
|
1355
|
+
| `object-kanban` with no `limit`: the board's `$top` | 100 | 100 (unchanged) |
|
|
1356
|
+
|
|
1357
|
+
What changed underneath:
|
|
1358
|
+
|
|
1359
|
+
- **`@object-ui/plugin-grid`.** `ObjectGrid` falls back to the spec's display
|
|
1360
|
+
default on every page it shows, with no local number behind it: if a future
|
|
1361
|
+
spec stops declaring the default, the module throws at load instead of
|
|
1362
|
+
inventing one. The old "server window" constant is renamed
|
|
1363
|
+
`DEFAULT_FETCH_BATCH_SIZE` and keeps its value. Since grouping moved to the
|
|
1364
|
+
server, it is read only when the grid groups a window of rows in the browser
|
|
1365
|
+
because the server does not group it (a grouping key the principal may not
|
|
1366
|
+
read), and never as a page size. The grouped view's rows-per-page selector
|
|
1367
|
+
also lists the size in force when it is not one of its fixed steps.
|
|
1368
|
+
- **`@object-ui/plugin-list`.** `ListView` splits its undeclared fallback by
|
|
1369
|
+
whether the view pages. The grid view, which it pages on the server, falls
|
|
1370
|
+
back to the spec's display default. Every other view falls back to a fetch
|
|
1371
|
+
batch of 100, the value it had. A declared `pagination.pageSize` sizes the
|
|
1372
|
+
window on every view, as before. With no declared size, switching between the
|
|
1373
|
+
grid view and an unpaged view now changes the window, so the list fetches
|
|
1374
|
+
again: one request, whose `$top` is the fetch batch. That qualifies the
|
|
1375
|
+
objectui#10512 note in this same release, which says a switch into a dataset
|
|
1376
|
+
chart with no user filter set issues no new request: it still holds when a
|
|
1377
|
+
page size is declared. A refused page size still warns, and the warning names
|
|
1378
|
+
the fallback that view actually used.
|
|
1379
|
+
- **`@object-ui/plugin-kanban`.** The board's default `limit` is renamed from
|
|
1380
|
+
`DEFAULT_KANBAN_LIMIT` to `DEFAULT_KANBAN_FETCH_BATCH_SIZE` and stays 100. Its
|
|
1381
|
+
diagnostic for a refused `limit` now calls it the board's fetch batch.
|
|
1382
|
+
|
|
1383
|
+
The display default follows the `@objectstack/spec` a host installs. A host
|
|
1384
|
+
that resolves an older spec gets that spec's default, on every surface alike.
|
|
1385
|
+
|
|
1386
|
+
Released as `minor`, not `major`: this repository keeps its major aligned with
|
|
1387
|
+
`@objectstack/spec`, so the behaviour change is spelled out here instead.
|
|
1388
|
+
- e2e8e68: **Behaviour change:** the spec's view-level `map` block on a list view is now read at
|
|
1389
|
+
runtime. `ListMapConfigSchema` (objectstack#9340) has been authorable and validated since
|
|
1390
|
+
the `@objectstack/spec` 17.1.0 pin — it flows into this repo's own `ListViewSchema` by
|
|
1391
|
+
reference — but nothing consumed it: `ListView`'s `case 'map'` forwarded only the legacy
|
|
1392
|
+
`schema.options.map` bag, so declaring `map: { titleField: 'title', locationField:
|
|
1393
|
+
'location' }` on a view changed nothing and marker titles fell back to the renderer's
|
|
1394
|
+
placeholder.
|
|
1395
|
+
|
|
1396
|
+
The block now reaches `plugin-map` and drives every one of its seven reads — coordinate
|
|
1397
|
+
extraction, marker title and description, and the initial camera. Precedence follows the
|
|
1398
|
+
convention the sibling visualization blocks in the same file already set: the view-level
|
|
1399
|
+
block wins over `options.map`, per key, exactly as `kanban` / `calendar` / `gallery` /
|
|
1400
|
+
`timeline` / `gantt` each merge their spec config over the legacy bag. Both sources go
|
|
1401
|
+
through the existing objectui#5177 key whitelist, and the branch still emits the flat
|
|
1402
|
+
form, so `getMapConfig`'s objectui#5018 precedence rule ("neither flattener emits a `map`
|
|
1403
|
+
key at all") stays true.
|
|
1404
|
+
|
|
1405
|
+
The visualization switcher had the same gap with a sharper consequence: the capability
|
|
1406
|
+
gate that decides which visualizations are offered also read `options.map` alone, so a
|
|
1407
|
+
view binding its coordinates in the spec block was filtered out of its own
|
|
1408
|
+
`appearance.allowedVisualizations` and fell back to `['grid']`. The gate now asks the same
|
|
1409
|
+
merged config the render seam forwards, so the two cannot disagree — including for a
|
|
1410
|
+
binding split across the two sources.
|
|
1411
|
+
|
|
1412
|
+
`InterfaceListPage` (ADR-0047 interface pages) forwards the referenced view's `map` block
|
|
1413
|
+
for the same reason. It is passed alongside the auto-derived `options.map` rather than
|
|
1414
|
+
replacing it, so a partial authored block — `map: { titleField: 'title' }` — keeps the
|
|
1415
|
+
derived coordinate binding instead of dropping it.
|
|
1416
|
+
|
|
1417
|
+
No defaults are introduced for `zoom` / `center`: an undeclared camera stays undeclared,
|
|
1418
|
+
so the fit-to-queried-records behaviour ruled in objectui#5000 is unchanged.
|
|
1419
|
+
- ca39427: Derive `ViewType` from `@objectstack/spec` instead of re-declaring it
|
|
1420
|
+
|
|
1421
|
+
`ViewType` and its zod face `ViewTypeSchema` were hand-written eleven-arm copies of
|
|
1422
|
+
the spec's list-view type vocabulary. `@objectstack/spec@17.3.0` added `page` and
|
|
1423
|
+
neither followed, so every structure keyed on them stayed total over the copy and
|
|
1424
|
+
compiled green while being incomplete — including the one whose doc comment promises
|
|
1425
|
+
that "a kind added to the union fails the build HERE". A spec-valid `type: 'page'`
|
|
1426
|
+
view was left unresolved and fell back to a grid with no error, no warning and no
|
|
1427
|
+
console line, while the published validator accepted it.
|
|
1428
|
+
|
|
1429
|
+
Both faces now derive from `@objectstack/spec/ui` `ListView['type']`, and the
|
|
1430
|
+
renderer-side structures derive from that in turn:
|
|
1431
|
+
|
|
1432
|
+
- `@object-ui/core` exports `ListViewVisualization` and `isListViewVisualization` —
|
|
1433
|
+
the visualizations `ListView` draws, which deliberately exclude `page` for the same
|
|
1434
|
+
reason the spec's own `VisualizationType` does (a `type: 'page'` view mounts a
|
|
1435
|
+
published page through `pageName` rather than drawing records).
|
|
1436
|
+
- A spec-valid but undrawable kind still falls back to a grid, but now warns once
|
|
1437
|
+
instead of degrading identically to a typo.
|
|
1438
|
+
|
|
1439
|
+
Widened surfaces: `ViewType` / `ViewTypeSchema` gain `page`; `UnifiedViewType` gains
|
|
1440
|
+
`tree` (it had drifted); the `list-view` SDUI registry offers `chart` and `tree`,
|
|
1441
|
+
which the renderer has drawn for releases.
|
|
1442
|
+
|
|
1443
|
+
⚠️ Consumers holding an exhaustive `switch` or a total `Record<ViewType, …>` will
|
|
1444
|
+
now fail to compile until they account for `page`. That failure is the point of the
|
|
1445
|
+
change — it is the guarantee that was silently lost.
|
|
1446
|
+
|
|
1447
|
+
⚠️ **Dated note, 2026-09-29 — `page` left the vocabulary again in this same release — objectui#11073.**
|
|
1448
|
+
Later in this release this repository began resolving `@objectstack/spec` 17.5.0, which removed `type: 'page'` from the list-view vocabulary (ADR-0049 enforce-or-remove). Because `ViewType` and `ViewTypeSchema` are derived from the spec, as this change made them, they no longer carry `page`. A `type: 'page'` view is refused by name at parse rather than being "spec-valid", and the one-time "cannot draw" warning has no kind left to explain. The rest of this change stands: the vocabulary still follows the spec, now in the narrowing direction.
|
|
1449
|
+
- 2d36552: Pins `@objectstack/spec`, `@objectstack/client`, `@objectstack/formula` and `@objectstack/lint` to `17.1.0`, and adapts the two consumer surfaces the new build moves.
|
|
1450
|
+
|
|
1451
|
+
The pin itself is a lockfile refresh — every manifest already declared `^17.0.0`, which admits `17.1.0`, so no dependency range changed. All four move together: a split resolution is what produced the dual-version spec graph that reddened `check:spec-symbols` in this repo's history.
|
|
1452
|
+
|
|
1453
|
+
**A `icontains` filter now reaches the driver as a filter.** `icontains` is a canonical `VIEW_FILTER_OPERATORS` member as of `17.1.0`, so an author can declare it on a `ViewFilterRule` and the spec validates it — but `@object-ui/data-objectstack`'s alias table had no row for it, and an unmapped operator is how this adapter shipped an unfiltered query before (objectstack#3948). It is an identity row like `contains`: `icontains` is itself a member of `VALID_AST_OPERATORS`, so the spelling the author writes is the spelling the AST takes, and no case-sensitivity is translated away. Declared rather than left to the table's `?? op` fall-through, on the rule its own parity test states — the AST gate accepting a spelling is not the driver compiling it into a `WHERE` clause.
|
|
1454
|
+
|
|
1455
|
+
The same operator reaches the list view's own bridge: `@object-ui/plugin-list`'s `mapOperator` gains an explicit `icontains` arm. The emitted spelling is identical to the input, but the arm is written out rather than left to the `default` passthrough — `icontains` is its own member of `VALID_AST_OPERATORS`, so a raw passthrough is accepted *today*, and depending on that coincidence is what the bridge's own parity test records as how it once stopped discriminating.
|
|
1456
|
+
|
|
1457
|
+
`@object-ui/core` adds `onSuccess` to its spec key inventory, so an author writing the key `17.1.0` now declares is no longer warned that it is unknown. That is a diagnostic statement only — the four declared action surfaces still drop the key before it reaches the runner, which is tracked separately.
|
|
1458
|
+
|
|
1459
|
+
**A stored view filtering case-insensitively still shows that operator when it is reopened.** `@object-ui/plugin-view`'s canonical-to-builder table is keyed by `ViewFilterOperator`, so `17.1.0` adding `icontains` failed to compile rather than letting the operator reach the FilterBuilder as a raw spelling its dropdown cannot select. It maps to the builder's `containsCaseInsensitive` — the id that authors the spec's `$icontains` — and deliberately not to `contains`, which would quietly rewrite a case-insensitive filter into a case-sensitive one the next time the view was saved.
|
|
1460
|
+
|
|
1461
|
+
**The page-editor palette keeps one entry per renderer.** `17.1.0` retires `element:filter` from `PageComponentType` and adds `record:discussion`, leaving the member count at 34 either side — so the swap is invisible to any count-based reading. The stale `element:filter` exclusion is dropped, and `record:discussion` is excluded because it is the *same renderer* as the already-offered `record:chatter`, not because it is unauthorable. Nothing the palette offers changes.
|
|
1462
|
+
|
|
1463
|
+
**The console eager-closure ceiling is re-baselined, by maintainer ruling.** The release is roughly 930 KB larger uncompressed and nearly all of it lands in `vendor-objectstack-*.js`, which put the closure past a ceiling that was deliberately sized to catch a 89 KiB regression — the gate refused the bump, correctly. Raising it was escalated rather than taken locally, because gate-strength policy had been ruled the maintainer's; the ruling on objectui#5531 authorised the raise. `MAX_EAGER_CLOSURE_GZIP_BYTES` and the `BASELINE` it is derived from move together in one commit, keeping headroom at 2.00% and below the 91,136-byte regression size the gate must still catch. The gate's *sensitivity* is untouched: a repeat of that regression from the new baseline still fails. No behaviour ships from this file — it is CI policy, recorded here because the version it governs is the one this changeset publishes.
|
|
1464
|
+
|
|
1465
|
+
### Patch Changes
|
|
1466
|
+
|
|
1467
|
+
- 0427036: On a `gantt` list view, the toolbar's Filter control and the `UserFilters` chips now
|
|
1468
|
+
narrow the chart (objectui#10037).
|
|
1469
|
+
|
|
1470
|
+
Both controls were offered on a gantt view and changed nothing the user could see.
|
|
1471
|
+
`ListView`'s own fetch applies the authored filter, the toolbar's filter group and the
|
|
1472
|
+
chips together, but the gantt chart does not draw those rows: it queries for itself —
|
|
1473
|
+
its registered renderer takes only the node's `schema` — and the node it was handed
|
|
1474
|
+
carried the authored `filter` alone.
|
|
1475
|
+
|
|
1476
|
+
The gantt node now carries the same effective filter `ListView`'s own fetch sends, so the
|
|
1477
|
+
chart's query honours both controls. The value keeps its identity while its content is
|
|
1478
|
+
unchanged, so re-rendering the list does not re-query the chart. The row ceiling from
|
|
1479
|
+
objectui#7210 is untouched.
|
|
1480
|
+
- af243c1: On `tree` and `chart` list views, the toolbar's Filter control and the `UserFilters`
|
|
1481
|
+
chips now narrow the view, and on a `gantt` list view the toolbar's Search box now
|
|
1482
|
+
narrows the chart (objectui#10250).
|
|
1483
|
+
|
|
1484
|
+
These three views run their own query instead of drawing the rows `ListView` fetched.
|
|
1485
|
+
The tree and chart nodes carried only the authored `filter`, and the gantt node carried
|
|
1486
|
+
no search term, so each control changed `ListView`'s own fetch and nothing on screen.
|
|
1487
|
+
|
|
1488
|
+
- The `tree` node and the object-bound `chart` node now carry the same effective filter
|
|
1489
|
+
`ListView`'s own fetch sends, as the `gantt` node has since objectui#10037. The value
|
|
1490
|
+
keeps its identity while its content is unchanged, so re-rendering the list does not
|
|
1491
|
+
re-query the view.
|
|
1492
|
+
- `ObjectGanttSchema` declares two optional keys, `search` and `searchableFields`.
|
|
1493
|
+
`ObjectGantt` sends `search` as `$search` and, only alongside a term,
|
|
1494
|
+
`searchableFields` as `$searchFields` — the pair a list's own query sends. A `gantt`
|
|
1495
|
+
list view writes both from its toolbar Search box and the view's `searchableFields`.
|
|
1496
|
+
|
|
1497
|
+
Unchanged: the toolbar state still does not reach the query of a `chart` view bound to a
|
|
1498
|
+
semantic `dataset`, and the toolbar Search box still does not reach the `tree` or `chart`
|
|
1499
|
+
query. objectui#10326 stops offering that Search box on `tree` and `chart` views.
|
|
1500
|
+
- fb7f38b: fix(plugin-list): a list view whose every authored column is denied by field-level security still sends a `$select`
|
|
1501
|
+
|
|
1502
|
+
`ListView`'s `$select` builder drops the authored columns the principal cannot read. When that left no column, it returned no projection, so the request carried no `$select` key and asked the server for every field of the object, the denied ones included. An emptied column list was read as "no restriction", the reading objectui#7215 fixed on `$expand` (objectui#10275).
|
|
1503
|
+
|
|
1504
|
+
The builder now asks whether the author declared any column, not whether any column survived the permission gate. An authored list that field-level security empties projects to `id` plus the fields the builder already adds through its FLS-gated routes (the platform columns every object carries excepted, as before): the `$expand` roots, the view bindings (kanban, calendar, gallery, timeline and gantt fields), the grid's grouping fields, and the operands of the row predicates (conditional formatting; the `visible` / `disabled` predicates and `recordIdField` of the row actions, the bulk actions and the object's declared actions; the object's `userActions` overrides). A denied operand is not added back. That is the shape `ObjectGrid` and `RelatedList` (objectui#10186) send.
|
|
1505
|
+
|
|
1506
|
+
Graded as defence in depth: ObjectStack's server strips denied fields from every returned row (`FieldMasker`), so nothing leaked from that backend. The projection matters for a backend that does not strip.
|
|
1507
|
+
|
|
1508
|
+
Unchanged: a list with no authored `columns` (absent or an empty array) still sends no projection, a partially-denied list projects its surviving columns as before, and nothing is filtered before the permission answer has loaded.
|
|
1509
|
+
|
|
1510
|
+
Behaviour to know about: on a backend that honours `$select`, a list whose every column is denied now receives rows carrying only the projected fields, as a list with one readable column already did.
|
|
1511
|
+
- 59636b3: On `tree` and `chart` list views, the toolbar no longer offers Search (objectui#10326).
|
|
1512
|
+
|
|
1513
|
+
Both views draw what they query for themselves, and neither query carries a search
|
|
1514
|
+
term: the tree's own `find` sends its filter, its row ceiling and its expansions, and
|
|
1515
|
+
the chart's queries, aggregate or dataset, take no term at all. A term typed there
|
|
1516
|
+
changed `ListView`'s own fetch, behind the record count and the export, and nothing
|
|
1517
|
+
the view drew.
|
|
1518
|
+
|
|
1519
|
+
- The Search control is not rendered while a `tree` or `chart` view is on screen,
|
|
1520
|
+
whatever the chart is bound to. An authored `userActions.search: true` does not bring
|
|
1521
|
+
it back on those views. Grid, kanban, calendar, gallery, timeline, map and gantt views
|
|
1522
|
+
keep it as before.
|
|
1523
|
+
- A term that is already set, whether the host restored it at mount through
|
|
1524
|
+
`initialSearchTerm` or the user typed it on another view before switching, is not
|
|
1525
|
+
applied to `ListView`'s own fetch or export while a `tree` or `chart` view is on
|
|
1526
|
+
screen. The term is kept rather than cleared: switching back to a view that offers
|
|
1527
|
+
Search applies it again, and `onSearchChange` is not called for the switch.
|
|
1528
|
+
- 5e0af5a: `ListView` no longer strands a server-paginated grid on a page a write has emptied. When a refetch comes back with zero rows past page 1 and the server's `total` says the last page is lower, the pager steps back to that last page and fetches it, showing a loading state in between. Before, bulk-deleting every row of the last page (or a concurrent delete) left the list on that page with the first-run "no records yet" empty state, although earlier pages still had records. A write that leaves the current page non-empty keeps the page, and an object with no records still shows the first-run empty state on page 1.
|
|
1529
|
+
- 6d1e77c: On a `chart` list view bound to a semantic `dataset`, a user filter that is already set
|
|
1530
|
+
no longer narrows `ListView`'s own fetch (objectui#10512).
|
|
1531
|
+
|
|
1532
|
+
The toolbar withholds the Filter builder and the `userFilters` chips on such a view
|
|
1533
|
+
(objectui#10327), but a filter group the host restored at mount through
|
|
1534
|
+
`initialFilters`, or a group or chip set on another view before switching, was still
|
|
1535
|
+
applied to `ListView`'s own fetch of the object's rows. That fetch still runs on the
|
|
1536
|
+
chart and feeds the record-count bar, so the count was narrowed by a filter nothing on
|
|
1537
|
+
screen showed or could clear.
|
|
1538
|
+
|
|
1539
|
+
- While a dataset-bound chart is on screen, `ListView`'s fetch and its export apply
|
|
1540
|
+
the view's own `filter` only; the toolbar's filter group and the chips are not sent.
|
|
1541
|
+
- The held state is kept rather than cleared: switching back to a view that offers the
|
|
1542
|
+
controls applies the same group again, and `onFilterChange` is not called for the
|
|
1543
|
+
switch.
|
|
1544
|
+
- A chart bound to the list object, and every other view, apply the toolbar's filter
|
|
1545
|
+
group and the chips as before. A switch into a dataset chart with no user filter set
|
|
1546
|
+
issues no new request.
|
|
1547
|
+
- dddbc27: fix(plugin-list): list filters label a lookup's values by the `displayField` the object definition declares (objectui#10545)
|
|
1548
|
+
|
|
1549
|
+
Once a list view's object definition loads, the filter builder's candidates and
|
|
1550
|
+
the toolbar's lookup filter read each lookup field's display field off that
|
|
1551
|
+
definition. They read `display_field`, then `reference_field`, and never
|
|
1552
|
+
`displayField`, the only one of the three `@objectstack/spec`'s `FieldSchema`
|
|
1553
|
+
declares. So a spec-compliant lookup's display field never reached the value
|
|
1554
|
+
picker, which fell back to its default, `name`.
|
|
1555
|
+
|
|
1556
|
+
Both now read `displayField`, and only it. This is the same single spelling
|
|
1557
|
+
`@object-ui/plugin-grid` reads off a definition. `FieldSchema` refuses the other
|
|
1558
|
+
two: `display_field` with a rename to `displayField`, and `reference_field` with
|
|
1559
|
+
a pointer to `referenceVia`.
|
|
1560
|
+
|
|
1561
|
+
Behaviour change: a field definition that carries only the refused
|
|
1562
|
+
`display_field` no longer sets the picker's display field, and neither does one
|
|
1563
|
+
that carries only `reference_field`. The picker falls back to `name`. A stored
|
|
1564
|
+
definition served through the ObjectStack adapter still works, because that
|
|
1565
|
+
adapter folds `display_field` onto `displayField` when the schema is loaded.
|
|
1566
|
+
A definition that reaches the list any other way, such as a host `DataSource`
|
|
1567
|
+
or an `objectDef` passed straight to `UserFilters`, needs `displayField`.
|
|
1568
|
+
|
|
1569
|
+
The two readers also stop reading `id_field` as the lookup's id column.
|
|
1570
|
+
`FieldSchema` declares no id column for a lookup and refuses both `id_field` and
|
|
1571
|
+
`idField`, so the picker now always keys a lookup by `id`, its default. A
|
|
1572
|
+
definition that carried `id_field` used to make the picker match and emit that
|
|
1573
|
+
column's values instead.
|
|
1574
|
+
|
|
1575
|
+
Also (objectui#10547): before a list view's object definition loads, the filter
|
|
1576
|
+
builder's candidates come from the view's declared `columns`, and that fallback
|
|
1577
|
+
no longer reads `options` off a list column. `ListColumnSchema` refuses the key
|
|
1578
|
+
with `unrecognized_keys`, so a spec-compliant view never carries it. A select
|
|
1579
|
+
field's options now reach the filter builder only from the object definition,
|
|
1580
|
+
once it loads, which is where they already came from after the load.
|
|
1581
|
+
- 4a3d500: `list-view` re-reads its rows when the data-invalidation bus (`notifyDataChanged` from `@object-ui/react`) reports a change to the object it queries (objectui#10572). A write that bypasses the data source — a page action over raw HTTP, a flow, a server action — fires no `onMutation`, so a list on a page used to show it only when the whole page was remounted. The re-read keeps the list mounted; rows a host hands the list inline are still the host's to refresh.
|
|
1582
|
+
- 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.
|
|
1583
|
+
|
|
1584
|
+
(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.)
|
|
1585
|
+
|
|
1586
|
+
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.
|
|
1587
|
+
|
|
1588
|
+
`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.
|
|
1589
|
+
|
|
1590
|
+
`@object-ui/i18n`: new keys `chart.refreshing` and `dashboard.refreshing` in every built-in locale pack.
|
|
1591
|
+
- 0c3f70a: A directly authored `object-grid` or `list-view` node resolves the spec's context tokens in its own filter (objectui#10607).
|
|
1592
|
+
|
|
1593
|
+
`filter: [['owner', '=', '{current_user_id}']]` on an `object-grid` or `list-view` node authored
|
|
1594
|
+
straight into a page, not reached through `object-view`, went out on `$filter` as the literal
|
|
1595
|
+
token: neither package called `@object-ui/core`'s `resolveFilterPlaceholders`. Each now resolves
|
|
1596
|
+
the node's own filter once through that shared resolver, with the session scope the host provides
|
|
1597
|
+
through `useFilterScope` (the console shell mounts `FilterScopeProvider` from the signed-in user
|
|
1598
|
+
and the active organization). On `object-grid` that is `filter` and its deprecated alias
|
|
1599
|
+
`defaultFilters`: the resolved `filter` feeds the grid's query and its server-side export, and the
|
|
1600
|
+
resolved alias feeds the query when `filter` is absent. On
|
|
1601
|
+
`list-view` it is `filter`, and the resolved value feeds the list's query, the child view it
|
|
1602
|
+
renders, the self-querying views (gantt, tree, chart), the export and the empty-state copy.
|
|
1603
|
+
`object-view` has done this since objectui#10506; these two nodes, authored directly, had not.
|
|
1604
|
+
|
|
1605
|
+
The relative-date macros (`{today}`, `{current_quarter_start}`, …) resolve in the same call, in
|
|
1606
|
+
the browser's local time, as on the other surfaces that call the resolver. Where they used to
|
|
1607
|
+
reach a backend that resolves them itself, as the ObjectStack server does in the tenant's
|
|
1608
|
+
configured `localization.timezone` (UTC by default), a browser whose local day differs from the
|
|
1609
|
+
server's day can now get a different day.
|
|
1610
|
+
|
|
1611
|
+
A token the scope cannot resolve is left as written and the resolver warns once, which is the
|
|
1612
|
+
resolver's own rule, so the filter is never widened.
|
|
1613
|
+
|
|
1614
|
+
The resolved value is held against its inputs: the authored filter, compared by structure, and the
|
|
1615
|
+
scope's three members (`currentUserId`, `currentOrgId`, `onUnresolved`), each compared on its own.
|
|
1616
|
+
A re-render that changes none of them does not refetch, including one that rebuilds an equal
|
|
1617
|
+
filter inline; a new signed-in user refetches with the new id. Date macros are therefore resolved
|
|
1618
|
+
once and held until the scope or the authored filter changes, and a refetch for another reason
|
|
1619
|
+
reuses the held value. `object-view` already hands these nodes a filter it has resolved
|
|
1620
|
+
(objectui#10506); resolving that value again changes nothing, because a resolved id or date no
|
|
1621
|
+
longer matches the placeholder pattern.
|
|
1622
|
+
|
|
1623
|
+
On `object-grid`, the load effect now also refetches when the resolved `defaultFilters` changes (a
|
|
1624
|
+
new signed-in user, or an in-place edit of the alias). It read that alias without keying on it
|
|
1625
|
+
before.
|
|
1626
|
+
- 02e6d36: Five more page blocks re-read when the data-invalidation bus (`notifyDataChanged` from `@object-ui/react`) reports a write to the object they read (objectui#10623). These blocks read neither `onMutation` nor the bus, so any write — through the data source or past it (a page action over raw HTTP, a flow, a server action) — showed only when something remounted them. Each fetch effect now names the `useDataInvalidation` nonce for its object; an unscoped change (`'*'`) matches too, and a change to another object does not.
|
|
1627
|
+
|
|
1628
|
+
- `@object-ui/plugin-timeline`: `object-timeline` re-reads its rows; a canvas that already shows rows stays mounted while the re-read runs.
|
|
1629
|
+
- `@object-ui/plugin-list`: `object-gallery` re-reads its cards; collapsed groups stay collapsed.
|
|
1630
|
+
- `@object-ui/plugin-map`: `object-map` re-reads the markers it is showing without its loading placeholder, so the map stays mounted and the camera stays where the user left it. If that re-read fails, the last markers stay on the map and the failure is logged to the console, as `ObjectGantt`'s background refresh does. A changed query still shows the placeholder and re-fits the camera, even when a bus event arrives with it. Two older faults in the same fetch are fixed as well: a slow earlier answer can no longer overwrite a newer one, and a load that succeeds now takes the map off an earlier error screen.
|
|
1631
|
+
- `@object-ui/components`: `element:number` re-reads its aggregate and `element:repeater` its rows.
|
|
1632
|
+
|
|
1633
|
+
Rows a host hands these blocks (`data`, `bind`, authored `items`, an inline `value` set) are still the host's to refresh; only the blocks' own queries follow the bus.
|
|
1634
|
+
- 12809a5: `ListView` hands its grid the object's field types together with the rows, so the grid draws a `password` / `secret` column masked from its first paint (objectui#10657, which folded objectui#10706).
|
|
1635
|
+
|
|
1636
|
+
`ListView` fetches the rows and hands them to `object-grid` as `data`, and those rows
|
|
1637
|
+
painted before the grid's own read of the object definition settled. In that window an
|
|
1638
|
+
untyped column over a `password` / `secret` field had no type: it drew the raw value, and
|
|
1639
|
+
since `object-grid` now withholds such a column instead, it would draw the mask for every
|
|
1640
|
+
untyped column until the read lands. `ListView` reads the object definition before it
|
|
1641
|
+
fetches the rows, so it now passes the definition's fields to the grid as `objectFields`
|
|
1642
|
+
with them, and the grid has no window at all. Only the grid view is handed it. When the
|
|
1643
|
+
definition could not be read, nothing is passed and the grid withholds on its own.
|
|
1644
|
+
- f9c06ef: Every data node that sends its own authored `filter` into a query now resolves the spec's context tokens first (objectui#10666). With `filter: [['owner', '=', '{current_user_id}']]` these nodes used to send the literal token; they now send the signed-in user's id, and `{current_org_id}` resolves to the active organization:
|
|
1645
|
+
|
|
1646
|
+
- `object-calendar`, `object-map` and `object-tree`, on both the object query and the inline (`provider: 'value'`) query;
|
|
1647
|
+
- `object-gantt`, `object-kanban` and `object-timeline`;
|
|
1648
|
+
- `record:related_list` and `record:line_items`, where the node's own filter is combined with the parent-record condition;
|
|
1649
|
+
- the `element:repeater`, `element:number` (both the `aggregate` filter and the `find` fallback) and `element:record_picker` page elements.
|
|
1650
|
+
|
|
1651
|
+
`list-view`, `object-grid` and `object-gallery` already did this, and are unchanged.
|
|
1652
|
+
|
|
1653
|
+
**New in `@object-ui/react`: `useResolvedFilter(filter, scope)`.** Pass it a node's authored filter and `useFilterScope()`. It resolves the filter through `@object-ui/core`'s shared `resolveFilterPlaceholders` and holds the result: re-rendering with an equal filter (even one rebuilt inline on every render) hands back the same value, so a fetch effect keyed on it does not run again, while a structurally different filter or a change of signed-in user or organization resolves again. A filter with no token to resolve is handed back as the value you passed in. The hold is the one `list-view` and `object-gallery` used privately; `plugin-list` now imports it from here, and no second copy remains in that package.
|
|
1654
|
+
|
|
1655
|
+
What changes for an app:
|
|
1656
|
+
|
|
1657
|
+
- A filter with no placeholders reaches the query as before, unchanged.
|
|
1658
|
+
- Nodes that re-queried whenever the host rebuilt an equal filter inline (`object-calendar`, `object-gantt`, `object-kanban`, `object-map`, `object-tree`) no longer issue those redundant queries.
|
|
1659
|
+
- Date macros such as `{today}` in these nodes' own filters are now resolved in the browser's local time, as they already are for `list-view`, `object-grid` and `object-view`. Before, they reached the server as literals, and the server resolved them in the tenant's time zone.
|
|
1660
|
+
|
|
1661
|
+
`@object-ui/types`: the `filter` docblocks on `ObjectMapSchema`, `ObjectTreeSchema`, `ObjectGanttSchema`, `ObjectCalendarSchema`, `ObjectKanbanSchema`, `ObjectChartSchema` and `ObjectGallerySchema` (and the matching zod descriptions) no longer say the filter is forwarded verbatim; they say its context tokens are resolved first. No type changes.
|
|
1662
|
+
- c610990: A directly authored `object-gallery` node now resolves the spec's context tokens in its own `filter` (objectui#10666). With `filter: [['owner', '=', '{current_user_id}']]` the gallery used to send the literal token on `$filter`; it now sends the signed-in user's id, and `{current_org_id}` resolves to the active organization. The filter goes through `@object-ui/core`'s shared `resolveFilterPlaceholders`, against the scope `FilterScopeProvider` supplies, the same call `list-view` and `object-grid` have made since objectui#10607. The resolved value is held and compared by structure, so re-rendering with an equal filter issues no extra query, and a change of signed-in user queries again. A filter without placeholders reaches the query unchanged. A gallery rendered as a `list-view` child was already handed a resolved filter, and nothing changes for it.
|
|
1663
|
+
|
|
1664
|
+
Date macros such as `{today}` in a directly authored gallery's filter are now resolved in the browser's local time, as they already are for `list-view`, `object-grid` and `object-view`. Before this change they reached the server as literals, and the server resolved them in the tenant's time zone.
|
|
1665
|
+
- be0ad00: `list-view` re-reads its rows when a `conditionalFormatting` rule, a row action def or a bulk action def adds a field its predicates read (objectui#10689).
|
|
1666
|
+
|
|
1667
|
+
The list puts each predicate operand of those three into `$select`, but its fetch effect did not re-run when any of them changed. So a rule added to a mounted list never had its field fetched, and the rule never matched. The effect now keys on the operand names by content, so an equal value in a new array, or a change the query cannot see such as a rule's colour, does not re-read.
|
|
1668
|
+
- 256b4c9: A filter refused as malformed (`INVALID_FILTER`) no longer throws out of render or out of a click
|
|
1669
|
+
handler at the sites that merge filters there. That holds whether this layer's filter converter
|
|
1670
|
+
refused it or, at the drill seam, the spec's own `parseFilterAST`. Each site now surfaces the
|
|
1671
|
+
refusal the way its siblings already do (objectui#10789). The filter stays **refused**: nothing
|
|
1672
|
+
that was refused is accepted, and no site falls back to "no filter".
|
|
1673
|
+
|
|
1674
|
+
- **`ElementDataSourceGate` / `useElementDataSource`** (`@object-ui/react`): the gate's merge of
|
|
1675
|
+
the component's `filter` with the composed binding, and the merge of a saved view's filter
|
|
1676
|
+
with the binding's own, lowered through the throwing converter inside render. A refused
|
|
1677
|
+
filter from either now makes the binding `missing` with a new optional `filterRefusal` on the
|
|
1678
|
+
result (`error` carries its message). The gate then draws, in place of the block, the
|
|
1679
|
+
malformed-filter notice the wrapped blocks draw: `view.malformedFilter`, naming the refused
|
|
1680
|
+
operator or field. `element:record_picker`, which reads the same hook, shows its
|
|
1681
|
+
configuration-error panel with the refusal.
|
|
1682
|
+
- **`PeoplePicker` / `RecordPickerDialog`** (`@object-ui/fields`): the recents merge and the
|
|
1683
|
+
dialog's `mergedFilter` lower through `toFilterNodeSafely`; a refusal is shown in the picker's
|
|
1684
|
+
existing error state and no query runs without the filter.
|
|
1685
|
+
- **Drill-downs** (`ObjectChart`, `ObjectPivotTable`, `DatasetWidget`, `DatasetReportRenderer`):
|
|
1686
|
+
a widget or report filter refused at the drill seam made the drill click throw. The converter
|
|
1687
|
+
refuses some (a spec `$not`, say). The spec's `parseFilterAST` refuses others, such as a scalar
|
|
1688
|
+
on a list operator in either dialect: `[['stage', 'in', 'won']]` or `{ stage: { $in: 'won' } }`.
|
|
1689
|
+
The drill is now not opened or emitted (never with the scope dropped), and the refusal is logged
|
|
1690
|
+
with `console.warn`, naming the operator. `composeDrillFilter` (`@object-ui/core`) now throws
|
|
1691
|
+
one refusal type: the spec's `INVALID_FILTER` error is re-raised as a `FilterOperatorError` with
|
|
1692
|
+
its message and the same `INVALID_FILTER` / 400 envelope, and any other error passes through
|
|
1693
|
+
unchanged.
|
|
1694
|
+
- **`ListView` export** (`@object-ui/plugin-list`): the server export builds its filter inside
|
|
1695
|
+
its `try`, so a refusal is named in the export menu instead of escaping the click.
|
|
1696
|
+
- **`convertFiltersToAST`** (`@object-ui/core`): an `$or` member that is the TRUE identity no
|
|
1697
|
+
longer stops the members after it from being read. `{ $or: [{}, { a: {} }] }` is now refused
|
|
1698
|
+
like `{ $or: [{ a: {} }, {}] }` already was, instead of answering TRUE (every row); a TRUE
|
|
1699
|
+
disjunct beside members that lower still absorbs its `$or`.
|
|
1700
|
+
- d0ae5d0: fix(plugin-list,plugin-grid): a grouped list view under a toolbar search groups on the server, and each group counts all its matching rows
|
|
1701
|
+
|
|
1702
|
+
A grouped list view whose data source answers the group header query hands
|
|
1703
|
+
the grid its own fetch, so the groups and their counts come from the server.
|
|
1704
|
+
A toolbar search used to take that back: the list view fetched one page of
|
|
1705
|
+
searched rows and the grid grouped that page in the browser, so once the
|
|
1706
|
+
matches outnumbered the page a group's count was the page's slice of it, and
|
|
1707
|
+
a group whose matches all fell past the page was missing.
|
|
1708
|
+
|
|
1709
|
+
The list view now hands the grid its toolbar term (the grid's `search` prop)
|
|
1710
|
+
and the view's `searchableFields`, and the grid sends them on the group header
|
|
1711
|
+
query and on every group's row query. A searched grouped list view shows only
|
|
1712
|
+
the groups that hold matching rows, each counting all its matches, and
|
|
1713
|
+
clearing the search restores the unsearched groups.
|
|
1714
|
+
|
|
1715
|
+
`ObjectGrid` now reads a host's `search` whenever one is passed, not only
|
|
1716
|
+
under host-driven paging: the host then owns the term, and the grid's own
|
|
1717
|
+
queries carry it. Turning grouping back on no longer asks for, or briefly
|
|
1718
|
+
shows, the groups of the query the grid held when it last grouped (a cleared
|
|
1719
|
+
search, or an old filter or sort). `@object-ui/plugin-grid` now declares
|
|
1720
|
+
`@objectstack/spec` `^17.5.0`, the release whose group header query takes a
|
|
1721
|
+
search.
|
|
1722
|
+
- ae582b7: A number field's authored `useGrouping` now decides whether its value renders with thousands
|
|
1723
|
+
separators (objectui#11026). This is the renderer half of `FieldSchema.useGrouping`, the
|
|
1724
|
+
digit-grouping hint `@objectstack/spec` declares on a field.
|
|
1725
|
+
|
|
1726
|
+
**What an author now controls.** Write `useGrouping` on a `number` field:
|
|
1727
|
+
|
|
1728
|
+
- `useGrouping: false` renders the value with no separators, whatever its `scale`: a
|
|
1729
|
+
`scale: 2` code field reads `12345.50`, not `12,345.50`.
|
|
1730
|
+
- `useGrouping: true` groups it, even at `scale: 0`: a scale-0 headcount reads `2,026`, not
|
|
1731
|
+
`2026`. An authored `true` reaches `Intl.NumberFormat` as `useGrouping: true`, which means
|
|
1732
|
+
"always". So in a locale that leaves a four-digit number alone by default (es-ES, pl-PL),
|
|
1733
|
+
`1234` renders `1.234` / `1 234`.
|
|
1734
|
+
- Leave it out and nothing changes: a declared `scale: 0` still reads as an ordinal and renders
|
|
1735
|
+
ungrouped (a year reads `2026`), and any other number is grouped the locale's way.
|
|
1736
|
+
|
|
1737
|
+
Before this change nothing in the console read the key, so the only way to drop the separators
|
|
1738
|
+
was `scale: 0`, and there was no way to keep them on a scale-0 count.
|
|
1739
|
+
|
|
1740
|
+
**Where it applies.** List and grid cells, record detail sections and highlights, the record
|
|
1741
|
+
quick-look panel, related-record tables and gallery cards. Each of these builds a cell's field
|
|
1742
|
+
from the object's field definition key by key, and each now copies `useGrouping` next to
|
|
1743
|
+
`scale`. Kanban cards and the grid's auto-generated columns hand the cell the whole field
|
|
1744
|
+
definition, so they follow it too. The form's number input and the dashboard's record table
|
|
1745
|
+
read neither `scale` nor `useGrouping` for grouping, and are unchanged.
|
|
1746
|
+
|
|
1747
|
+
**API (additive).**
|
|
1748
|
+
|
|
1749
|
+
- `@object-ui/core`: `DisplayNumberFormatOptions` gains `useGrouping?: boolean`, and
|
|
1750
|
+
`shouldGroupDisplayNumber` takes it as an optional third parameter. An authored boolean
|
|
1751
|
+
answers first; with it unset, the existing `scale`/`currency` rules decide as before.
|
|
1752
|
+
`@object-ui/i18n` re-exports both names, so the same options reach it too.
|
|
1753
|
+
- `@object-ui/types`: `NumberFieldMetadata` declares `useGrouping`, typed from the spec's
|
|
1754
|
+
`FieldSchema.useGrouping`.
|
|
1755
|
+
- f61dab1: `reference` is now the only spelling ObjectUI writes or reads for a relational field's target object (objectui#11070, round 4, under the objectui#6837 ruling: 「objectui不是前端的项目吗?后端的元数据只要对,前端按协议执行就行了呀」).
|
|
1756
|
+
|
|
1757
|
+
- **Types.** `LookupFieldMetadata`, `MasterDetailFieldMetadata` and `DetailViewField` (with its zod mirror `DetailViewFieldSchema`) declare `reference` and no longer declare `reference_to`. On the two field metadata types the member is typed by reference to `@objectstack/spec`'s `FieldSchema.reference`. `@object-ui/plugin-form`'s `FieldDefaultsSchemaLike` drops its `reference_to` member the same way.
|
|
1758
|
+
- **Readers.** `LookupField`, `UserField`, `LookupCellRenderer`, `UserCellRenderer`, the inline editor's reference fallback, the form's `current_user` seeding and the inline-subform parent lookup read `reference` alone.
|
|
1759
|
+
- **Emitters.** Every in-repo producer that builds a field definition or a widget `field` prop writes `reference`: the action-param dialog (`paramToField`), the bulk-action dialog, the record detail page, drawer, footer, related list and synthesised page, the gallery card, the form's section-field override and the flow designer's reference picker. The grid's and the dashboard's relational copy sets carry `reference` and no longer copy `reference_to`.
|
|
1760
|
+
- **Ingestion.** `normalizeFieldReferenceKeys` (behind `ObjectStackAdapter.getObjectSchema` and `MetadataProvider`) still folds a legacy `reference_to` / `referenceTo` onto `reference` when `reference` is absent, and still warns in dev. It no longer stamps `reference_to` onto every relational definition, and it still never drops or overwrites a key.
|
|
1761
|
+
|
|
1762
|
+
`@objectstack/spec`'s `FieldSchema` refuses `reference_to` by name, and objectstack#13847 rewrites stored ones on the serve path and in `os migrate meta`, so a definition served by an ObjectStack backend is unaffected.
|
|
1763
|
+
|
|
1764
|
+
## ⚠️ BREAKING for a host that hands `reference_to` to the widgets directly
|
|
1765
|
+
|
|
1766
|
+
The type change is a compile error for TypeScript that writes `reference_to` on `LookupFieldMetadata`, `MasterDetailFieldMetadata` or `DetailViewField`: rename it to `reference`.
|
|
1767
|
+
|
|
1768
|
+
At runtime the break reaches exactly one kind of host: one that serves object definitions spelling the target only as `reference_to` through a `DataSource` other than `ObjectStackAdapter`, or that passes such a definition straight into `LookupField`, `UserField` or a cell renderer. Those definitions never pass the ingestion fold. Measured with an object-bound lookup field on `ObjectForm` and a lookup column on `ObjectGrid`, both fed by a hand-written `DataSource` whose `getObjectSchema` returns `{ type: 'lookup', reference_to: 'account' }`:
|
|
1769
|
+
|
|
1770
|
+
- before this change, opening the picker queried `account` and the cell resolved the record's name;
|
|
1771
|
+
- after it, the picker has no object to query (no `find` call is made) and the cell shows the raw id beside the unresolved-reference marker.
|
|
1772
|
+
|
|
1773
|
+
The same definition served through `ObjectStackAdapter.getObjectSchema` still works, before and after: the fold adds `reference`, the picker queries `account`, the cell resolves the name, and the dev warning names the field. **Fix:** spell the target `reference` in the definition your `DataSource` serves.
|
|
1774
|
+
|
|
1775
|
+
⚠️ **Dated note, 2026-09-30 — the break reaches more than the paragraph above states — objectui#11070.** Two corrections from the contract review of this change, appended rather than edited in; neither changes what ships.
|
|
1776
|
+
|
|
1777
|
+
- "The break reaches exactly one kind of host" above is too narrow. Host code that read `reference_to` off a definition after ingestion — from `ObjectStackAdapter.getObjectSchema()` or `useMetadata().objects` — also loses that key, because the ingestion pass no longer stamps it (the **Ingestion** bullet above states the fact, and `reference_to` was never a declared member of those definitions). **Fix:** read `reference`.
|
|
1778
|
+
- The measured break above is a `lookup` field's. A `user` field handed a `reference_to`-only definition directly does not end at "no query": `UserField` falls back to `sys_user`, so its picker queries `sys_user` rather than the object `reference_to` named. **Fix:** spell the target `reference` there too.
|
|
1779
|
+
|
|
1780
|
+
The text above is kept as the reading of this change.
|
|
1781
|
+
- 81f8498: objectui now resolves `@objectstack/*` 17.5.0 and `zod` 4.6.5, and follows every contract move that release makes (objectui#11073). `@objectstack/spec` 17.5.0 and `@objectstack/core` 17.5.0 require `zod ^4.6.1`, so objectui's own `zod` resolves to the same 4.6.5: two zod minors do not type-check against each other.
|
|
1782
|
+
|
|
1783
|
+
⚠️ Breaking in places, marked `minor` under this repo's version-alignment rule (a `major` in the fixed group would move all of it off the `@objectstack` major). Every narrowing below is the spec's own, and already reaches any consumer that resolves `@objectstack/spec ^17.5`.
|
|
1784
|
+
|
|
1785
|
+
- `@object-ui/types`: declares `@objectstack/spec ^17.5.0` and `zod ^4.6.1`. A named list view's `pageName` and `tabs` are retired, as the spec retired them: typed `never` and refused by name at parse. `DashboardWidgetSchema` attaches the spec's two new checks (`checkDashboardWidgetStageOrder`, `checkDashboardWidgetMetricMeasureArity`), so a widget the spec refuses is refused here too. Where a strict object is an arm of a plain union, its unknown-key refusal is terminal again (the spec's own mechanism, needed under zod 4.6): the union answers with every arm instead of the one that failed only on unknown keys. That changes the error shape, never the verdict (measured on every objectui union). Two by-name named-view checks that can no longer run are retired; the spec's refusal answers those documents.
|
|
1786
|
+
- `@object-ui/core`: declares `@objectstack/spec ^17.5.0`. `page` leaves the list-view kinds the normalizer knows, as the spec removed `type: 'page'`. `isRefusedTextComparand` and `textComparandRefusalReason` are re-exported from `@objectstack/spec/data`, whose copies are byte-identical. `SPEC_ACTION_KEYS` lists `execution`, which `ActionSchema` now declares.
|
|
1787
|
+
- `@object-ui/data-objectstack`: declares `@objectstack/spec ^17.5.0`, up from `^17.4.0`. Its bundle carries `@object-ui/core`'s filter converter, which now takes `isRefusedTextComparand` and `textComparandRefusalReason` from `@objectstack/spec/data`. `@objectstack/spec` 17.4.0 exports neither, so the old range admitted a spec under which the shipped module names exports that are not there (objectui#5793's floor gate). Nothing moves at runtime in this repository, because the lockfile already resolves 17.5.0. `patch`, as the floor raise of objectui#10864 was.
|
|
1788
|
+
- `@object-ui/fields`: the percent and number `scale` clamp is gone, at the sunset its own test named. The spec now refuses a `scale` above 100 at the declaration, so the width a formatter cannot render no longer arrives, and every face passes the declared width through as it did before the clamp.
|
|
1789
|
+
- `@object-ui/components`: two module-local prop interfaces of the action renderers are renamed (`ActionButtonRendererProps`, `ActionIconRendererProps`), because the spec now exports the old names for its authored props. Neither is reachable through the package's exports map; only the shipped per-file declarations change.
|
|
1790
|
+
- `@object-ui/plugin-list`: the README's view-type example no longer lists the retired `page` kind.
|
|
1791
|
+
- `@object-ui/app-shell`: the flow designer follows 17.5.0 (maintainer ruling on objectui#11088, decision 2). A new `wait` node is seeded `waitEventConfig: { eventType: 'timer', timerDuration: 'PT1H' }`, because the spec now refuses a timer wait with no duration; the inspector shows the Duration at once and it can be changed. The decision form offers the spec's new `mode` (exclusive or inclusive), with no default declared because the spec applies none. The screen form's `mode` states `create` again, because the spec now applies that default. `minor` because the decision form gains a control.
|
|
1792
|
+
- `@object-ui/app-shell`: declares `@objectstack/spec ^17.5.0`, up from `^17.4.0`, and `zod ^4.6.1`, up from `^4.4.3`. The designer now states 17.5.0 behaviour. The screen form's declared `create` default equals a default the spec applies only since 17.5.0, and objectui#9109 requires the two to be equal, so a range that admitted 17.4.0 claimed more than the designer holds to. The resolved versions do not move.
|
|
1793
|
+
- `@object-ui/console`: the bundle inlines the 17.5.0 packages, so its client-side validation answers as the published 17.5.0 contract does. The flow preview sample's script nodes use the 17.5.0 script contract. The eager-closure budget is re-baselined for the release's growth, under the maintainer ruling on objectui#11088 (decision 1).
|
|
1794
|
+
- b24f93a: A page now renders the accessible name an author declares in its `aria` bag, and the list view and the `record:*` blocks read the same bag through the one shared reader (objectui#11083).
|
|
1795
|
+
|
|
1796
|
+
**The page root (a fix).** The spec's `PageSchema` declares `aria` as `AriaPropsSchema`, and `PageNodeSchema` mirrors it. The page renderer passed its wrapper only DOM attributes, so the `aria` object was dropped and `{ type: 'page', aria: { ariaLabel: 'Orders' } }` rendered no `aria-label` anywhere. The page's root element now carries:
|
|
1797
|
+
|
|
1798
|
+
- `aria-label` from `ariaLabel`. A locale map (`{ en: 'Orders', 'zh-CN': '订单' }`) gives the entry for the display locale (`useDisplayLocale()`);
|
|
1799
|
+
- `aria-describedby` from `ariaDescribedBy`;
|
|
1800
|
+
- `role` from `role`.
|
|
1801
|
+
|
|
1802
|
+
The page adds no default role, and a page that declares no `aria` renders the same DOM as before. The flat `ariaLabel` on the page node still reaches the root as before; when both spellings name the same attribute, the nested `aria` bag wins.
|
|
1803
|
+
|
|
1804
|
+
**The list view and the `record:*` blocks (no visible change).** `ListView`'s list region and the `record:*` blocks' `useRecordAriaProps` used to map the nested bag themselves. Both now go through `resolveInlineAriaProps` from `@object-ui/react`, the reader the `element:*` blocks use, and keep only their own defaults: the list region's `region` role, and each block's default role, default name and the report of the refused `aria.label` spelling. One difference: an authored empty `role: ''` on a list view used to render an empty `role` attribute, and now falls back to `region`. The list view still reads `aria.live`, which objectui's `ListViewSchema` declares on top of the spec's bag.
|
|
1805
|
+
- 6158e4c: An `object-grid` publishes `description` and `emptyState` now that `@objectstack/spec` 17.6.0
|
|
1806
|
+
declares them on its `object-grid` row, and `emptyState.title` / `.message` take an inline
|
|
1807
|
+
locale map on both the grid and the list view (objectui#11227).
|
|
1808
|
+
|
|
1809
|
+
**`@object-ui/plugin-grid` (feature and fix).**
|
|
1810
|
+
|
|
1811
|
+
- `description` and `emptyState` are in the grid's declared inputs, so the designer panel, the
|
|
1812
|
+
component manifest and the generated `sdui-intrinsics.d.ts` offer them, and the SDUI parser
|
|
1813
|
+
no longer reports an authored `emptyState` as `unknown-prop`. `description` declares both
|
|
1814
|
+
arms of the `I18nLabel` union, so a locale map on it is not a `type-mismatch`.
|
|
1815
|
+
- `emptyState.title` and `emptyState.message` are resolved against the display locale, as
|
|
1816
|
+
`label` and `description` are. Before, a locale map on either reached the empty-state
|
|
1817
|
+
component as an object, and the grid failed to render. A map with no usable entry keeps
|
|
1818
|
+
that member's default: the table's "No results found" heading, and no message line.
|
|
1819
|
+
- `keyboardNavigation`, the row's third 17.6.0 key, is not published. The spec marks it
|
|
1820
|
+
`[EXPERIMENTAL — not enforced]`, and nothing in the grid reads it yet.
|
|
1821
|
+
|
|
1822
|
+
**`@object-ui/plugin-list` (fix).** A list view's authored `emptyState.title` and
|
|
1823
|
+
`emptyState.message` are resolved against the display locale. Before, a locale map on either
|
|
1824
|
+
was treated as absent, and the empty state drew the default copy in every locale. A plain
|
|
1825
|
+
string is drawn as before.
|
|
1826
|
+
|
|
1827
|
+
**`@object-ui/types` (breaking for a reader of the text members, hence `minor`).**
|
|
1828
|
+
|
|
1829
|
+
- `ObjectGridSchema.emptyState` is the spec's `EmptyState`, and the Zod twin takes the spec's
|
|
1830
|
+
`EmptyStateSchema` by reference. `title` and `message` are `string | I18nLabel`, `icon` stays
|
|
1831
|
+
a string, and an unknown member is still refused by name. A locale map on `title` or
|
|
1832
|
+
`message` now parses, and a value that is neither a string nor a map is refused at that
|
|
1833
|
+
member.
|
|
1834
|
+
- `NamedListView.emptyState` is `ListViewSchema['emptyState']`, the same spec type.
|
|
1835
|
+
- TypeScript code that reads `emptyState.title` or `.message` as a `string` no longer
|
|
1836
|
+
type-checks. Resolve the value first, for example with `resolveI18nLabel` from
|
|
1837
|
+
`@objectstack/spec/ui`.
|
|
1838
|
+
|
|
1839
|
+
**Correction, 2026-10-03 (objectui#11068, the `keyboardNavigation` build).** The bullet above
|
|
1840
|
+
that says `keyboardNavigation` "is not published" and that "nothing in the grid reads it yet" is
|
|
1841
|
+
superseded. The grid honours the key, with arrow-key cell navigation that is on by default when
|
|
1842
|
+
the grid renders editable, and it is in the grid's declared inputs, so the designer panel, the
|
|
1843
|
+
component manifest and the generated `sdui-intrinsics.d.ts` offer it
|
|
1844
|
+
(`.changeset/11068-keyboard-navigation.md`).
|
|
1845
|
+
- 4a1adb7: The list view's record-count bar reads the right noun form in every language at every count (objectui#11445): it passes `count` to the `list.recordCount` count family instead of choosing `list.recordCountOne` on `=== 1`, so Russian reads `2 записи` and Arabic uses its dual at 2. `LIST_DEFAULT_TRANSLATIONS` carries the family's `_one` / `_other` rows.
|
|
1846
|
+
- f560ded: The gallery card's field bag now carries the field's `max`, the storage
|
|
1847
|
+
statement the percent cell reads through the spec's `percentScaleOf`
|
|
1848
|
+
(objectui#11475). Without it, a whole-stored `50` (`max: 100`) would read
|
|
1849
|
+
`5000%` on a card beside `50%` in the grid.
|
|
1850
|
+
- 9801765: The selection bar's built-in **Delete** now honours `userActions.delete.visibleWhen`
|
|
1851
|
+
per selected record (objectui#4420). It used to read that key as a bare boolean — the
|
|
1852
|
+
object-level verdict only — so ticking a record the author's predicate excludes still
|
|
1853
|
+
offered the red Delete, and pressing it deleted the record the predicate was written to
|
|
1854
|
+
protect. The row kebab on the same screen hid its Delete correctly, so one declared key
|
|
1855
|
+
meant two different things on two surfaces.
|
|
1856
|
+
|
|
1857
|
+
Ruled by the maintainer on 2026-08-17 (behaviour 1 of the card's three): **filter the
|
|
1858
|
+
operation and report the skipped**. The bar evaluates the predicate once per selected
|
|
1859
|
+
record, the delete runs over the allowed subset, and the excluded records are reported
|
|
1860
|
+
rather than silently dropped. The button itself is never hidden or disabled by the
|
|
1861
|
+
predicate — a mixed selection is not punished for one stray tick — and a selection where
|
|
1862
|
+
every row is excluded is a legible refusal rather than an unexplained absence.
|
|
1863
|
+
|
|
1864
|
+
- `@object-ui/core` gains `partitionRowsByPredicate`, the set-shaped counterpart of
|
|
1865
|
+
`evalRowPredicate`: the fail-closed per-record fold a bulk gate needs, written once.
|
|
1866
|
+
A bulk gate evaluates N records in a loop, which is why it can never be a hook.
|
|
1867
|
+
- `@object-ui/plugin-grid`'s bulk bar routes an excluded selection through
|
|
1868
|
+
`BulkActionDialog`, whose existing `bulk-skipped-notice` slot reports the skipped
|
|
1869
|
+
count; a selection with nothing excluded keeps the consumer's own delete flow
|
|
1870
|
+
untouched. `resolveRowCrudAffordances` now also returns `objectDeletePredicates` —
|
|
1871
|
+
the bulk half of the same predicates, gated on the object verdict rather than on the
|
|
1872
|
+
row `onDelete` wiring. The dialog declines to run over zero records.
|
|
1873
|
+
- `@object-ui/plugin-list`'s non-grid bulk bar (kanban / calendar / gallery / …) filters
|
|
1874
|
+
the built-in `delete` to the eligible subset and states the skipped count inline.
|
|
1875
|
+
|
|
1876
|
+
Custom bulk action ids are untouched: they route through the action runner carrying
|
|
1877
|
+
their own gates. This is a UI affordance — server enforcement was never the leak.
|
|
1878
|
+
- 41b7ce3: **View configuration is explicitly org-wide, and its write path is now gated (objectstack#7494's
|
|
1879
|
+
ruling, maintainer 2026-08-12).** The `sort` / `hiddenFields` / `columnState` / `rowHeight` that a
|
|
1880
|
+
list toolbar persists were never per-user: they are one shared row on the view, so an ordinary user
|
|
1881
|
+
dragging a column or cycling density was re-styling that view for the entire organization. Nothing
|
|
1882
|
+
in the console said so, and nothing stopped it. A per-user scope stays parked (objectstack#7611,
|
|
1883
|
+
v18) and is deliberately not built here — which is precisely why the write has to be gated rather
|
|
1884
|
+
than narrowed: there is no second, private store for it to fall back to.
|
|
1885
|
+
|
|
1886
|
+
`ObjectStackAdapter.updateViewConfig` now refuses when the session's **reported** ADR-0066 capability
|
|
1887
|
+
set does not contain `manage_metadata`, throwing the new `ViewConfigPermissionDeniedError`
|
|
1888
|
+
(`VIEW_CONFIG_PERMISSION_DENIED`, with `isViewConfigPermissionDeniedError` and the
|
|
1889
|
+
`VIEW_CONFIG_CAPABILITY` constant alongside it). The gate is the **first** statement in the method —
|
|
1890
|
+
before `connect()`, before the payload is assembled — so a refused call puts nothing on the wire.
|
|
1891
|
+
It is on the write rather than on the toolbar button on purpose: withholding the affordance would
|
|
1892
|
+
leave the method still accepting the call from anything else holding the adapter, whereas a gate on
|
|
1893
|
+
the write is inherited by every caller, present and future.
|
|
1894
|
+
|
|
1895
|
+
`manage_metadata` is not a newly minted name. It is the capability this repo already treats as
|
|
1896
|
+
metadata-authoring authority — `HomePage`'s `AUTHORING_CAPABILITY`, the one the server itself
|
|
1897
|
+
refuses metadata writes without — and the gated write goes through `client.meta.saveItem`, the very
|
|
1898
|
+
same ADR-0005 metadata door, so this applies the authority the server is already applying instead of
|
|
1899
|
+
inventing a parallel one.
|
|
1900
|
+
|
|
1901
|
+
**Unknown fails open, by doctrine.** A capability set that was never reported (a backend predating
|
|
1902
|
+
ADR-0066, or no permission provider mounted) is not a denial: the server enforces regardless, so a
|
|
1903
|
+
client-side refusal on missing data cannot protect anything and can only break a permitted user. A
|
|
1904
|
+
*reported* empty grant gates strictly. Hosts push the session's capabilities in with the new
|
|
1905
|
+
`setSystemCapabilities`; `ObjectView` wires it from `usePermissions()`.
|
|
1906
|
+
|
|
1907
|
+
The refusal is also **said out loud**. `ObjectView`'s persist path previously swallowed every failure
|
|
1908
|
+
into `console.error`, which for a debounced toggle whose UI has already moved would have left the
|
|
1909
|
+
operator looking at a density they did not get; a denied write now raises a toast. And the "View
|
|
1910
|
+
settings" popover — where density and field visibility are actually changed — now states the scope
|
|
1911
|
+
before the operator acts: *"Grouping, color, density, and visible fields. Applies to everyone who
|
|
1912
|
+
uses this view."*, translated in all ten packs.
|
|
1913
|
+
- 39f4309: Published typings from every `vite-plugin-dts` package now carry an explicit extension on
|
|
1914
|
+
every relative specifier, and a type error in the declaration build now fails the build
|
|
1915
|
+
instead of being printed and ignored (objectui#5439, objectui#5483).
|
|
1916
|
+
|
|
1917
|
+
**Consumers on `moduleResolution: nodenext` or `node16` may see NEW type errors, and that
|
|
1918
|
+
is the fix working.** These packages re-export mostly through NAMED re-exports —
|
|
1919
|
+
`export { useObjectChat } from './useObjectChat'`. TypeScript could not follow the
|
|
1920
|
+
extensionless hop, but it still DECLARED the name, so the symbol resolved to a silent
|
|
1921
|
+
`any`. Nothing errored; consumers simply got no types. With the extension emitted, the
|
|
1922
|
+
symbol carries its real type, and any call site that was relying on the `any` now type
|
|
1923
|
+
checks for the first time. This is the mode that produced the 21 residual `TS7006` on
|
|
1924
|
+
`@object-ui/app-shell` reported against objectui#5365 — a type hole that opened quietly,
|
|
1925
|
+
unlike objectui#5365's own `export * from './ui'` packages where the same defect surfaced
|
|
1926
|
+
immediately as `TS2305: has no exported member`.
|
|
1927
|
+
|
|
1928
|
+
410 extensionless relative specifiers across 19 packages were emitted before this change;
|
|
1929
|
+
the count is now 0 in all 22 packages that build typings through `vite-plugin-dts`.
|
|
1930
|
+
`@object-ui/fields` was already clean — its sources write explicit `.js` specifiers — and
|
|
1931
|
+
is wired so it stays that way.
|
|
1932
|
+
|
|
1933
|
+
The second half changes no emitted output today: 22/22 packages built green unmodified, so
|
|
1934
|
+
making the declaration step's exit code honest turns nothing red. It changes what a FUTURE
|
|
1935
|
+
regression does — print and exit 0, versus fail the build.
|
|
1936
|
+
- b4393e5: The last three sort-axis consumers read the platform's per-column sortability signal instead
|
|
1937
|
+
of re-deriving it from the field's type (objectui#6108, inheriting objectstack#10235 ruling A
|
|
1938
|
+
through objectui#5729's landed contract). ListView's toolbar sort picker and both of
|
|
1939
|
+
RelatedList's sort entry points — the embedded table's column headers and the `data-list`
|
|
1940
|
+
sort-button row — now go through `isPlatformSortableField`, the same spelling the grid header
|
|
1941
|
+
adopted; their `UNMATERIALIZED_FIELD_TYPES` / `isUnmaterializedFieldType` re-derivations are
|
|
1942
|
+
deleted.
|
|
1943
|
+
|
|
1944
|
+
The re-derivation was not wrong about `formula`: the platform computes its own projection from
|
|
1945
|
+
the same `@objectstack/spec` storage fact, which is why the drift went unnoticed across two
|
|
1946
|
+
cards. It parts company on everything the projection encodes as ABSENCE — an unknown name, a
|
|
1947
|
+
dotted path a caller can put in a related list's `columns`, an unprovisioned audit column —
|
|
1948
|
+
where a type read finds no field definition, answers "sortable", and offers a control the
|
|
1949
|
+
runtime meets with `400 INVALID_SORT`. It parts company again on any refusal that carries no
|
|
1950
|
+
`reason: virtual-type`, and it cannot follow the platform in the other direction either: a
|
|
1951
|
+
field the platform now DOES order by stays withheld forever on its type alone.
|
|
1952
|
+
|
|
1953
|
+
Two behaviours are deliberately unchanged. The relational carve-out stays separate from the
|
|
1954
|
+
signal — the projection answers `sortable: true` for a `lookup` because the platform can order
|
|
1955
|
+
by the stored foreign key, while the UI withholds because that order means nothing beside a
|
|
1956
|
+
column of names — so a relational column does not get its sort back. And ListView's picker
|
|
1957
|
+
still lists a field the CURRENT sort already names, which is the only way to remove a sort the
|
|
1958
|
+
server refuses outright; that exception now covers platform-refused fields, not just formulas.
|
|
1959
|
+
|
|
1960
|
+
A deployment that served no `sortability` key at all is a different case from "nothing is
|
|
1961
|
+
sortable": that branch keeps the type read as a compatibility floor, so behaviour on a backend
|
|
1962
|
+
older than objectstack#10235 (or an inline/mock data source) is byte-identical to before.
|
|
1963
|
+
- bd2f56a: `ListView`'s toolbar sort picker no longer persists a sort the platform refuses to order by.
|
|
1964
|
+
|
|
1965
|
+
The picker keeps a platform-refused field listed while the CURRENT sort names it
|
|
1966
|
+
(#6108). That exception is deliberate and stays: it is the only way a user can
|
|
1967
|
+
REMOVE a sort the server answers `400 INVALID_SORT` for — withholding the option
|
|
1968
|
+
unconditionally renders a blank row nobody can delete, and drops the sort silently
|
|
1969
|
+
on the next edit.
|
|
1970
|
+
|
|
1971
|
+
What was wrong is that the picker rendered and emitted from the same array. Editing
|
|
1972
|
+
anything ELSE in that popover — adding a second sort key, resetting to the view's
|
|
1973
|
+
default — re-emitted the whole array with the refused entry still in it, and the
|
|
1974
|
+
host's `onSortChange` turned that into `persistViewPatch({ sort })`: a
|
|
1975
|
+
personalization PUT storing a refused column, written by a user who never touched
|
|
1976
|
+
that row. A view stored before the sortability signal existed therefore kept
|
|
1977
|
+
re-persisting its refused `$orderby` indefinitely.
|
|
1978
|
+
|
|
1979
|
+
Every `onSortChange` this component emits — the builder, the column-header sort and
|
|
1980
|
+
"reset to default" — now crosses one boundary that drops what the served projection
|
|
1981
|
+
refuses, while `currentSort` keeps the array whole. So what the picker LISTS and
|
|
1982
|
+
what it PERSISTS are separate: the refused entry stays visible and removable,
|
|
1983
|
+
removing it persists the removal, and no write carries it. This is the separation
|
|
1984
|
+
#5729 already made at the grid seam (`ObjectGrid`'s `manualSort` /
|
|
1985
|
+
`manualOnSortChange` pair); the picker was the second door onto the same stored
|
|
1986
|
+
view state.
|
|
1987
|
+
|
|
1988
|
+
Only under a served sortability projection (objectstack#10235 ruling A). `undefined`
|
|
1989
|
+
means NO SIGNAL SERVED — an older deployment, an inline/mock data source — not
|
|
1990
|
+
"nothing is sortable", and that branch is byte-identical in behaviour to before.
|
|
1991
|
+
- babe956: `list-view` stops spelling "the author declared no columns" as an explicit empty
|
|
1992
|
+
projection (objectui#6598).
|
|
1993
|
+
|
|
1994
|
+
A production `kind:'html'` page carried `<list-view objectName="opportunity">`
|
|
1995
|
+
with no `columns` and rendered the row count, the filter/group/sort toolbar and
|
|
1996
|
+
the index column — and **not one data column**, with no diagnostic anywhere.
|
|
1997
|
+
`ObjectGrid` derives default columns for exactly that case ("Default columns
|
|
1998
|
+
priority (when schema doesn't specify columns)"), and it never ran: the
|
|
1999
|
+
derivation is gated on `schema.fields` being ABSENT, `ListView` sent
|
|
2000
|
+
`fields: []`, and an empty array is truthy. `normalizeColumns` had already read
|
|
2001
|
+
the empty `columns` as unauthored, so the two keys disagreed about the same fact
|
|
2002
|
+
and the stricter reading won.
|
|
2003
|
+
|
|
2004
|
+
`ListView` now asks whether the AUTHOR declared a projection — `columns` present
|
|
2005
|
+
and non-empty, after the legacy `fields` fold — and hands the child grid nothing
|
|
2006
|
+
at all when they did not, so the grid's own defaults apply.
|
|
2007
|
+
|
|
2008
|
+
⚠️ The predicate reads the authored value and never what survived filtering, and
|
|
2009
|
+
that distinction is load-bearing: when the author DID declare columns and the
|
|
2010
|
+
field gate removed every one of them, the empty projection is still sent.
|
|
2011
|
+
`ObjectGrid` re-applies FLS on its derived column path only, never on the
|
|
2012
|
+
explicit-columns path, so falling through to the derivation there would put
|
|
2013
|
+
fields on screen that the author never asked for and the principal may not read.
|
|
2014
|
+
|
|
2015
|
+
Measured single-variable on the html tier: a bare
|
|
2016
|
+
`<object-grid objectName="opportunity" />` renders the object's default columns;
|
|
2017
|
+
the same object behind `<list-view>` rendered none. Pinned at the handoff
|
|
2018
|
+
(`ListView.unauthoredColumnProjection-6598.test.tsx`) and end to end over the
|
|
2019
|
+
real grid on a real html-kind page
|
|
2020
|
+
(`htmlTierListViewDefaultColumns-6598.test.tsx`).
|
|
2021
|
+
|
|
2022
|
+
This is one half of the reported symptom. Which columns the defaults resolve to
|
|
2023
|
+
still depends on who owns the fetch — with a host like `ListView` fetching, the
|
|
2024
|
+
grid takes its inline-data branch and derives from the row payload's keys rather
|
|
2025
|
+
than from the object schema's policy (hidden and readonly system-managed fields
|
|
2026
|
+
dropped, `highlightFields` honoured). That precedence sits in
|
|
2027
|
+
`packages/plugin-grid` and is filed separately.
|
|
2028
|
+
- 3beef6d: The spec's `dataSource` element binding is now DECLARED by the blocks that read
|
|
2029
|
+
it, so the html tier stops reporting the one working saved-view spelling as
|
|
2030
|
+
`unknown-prop` (objectui#6678).
|
|
2031
|
+
|
|
2032
|
+
`PageComponentSchema.dataSource` — `{ object, view, filter, sort, limit }` — is
|
|
2033
|
+
the one spelling that resolves a saved view for an object-bound block. It works,
|
|
2034
|
+
and it drew the identical `unknown-prop` warning as the two spellings that do
|
|
2035
|
+
nothing (`viewName`, `view`), because `validateTree` looks a prop up in the
|
|
2036
|
+
block's declared `inputs` and no registration declared this key. On the tier
|
|
2037
|
+
built to accept AI-authored pages, where the diagnostic IS the contract, the
|
|
2038
|
+
only signal pointed away from the key that works.
|
|
2039
|
+
|
|
2040
|
+
Adopting the maintainer ruling of 2026-08-29 — option B **in the injection
|
|
2041
|
+
form**:
|
|
2042
|
+
|
|
2043
|
+
- `ELEMENT_DATA_SOURCE_INPUT` is the single declaration, in `@object-ui/core`
|
|
2044
|
+
beside the binding's own semantics; `Registry.register` emits it for any
|
|
2045
|
+
registration whose renderer passed through the new `elementDataSourceBlock()`
|
|
2046
|
+
seam. One mechanism, one copy — not a hand-kept declaration per block, which is
|
|
2047
|
+
the shape that drifts and that a new block forgets. The seam lives in
|
|
2048
|
+
`@object-ui/core` and is re-exported by `@object-ui/react` beside
|
|
2049
|
+
`ElementDataSourceGate` for discoverability; call sites take the core import,
|
|
2050
|
+
because a registration runs at module scope and this repo's suites partially
|
|
2051
|
+
mock `@object-ui/react`.
|
|
2052
|
+
- Seventeen renderers, in thirteen files across twelve packages, reach the seam
|
|
2053
|
+
and now publish the key to the save gate, the parser whitelist, the generated
|
|
2054
|
+
JSX authoring types and the block list. The card named nine blocks; the tree
|
|
2055
|
+
also has `plugin-grid`, `plugin-timeline`, two further `plugin-form` blocks and
|
|
2056
|
+
`element:record_picker` — nothing was hand-listed, so the mechanism covered
|
|
2057
|
+
them. `element:record_picker` consumes the gate's HOOK and status panels rather
|
|
2058
|
+
than the wrapper tag (its object lives under `properties`), and was found by a
|
|
2059
|
+
render probe rather than by reading sources.
|
|
2060
|
+
- `dataSource` on a block that does NOT read it (`flex`, `card`) still reports
|
|
2061
|
+
`unknown-prop`. Adding the key to `sdui-parser`'s `BASE_PROPS` was refused for
|
|
2062
|
+
exactly this reason — that set mirrors `BaseSchema`, and silencing the key
|
|
2063
|
+
everywhere would make the diagnostic lie in the other direction.
|
|
2064
|
+
- New `check:element-data-source-declaration` fails any source that consumes the
|
|
2065
|
+
gate without reaching the seam, so a block added tomorrow cannot forget.
|
|
2066
|
+
|
|
2067
|
+
Behaviour of the binding itself is unchanged — this is a declaration, not a
|
|
2068
|
+
resolution change. The saved view still resolves its columns, and an
|
|
2069
|
+
unresolvable `view` still fails loudly rather than widening to the object's full
|
|
2070
|
+
scope.
|
|
2071
|
+
|
|
2072
|
+
The spec/registry parity gates (repo-wide and the `record:related_list` per-block
|
|
2073
|
+
pin) now derive their accepted set from the WHOLE node contract rather than from
|
|
2074
|
+
`ComponentPropsMap[type]` alone. `PageComponentSchema` accepts and keeps
|
|
2075
|
+
`dataSource` on a page-component node — it is a node-level key, a sibling of
|
|
2076
|
+
`type` and `className`, not a per-block prop — so the gates' previous complaint
|
|
2077
|
+
was measurably wrong. Derived from the spec, not exempted, and both still
|
|
2078
|
+
discriminate against an invented key.
|
|
2079
|
+
- 06b8c42: Re-key three more renderer effects onto the primitives they actually read,
|
|
2080
|
+
instead of the memoised object identity that produced them (objectui#6697 —
|
|
2081
|
+
the three census members from objectui#6592 that sit outside its
|
|
2082
|
+
`getDataConfig(schema)` family):
|
|
2083
|
+
|
|
2084
|
+
- `RelatedList`'s collection fetch now depends on `defaultSortKey` /
|
|
2085
|
+
`filterKey` (the `JSON.stringify`-derived content strings the two memos are
|
|
2086
|
+
already keyed on) rather than on `defaultSortSpec` / `listFilterNode`.
|
|
2087
|
+
- `page:tabs`' related-count probe now depends on a serialised `probeKey`
|
|
2088
|
+
rather than on the `probeTargets` `Map`.
|
|
2089
|
+
- `ListView`'s data fetch now depends on the `expandFields` memo's own INPUTS
|
|
2090
|
+
— `schema.columns`, the alternate views' binding blocks and
|
|
2091
|
+
`objectDef?.fields`, all props and state a discard cannot move — rather than
|
|
2092
|
+
on the `expandFields` array the memo returns.
|
|
2093
|
+
|
|
2094
|
+
`useMemo` carries no semantic guarantee — React is permitted to discard a memo
|
|
2095
|
+
cache and recompute even when its dependency array compares equal to the
|
|
2096
|
+
previous render — and all three factories return a FRESH value on every call
|
|
2097
|
+
(`normalizeSortSpec`/`toFilterNode` build a new array / a freshly lowered AST,
|
|
2098
|
+
the probe factory builds a new `Map`, `buildExpandFields` returns a new array
|
|
2099
|
+
in every branch). So each effect re-ran on a discard alone, with nothing an
|
|
2100
|
+
author or a caller controls having changed: an extra `dataSource.find` for the
|
|
2101
|
+
related collection, an extra `dataSource.find` for the list window, and a
|
|
2102
|
+
redundant re-probe of every tab's count. Keying on the primitives makes a
|
|
2103
|
+
cache discard a no-op and returns `useMemo` to being a pure optimisation.
|
|
2104
|
+
|
|
2105
|
+
Severity is low and the fix is deliberately narrow: the observable was a
|
|
2106
|
+
redundant round trip, never incorrect data, so only the re-run condition
|
|
2107
|
+
moves — each effect body still reads the memoised value, and a genuine change
|
|
2108
|
+
still refetches exactly as before.
|
|
2109
|
+
|
|
2110
|
+
The three take two routes on purpose — key on the nearest DISCARD-IMMUNE
|
|
2111
|
+
thing. `RelatedList`'s memos are keyed on exactly one primitive each, and
|
|
2112
|
+
`page:tabs`' probe memo is keyed on another MEMO's output (`items`), which is
|
|
2113
|
+
not discard-immune, so both take a content string. `ListView`'s memo is keyed
|
|
2114
|
+
on props and state, so it names those directly: a value key over
|
|
2115
|
+
`expandFields` would NOT have been content-equivalent there — `buildExpandFields`
|
|
2116
|
+
collapses the collected set down to the relation roots, while the effect body
|
|
2117
|
+
also builds `$select` from `schema.columns` and the view bindings — and it
|
|
2118
|
+
would have defeated objectui#4567's live-dependency pin, which ruled that
|
|
2119
|
+
"ListView's by-identity dependency is correct for a real column change" and
|
|
2120
|
+
put the identity stabilisation at the PRODUCER.
|
|
2121
|
+
|
|
2122
|
+
One correction to the census card's account, measured while pinning it: for
|
|
2123
|
+
`page:tabs` the redundant probe costs nothing on the wire.
|
|
2124
|
+
`RelatedCountStore.fetch` returns the cached count as its first act and dedupes
|
|
2125
|
+
concurrent probes, so the extra work is the effect re-running, not an extra
|
|
2126
|
+
request.
|
|
2127
|
+
- 4dfdcc3: `ListView` reads `exportOptions.streaming` without a cast (objectui#6956). The
|
|
2128
|
+
two `as any` reads — the `exportableFormats` server-availability check and
|
|
2129
|
+
`handleExport`'s server-eligibility gate — and the `'pdf'` in the bare-array
|
|
2130
|
+
fold's cast are gone: the `ListViewSchema` type now carries `streaming` and not
|
|
2131
|
+
`'pdf'`, because `@object-ui/types`' zod mirror binds the spec's `exportOptions`
|
|
2132
|
+
field by reference. No behaviour change: the same formats are offered,
|
|
2133
|
+
`streaming: false` still forces the client-side path, and the bare-array fold
|
|
2134
|
+
(`resolvedExportOptions`, a stored `['csv', 'xlsx']` folded to `{ formats }`)
|
|
2135
|
+
STAYS — nothing on the render path parses and `ObjectView` forwards a stored
|
|
2136
|
+
value verbatim, so the spec's parse-time lift never runs before this renderer
|
|
2137
|
+
and the fold is load-bearing rather than legacy. A `'pdf'` stored before the
|
|
2138
|
+
retirement still arrives as data and is still dropped from the export menu with
|
|
2139
|
+
the existing one-time warning.
|
|
2140
|
+
- d1842ab: `DataEmptyState` now declares `role="status"` by default, so an empty result is
|
|
2141
|
+
distinguishable from a failed one on every surface that renders it
|
|
2142
|
+
(objectui#7132).
|
|
2143
|
+
|
|
2144
|
+
This is the convergence half of the two rulings that landed as objectui#7063 and
|
|
2145
|
+
objectui#7064, both resting on objectstack#13848: uniform behaviour belongs to
|
|
2146
|
+
the platform, and per-surface compensation is the per-app tax being ruled
|
|
2147
|
+
against. Those two fixed their own surfaces deliberately and locally; this card
|
|
2148
|
+
measured whether the shared primitive should carry the property. It did not.
|
|
2149
|
+
|
|
2150
|
+
**Measured, not assumed.** All the surfaces were rendered and their empty boxes
|
|
2151
|
+
read directly:
|
|
2152
|
+
|
|
2153
|
+
| surface | `role` before |
|
|
2154
|
+
|---|---|
|
|
2155
|
+
| `DataEmptyState` bare default | *none* |
|
|
2156
|
+
| `plugin-list` empty list | *none* |
|
|
2157
|
+
| `plugin-list` load-error panel | *none* |
|
|
2158
|
+
| `plugin-detail` activity timelines | *none* |
|
|
2159
|
+
| `ui:empty` schema renderer | *none* |
|
|
2160
|
+
| `plugin-dashboard` `WidgetEmptyState` (#7063) | `status`, typed at the call site |
|
|
2161
|
+
| `plugin-kanban` empty board | `status`, typed at the call site |
|
|
2162
|
+
|
|
2163
|
+
The sibling states in the same file had always declared themselves —
|
|
2164
|
+
`DataLoadingState` is `role="status"`, `DataErrorState` is `role="alert"` — and
|
|
2165
|
+
the empty state alone declared nothing. So the surfaces were not legitimately
|
|
2166
|
+
differing: the ones that wanted the property had each hand-typed the same line,
|
|
2167
|
+
and the ones that had not yet done so were silently missing it. That is one
|
|
2168
|
+
platform default, copied by hand, at package level.
|
|
2169
|
+
|
|
2170
|
+
**It is a default, not a fixed attribute** — `role` is spread from props, so a
|
|
2171
|
+
call site keeps the last word. That is what makes this inert for the two ruled
|
|
2172
|
+
surfaces: both already pass `role="status"` explicitly and receive the identical
|
|
2173
|
+
attribute with or without it. Neither surface's behaviour changes.
|
|
2174
|
+
|
|
2175
|
+
**One real defect fell out of the measurement.** `plugin-list` renders its load
|
|
2176
|
+
FAILURE through `DataEmptyState`, borrowing it for layout — so a 403 saying "You
|
|
2177
|
+
don't have access" and a young object saying "Nothing here yet" were the same
|
|
2178
|
+
node shape, with no role on either. That panel now declares `role="alert"`,
|
|
2179
|
+
which both fixes the pre-existing indistinguishability and stops the new default
|
|
2180
|
+
from announcing an outage as a routine status.
|
|
2181
|
+
|
|
2182
|
+
Metric/KPI widgets are untouched: their carve-out (`rows.length === 0 &&
|
|
2183
|
+
!isMetric`) gates whether an empty state is rendered *at all*, upstream of this
|
|
2184
|
+
component, so a KPI still reads `0` rather than "no data".
|
|
2185
|
+
- a6d8b8d: Fix: a grid grouped by a field it does not also show as a column no longer collapses
|
|
2186
|
+
every row into one `(empty)` group (objectui#7179).
|
|
2187
|
+
|
|
2188
|
+
`$select` was built from the view's `columns` and nothing else, so a view declaring
|
|
2189
|
+
`grouping: { fields: [{ field: 'business_unit' }] }` on a field absent from its columns
|
|
2190
|
+
never asked the server for that field. It was `undefined` on every row by the time
|
|
2191
|
+
grouping ran, and the grouping label builder — correctly, for a genuinely empty value —
|
|
2192
|
+
answered `(empty)` for all of them. The result was one collapsible group holding every
|
|
2193
|
+
record, with no error, no warning and no empty state: a grid that looked like it grouped
|
|
2194
|
+
and did not, reading as "these records have no value for this field".
|
|
2195
|
+
|
|
2196
|
+
The grouping fields are now unioned into the projection, at both places it is built —
|
|
2197
|
+
`ObjectGrid` when it fetches for itself, and `ListView` when it fetches and hands the
|
|
2198
|
+
rows down. Lookup grouping fields are unioned into `$expand` as well: a `select` that
|
|
2199
|
+
fetches a bare foreign key without populating it buckets by raw id instead of by name,
|
|
2200
|
+
which is a different wrong answer rather than a fix.
|
|
2201
|
+
|
|
2202
|
+
Authors do not need to mirror a grouping field in `columns` any more. That was never
|
|
2203
|
+
required by `@objectstack/spec` — `grouping` is a sibling of `columns`, not a subset of
|
|
2204
|
+
it — and the neighbouring view kinds (kanban, gantt, timeline) already unioned their
|
|
2205
|
+
`groupByField` with no column needed. Refusing the configuration at author time was
|
|
2206
|
+
considered and rejected: it would make the grid the odd one out and reject working
|
|
2207
|
+
intent that the schema explicitly allows.
|
|
2208
|
+
|
|
2209
|
+
The union is guarded, and the guard is as load-bearing as the fix. A `grouping.fields[]`
|
|
2210
|
+
entry carries a bare string that has never been through column validation, and some
|
|
2211
|
+
backends answer an unknown `$select` key with an empty result set rather than ignoring
|
|
2212
|
+
it. Unioned unguarded, a grouping field naming something the object does not declare
|
|
2213
|
+
would have turned this bug into a strictly worse one — no rows at all, equally silently.
|
|
2214
|
+
Grouping fields are therefore intersected with the object's declared fields and passed
|
|
2215
|
+
through the same field-level-security gate as columns and predicate operands before they
|
|
2216
|
+
reach the query.
|
|
2217
|
+
- f626808: fix(app-shell,plugin-list): a list view's own `description` now reaches the screen
|
|
2218
|
+
|
|
2219
|
+
A `description` authored on a per-list-view entry (`listViews.<viewName>.description`)
|
|
2220
|
+
was validated, built and served correctly, then silently never rendered. Two
|
|
2221
|
+
independent cuts, both fixed here:
|
|
2222
|
+
|
|
2223
|
+
- **app-shell** — `ObjectView`'s `renderListView` relay copied ~46 keys off the
|
|
2224
|
+
active view onto the schema it hands `ListView` (`label`, `sort`, `filter`,
|
|
2225
|
+
`hiddenFields`, `inlineEdit`, `color`, `allowExport`, …) but had no rung for
|
|
2226
|
+
`description`, so the renderer could only ever see the object-level list's
|
|
2227
|
+
description and a per-view one was unreachable. It is relayed now, with the
|
|
2228
|
+
same two-rung shape as `label`. This is *not* the object's own
|
|
2229
|
+
`objectDef.description`, which stays the page header's subtitle.
|
|
2230
|
+
- **plugin-list** — `ListView` rendered `typeof description === 'string' ? … : ''`,
|
|
2231
|
+
a type test rather than a resolution. `ListViewSchema.description` is
|
|
2232
|
+
`I18nLabel`, so an inline locale map (`{ en, 'zh-CN' }`) — metadata the spec
|
|
2233
|
+
entitles an author to write — rendered a blank strip in every locale. It now
|
|
2234
|
+
resolves through the same shared helper the sibling `label` uses, and the
|
|
2235
|
+
visibility guard reads the resolved text, so a map with no usable entry drops
|
|
2236
|
+
the strip instead of reserving empty space for it.
|
|
2237
|
+
|
|
2238
|
+
`appearance.showDescription: false` still suppresses the description in both arms.
|
|
2239
|
+
- 3ed3eec: fix(plugin-form,plugin-list,plugin-view,react): read `SchemaRendererContext` as declared, not through a cast to `any` (objectui#7209)
|
|
2240
|
+
|
|
2241
|
+
Six reads of the renderer context erased its type. Five cast at the read —
|
|
2242
|
+
`useContext(SchemaRendererContext as React.Context<any>)` in the
|
|
2243
|
+
`embeddable-form` and `object-master-detail-form` bridges, in `ListViewBlock`
|
|
2244
|
+
and in `useResolvedDataSource`, and the same cast spelled with a bare
|
|
2245
|
+
`Context<any>` in `useElementDataSource` — and the `object-view` renderer's
|
|
2246
|
+
module re-declared the imported context as a `React.Context<any>`. Through
|
|
2247
|
+
that cast a read of a member the context does not declare compiled clean,
|
|
2248
|
+
which is how the phantom `ctx.formValues ?? ctx.data` channel retired by
|
|
2249
|
+
objectui#7206 went unnoticed. Each of these reads now sees `SchemaRendererContextType` as
|
|
2250
|
+
`@object-ui/react` declares it, so such a read is a compile error on the day it
|
|
2251
|
+
is written.
|
|
2252
|
+
|
|
2253
|
+
No runtime behaviour changes. Removing the casts surfaced no read of an
|
|
2254
|
+
undeclared member; it surfaced two places where the declared `null` ("no
|
|
2255
|
+
adapter bound") met a prop that does not admit it:
|
|
2256
|
+
|
|
2257
|
+
- `embeddable-form` handed that `null` to `EmbeddableForm`'s optional
|
|
2258
|
+
`dataSource`. It now collapses it to `undefined`, as
|
|
2259
|
+
`object-master-detail-form` already did; `EmbeddableForm` only ever tests the
|
|
2260
|
+
adapter for truthiness, so the two absences behave the same.
|
|
2261
|
+
- `object-view` / `view` hand it to `ObjectViewProps.dataSource`, which is
|
|
2262
|
+
declared required and stays so (objectui#7842). That one value now carries a
|
|
2263
|
+
narrow, commented assertion instead of the whole context being erased; the
|
|
2264
|
+
value passed is the same as before.
|
|
2265
|
+
|
|
2266
|
+
The published signatures of `useResolvedDataSource` and `useElementDataSource`
|
|
2267
|
+
do not move.
|
|
2268
|
+
- 5015fcf: A gantt list view no longer shows the record-count bar, because the bar describes a
|
|
2269
|
+
request that view does not draw (objectui#7210, half 1).
|
|
2270
|
+
|
|
2271
|
+
`ListView` renders one record-count bar for every `viewType`: the row count, the
|
|
2272
|
+
"Showing first N records. More data may be available." warning that goes with its own
|
|
2273
|
+
`$top: pageSize` query, and a rows-per-page selector that re-issues that query. On grid,
|
|
2274
|
+
kanban, calendar, gallery, timeline and map the bar is accurate — those renderers draw
|
|
2275
|
+
the `data` `ListView` hands down.
|
|
2276
|
+
|
|
2277
|
+
On `gantt` it is not. Measured, not inferred: the registered `object-gantt` renderer
|
|
2278
|
+
forwards no prop but `schema`, so the `data` prop never reaches the chart and
|
|
2279
|
+
`ObjectGantt` issues its own query — one that carries no `$top` at all. In a harness of
|
|
2280
|
+
18 rows with `pagination.pageSize: 6`, the chart drew all 18 while the bar under it read
|
|
2281
|
+
"6 records · Showing first 6 records. More data may be available." Because it is the
|
|
2282
|
+
only paging disclosure on the screen, a reader takes it as describing the chart; that
|
|
2283
|
+
reading already produced a wrong finding in an application repo, which cost a browser
|
|
2284
|
+
session to disprove. Authoring `pagination.pageSize` could not have fixed it either —
|
|
2285
|
+
the chart's request never carried a page size to begin with.
|
|
2286
|
+
|
|
2287
|
+
Scoped to `gantt` alone. Every other surface keeps its bar unchanged, warning included;
|
|
2288
|
+
a kanban over the same result set still reports its page honestly.
|
|
2289
|
+
|
|
2290
|
+
Not changed here, deliberately: the gantt's query still has no ceiling, and `ListView`
|
|
2291
|
+
still issues its own paged query for that view (its rows still drive the loading
|
|
2292
|
+
skeleton and load-error panel, `UserFilters`' option counts, and the client-side CSV /
|
|
2293
|
+
JSON export). Capping a non-grid view's fetch is an open maintainer decision — adding
|
|
2294
|
+
one would turn a complete schedule into a quietly truncated one — and this correction is
|
|
2295
|
+
right whichever way that lands.
|
|
2296
|
+
- 67dadd6: FLS-gate the `$expand` projection at both build sites (objectui#7215).
|
|
2297
|
+
|
|
2298
|
+
objectui#6898 closed field-level security on `$select`. `$expand` was left ungated at
|
|
2299
|
+
both projection sites — `ObjectGrid`'s own fetch and `ListView`'s `expandFields` memo —
|
|
2300
|
+
so a `lookup` / `master_detail` / `user` / `tree` field the current principal cannot
|
|
2301
|
+
read was still handed to the server for expansion. `$select` on a denied lookup asks for
|
|
2302
|
+
its bare foreign key; `$expand` on the same field asks the server to resolve it and
|
|
2303
|
+
return the related record, so the larger of the two disclosures was the ungated one.
|
|
2304
|
+
|
|
2305
|
+
**Reproduced before it was fixed**, as failing tests at both sites, and the same leak
|
|
2306
|
+
reaches further on the `ListView` path: that builder's `$select` gate drops the denied
|
|
2307
|
+
column and then adds the expand roots back unconditionally, so the denied field walked
|
|
2308
|
+
back into `$select` as well. Gating the expansion closes both halves.
|
|
2309
|
+
|
|
2310
|
+
**Grading, measured rather than assumed.** Against ObjectStack's own server this is
|
|
2311
|
+
defence-in-depth, exactly as objectui#6898 is: `plugin-security`'s
|
|
2312
|
+
`FieldMasker.maskRecord` deletes every unreadable key from each returned row, and
|
|
2313
|
+
objectql's expand path writes the resolved record back under that same key, so one
|
|
2314
|
+
statement removes the expanded object and the bare id alike; the expansion sub-read is
|
|
2315
|
+
itself gated (`__expandRead` takes the referenced object's full CRUD + RLS + FLS
|
|
2316
|
+
treatment). It is load-bearing for any backend that does not strip.
|
|
2317
|
+
|
|
2318
|
+
**Nothing a permitted view did stops working.** The gate judges the OUTPUT of
|
|
2319
|
+
`buildExpandFields`, which is already a subset of the object's declared
|
|
2320
|
+
reference-bearing fields, so the "`checkField` answers false for an undeclared key"
|
|
2321
|
+
trap cannot be reached and derived / host-joined columns are untouched. An unanswered
|
|
2322
|
+
permission policy filters nothing. `buildExpandFields` itself is unchanged.
|
|
2323
|
+
- ac257b3: FLS-gate the speculative half of `ListView`'s `$select` projection (objectui#7216).
|
|
2324
|
+
|
|
2325
|
+
`ListView` builds `$select` from two populations and, until now, asked them different
|
|
2326
|
+
questions. The user-declared `columns` were passed through `perms.checkField(...)` by
|
|
2327
|
+
objectui#6898. Everything the builder adds ON TOP — the kanban / gantt / timeline /
|
|
2328
|
+
calendar / gallery field bindings, the timeline's auto-added `status` / `priority`
|
|
2329
|
+
badge fields, and the operands harvested from row-action and conditional-formatting
|
|
2330
|
+
predicates (objectui#3501) — went through `addSpeculative`, which intersected them
|
|
2331
|
+
against the object's declared fields and then added them unconditionally.
|
|
2332
|
+
|
|
2333
|
+
The two gates it needed answer unrelated questions and neither substitutes for the
|
|
2334
|
+
other. The known-field gate keeps an **unknown** key out, because some backends answer
|
|
2335
|
+
an unknown `$select` key with an empty result set rather than ignoring it. The FLS gate
|
|
2336
|
+
keeps a **known but denied** key out, because sending it leaks the value at the server
|
|
2337
|
+
boundary even though the UI hides it. A field can be perfectly well-declared and still
|
|
2338
|
+
denied, and that was the case this path did not handle: a kanban grouped by a denied
|
|
2339
|
+
field, or a gantt bound to a denied date, still named it in the request.
|
|
2340
|
+
|
|
2341
|
+
**Reproduced before it was fixed.** Eight of fourteen new pins fail on the unmodified
|
|
2342
|
+
tree, each reaching the denied field only through a view binding — a denied *column* has
|
|
2343
|
+
been dropped since objectui#6898, so a pin naming one would have proved nothing.
|
|
2344
|
+
|
|
2345
|
+
The gate goes inside `addSpeculative` rather than at the five call sites, so every route
|
|
2346
|
+
into the speculative union is covered at once; gating call sites one at a time is how
|
|
2347
|
+
the asymmetry arose. It runs **after** the known-field intersection, the ordering
|
|
2348
|
+
objectui#7179 established: `checkField` answers false for an undeclared key, so asking
|
|
2349
|
+
it first would drop derived and computed bindings — and would be the reason they were
|
|
2350
|
+
dropped. The platform record columns (`created_at`, `owner_id`, the audit FKs) are
|
|
2351
|
+
carved out for the reason they already are elsewhere in this builder: every object
|
|
2352
|
+
carries them and none declares them, so no field policy mentions them and an FLS answer
|
|
2353
|
+
about them is always false. Without the carve-out a calendar bound to `created_at` would
|
|
2354
|
+
go blank for everybody.
|
|
2355
|
+
|
|
2356
|
+
**Nothing a permitted view did stops working.** A permitted binding is still projected,
|
|
2357
|
+
an unanswered permission policy filters nothing, and the projection is rebuilt when the
|
|
2358
|
+
policy answers — pinned, because a gate that only runs before `/me/permissions` resolves
|
|
2359
|
+
is a dead gate that passes almost every test written for it.
|
|
2360
|
+
|
|
2361
|
+
objectui#7179's `addGroupingField` wrapper is removed: its predicate was identical to
|
|
2362
|
+
the one now inside `addSpeculative`, and two spellings of one gate is the shape that
|
|
2363
|
+
lets them drift. That path keeps its behaviour and gains the FLS pin it never had.
|
|
2364
|
+
- 2c878be: Honour a gantt view's authored `navigation` — forward it down the gantt
|
|
2365
|
+
view-schema path, and give the component a destination for the non-overlay
|
|
2366
|
+
modes (objectui#7334).
|
|
2367
|
+
|
|
2368
|
+
**Leg 1, `@object-ui/plugin-list`.** `ObjectGantt` resolves
|
|
2369
|
+
`schema.navigation ?? { mode: 'drawer' }` and classifies four overlay modes
|
|
2370
|
+
(`drawer` / `modal` / `split` / `popover`). Nothing ever put `navigation` on the
|
|
2371
|
+
node it receives — `ListView`'s shared `baseProps` declares no such key and the
|
|
2372
|
+
`case 'gantt'` branch added only the data keys — so that fallback was the only
|
|
2373
|
+
branch ever taken. A gantt view authoring `navigation: { mode: 'page' }` (or
|
|
2374
|
+
`modal`, or `split`) opened a drawer instead, with no diagnostic. The key was
|
|
2375
|
+
already authorable and already read; this restores declared = enforced. No new
|
|
2376
|
+
authorable key is added.
|
|
2377
|
+
|
|
2378
|
+
The key is forwarded on the `case 'gantt'` branch and deliberately **not** on
|
|
2379
|
+
the shared `baseProps`, because every other child view resolves this same
|
|
2380
|
+
question through the `onRowClick` that `baseProps` already carries:
|
|
2381
|
+
`ObjectGrid`, `ObjectGallery`, `ObjectKanban`, `ObjectMap`, `ObjectTimeline` and
|
|
2382
|
+
`ObjectTree` hand that callback to `useNavigationOverlay` unconditionally, and
|
|
2383
|
+
the hook gives an external `onRowClick` full priority over any `navigation` — so
|
|
2384
|
+
on `baseProps` the authored config would arrive there only to be outranked.
|
|
2385
|
+
`ObjectCalendar` is the one that would genuinely change behaviour: it mirrors
|
|
2386
|
+
gantt's `navIsOverlay ? undefined : onRowClick`, so an authored non-overlay mode
|
|
2387
|
+
would stop it suppressing the host handler. Gantt is the only branch where
|
|
2388
|
+
forwarding settles the question rather than splitting it, because its wrapper
|
|
2389
|
+
drops host props entirely (objectui#7210 / objectui#7222) and there is no
|
|
2390
|
+
`onRowClick` there to outrank anything.
|
|
2391
|
+
|
|
2392
|
+
**Leg 2, `@object-ui/plugin-gantt`.** `ObjectGantt` now supplies its own
|
|
2393
|
+
`onNavigate`, so a click under an authored `page` or `new_window` actually
|
|
2394
|
+
reaches the record page. Leg 1 alone would have made `page` reachable and inert:
|
|
2395
|
+
`useNavigationOverlay`'s `page` branch calls `onNavigate` and returns with no
|
|
2396
|
+
fallback, this component supplied none, and its registration hands it no host
|
|
2397
|
+
`onRowClick` either — both carriers empty at once, turning an authored `page`
|
|
2398
|
+
from "a drawer opens, which is wrong" into "the click does nothing". The
|
|
2399
|
+
destination is the record-page href the component already computes for the
|
|
2400
|
+
drawer's full-page link, so the two cannot diverge; same-tab navigation uses the
|
|
2401
|
+
history plus `popstate` pair (matching `DashboardRenderer` and `PageHeader`),
|
|
2402
|
+
and `new_window` now opens the app-prefixed href rather than the hook's
|
|
2403
|
+
unprefixed fallback. No host prop is forwarded to the chart.
|
|
2404
|
+
|
|
2405
|
+
A view that authors no `navigation` still gets **no** key and still opens its
|
|
2406
|
+
drawer, so the component's default is unchanged for every gantt in the product
|
|
2407
|
+
today; an authored `none` stays inert.
|
|
2408
|
+
- 237e5b8: fix(plugin-list): stop re-issuing the identical records query on every visualization switch
|
|
2409
|
+
|
|
2410
|
+
`ListView`'s fetch effect named `currentView` in its dependency list, so
|
|
2411
|
+
switching between Grid, Kanban, Calendar and the other visualizations re-ran it
|
|
2412
|
+
— but the query it builds never reads the current visualization. The only way a
|
|
2413
|
+
surface reaches the wire is the `$skip` of its window, and on page 1 that number
|
|
2414
|
+
is 0 for a flat grid and 0 for every other surface, so the re-issued request was
|
|
2415
|
+
byte-for-byte the one already on screen. A board reached through a view switch
|
|
2416
|
+
therefore cost two identical `GET /api/v1/data/<object>?top=…&select=…` round
|
|
2417
|
+
trips before its first paint.
|
|
2418
|
+
|
|
2419
|
+
The effect is now keyed on the window itself (`fetchSkip`), and the two
|
|
2420
|
+
surface-shaped readings the fetch used to latch — the server total behind the
|
|
2421
|
+
grid's pager, and the row-cap banner's "…but the real total is known" half — are
|
|
2422
|
+
derived at render instead. Every re-fetch that changes the window is kept:
|
|
2423
|
+
turning the page still refetches, and leaving a paged grid from page 3 for a
|
|
2424
|
+
board that consumes the whole batch still refetches. What is gone is the
|
|
2425
|
+
round trip that changed nothing.
|
|
2426
|
+
- d327b9c: FLS-gate the `$expand` projection at the seven remaining `buildExpandFields`
|
|
2427
|
+
call sites (objectui#7429).
|
|
2428
|
+
|
|
2429
|
+
objectui#7215 / PR #7229 gated the two projection sites in its scope
|
|
2430
|
+
(`ObjectGrid`, `ListView`). objectui#7230 / PR #7428 gated four more
|
|
2431
|
+
(`ObjectCalendar`, `ObjectGantt`, `RecordDetailView`, `DetailView`). This
|
|
2432
|
+
closes the seven that were left: `ObjectKanban`, `ObjectTree`, `ObjectView`
|
|
2433
|
+
(the non-grid record-fetch effect), `ObjectMap`, `ObjectGallery`,
|
|
2434
|
+
`ObjectTimeline`, and the metadata-admin `PagePreview`'s record-binding fetch.
|
|
2435
|
+
|
|
2436
|
+
**All seven pass no column list at all**, which makes every one of them the
|
|
2437
|
+
sharp shape: `buildExpandFields` reads an absent column list as "no column
|
|
2438
|
+
restriction" and falls back to **every declared relation on the object**,
|
|
2439
|
+
denied ones included. So each of these components asked the server to resolve
|
|
2440
|
+
the object's full relation set by default, not by configuration — the
|
|
2441
|
+
ordinary shape of each surface, not a corner of it.
|
|
2442
|
+
|
|
2443
|
+
**`PagePreview` is the one site where the judged principal is not the page's
|
|
2444
|
+
eventual audience.** It calls the browser's own `fetch` with
|
|
2445
|
+
`credentials: 'include'` rather than `DataSource.find`, so it runs under
|
|
2446
|
+
whichever session is loading the Studio preview. Gating on that same session's
|
|
2447
|
+
`usePermissions()` is still the correct principal: it is exactly the request
|
|
2448
|
+
the browser is about to make, on its own credentials, regardless of who later
|
|
2449
|
+
opens the published page.
|
|
2450
|
+
|
|
2451
|
+
**Reproduced before it was fixed**, as a failing test per site (and, for the
|
|
2452
|
+
two sites — `ObjectView`, `PagePreview` — where the gate was implemented
|
|
2453
|
+
before its test was run red, a reverse-verification: the gate was reverted,
|
|
2454
|
+
all four denial-and-set pins on each went red, and the two deferral/positive
|
|
2455
|
+
control pins stayed green, before the gate was restored).
|
|
2456
|
+
|
|
2457
|
+
**Grading, measured rather than assumed** — the same reading objectui#6898,
|
|
2458
|
+
#7215 and #7230 recorded: against ObjectStack's own server this is
|
|
2459
|
+
defence-in-depth, not a live disclosure. `plugin-security`'s
|
|
2460
|
+
`FieldMasker.maskRecord` deletes every unreadable key from each returned row
|
|
2461
|
+
and objectql's expand path writes the resolved record back under that same
|
|
2462
|
+
key, so one statement removes the expanded object and the bare id alike; the
|
|
2463
|
+
expansion sub-read is itself gated (the referenced object's full CRUD + RLS +
|
|
2464
|
+
FLS treatment, objectstack#7626). It is load-bearing for any backend that does
|
|
2465
|
+
not strip, and the client-request side is real regardless.
|
|
2466
|
+
|
|
2467
|
+
**Nothing a permitted view did stops working.** The gate judges each site's
|
|
2468
|
+
`buildExpandFields` OUTPUT, which contains only the object's declared
|
|
2469
|
+
reference-bearing fields, so the "`checkField` answers false for an
|
|
2470
|
+
undeclared key" trap cannot be reached. An unanswered permission policy
|
|
2471
|
+
filters nothing. `buildExpandFields` itself is unchanged.
|
|
2472
|
+
|
|
2473
|
+
`@object-ui/permissions` is added to `dependencies` for `plugin-kanban`,
|
|
2474
|
+
`plugin-tree`, `plugin-map`, `plugin-timeline`, and `plugin-view` — the fifth
|
|
2475
|
+
one objectui#7429's own dependency count missed (it named four); `plugin-list`
|
|
2476
|
+
and `app-shell` already had it.
|
|
2477
|
+
- c6198c2: **Breaking for authored metadata:** `ComponentInput.label`, `ComponentInput.defaultValue` and
|
|
2478
|
+
`ComponentInput.advanced` are RETIRED on both faces (objectui#7493 item ① and objectui#7781;
|
|
2479
|
+
maintainer ruling A of 2026-09-06, immediate, no deprecation window; ADR-0049 enforce-or-remove).
|
|
2480
|
+
They are the three keys the manifest serializer does not forward, and nothing read them on any
|
|
2481
|
+
publication or consumption path.
|
|
2482
|
+
|
|
2483
|
+
No manifest ever published them, so no consumer could ever have read them. `sdui-parser`'s
|
|
2484
|
+
serializer (`packages/sdui-parser/src/index.ts`) forwards exactly seven keys per input — `name`,
|
|
2485
|
+
`type`, `of`, `required`, `enum`, `binding`, `description` — so a value authored under any of the three
|
|
2486
|
+
never reached `sdui.manifest.json`, the generated JSX `.d.ts`, or a diagnostic; its boundary type
|
|
2487
|
+
has no slot for them; the registry's data-source seam reads `name` only; and neither the designer
|
|
2488
|
+
nor the app-shell inspectors consult registry `inputs` at all. A structural census over every
|
|
2489
|
+
`inputs:` array in the repository (re-measured on this change's merge-base, `name` 951 and `type`
|
|
2490
|
+
951 as the controls) counted the writes: `label` 908, `defaultValue` 245, `advanced` 9 — written on
|
|
2491
|
+
nearly every registration, read by nothing.
|
|
2492
|
+
|
|
2493
|
+
FROM → TO, per key — all three **TOMBSTONED, not removed**, because the route was measured on
|
|
2494
|
+
the built face before it was chosen: `ComponentInputSchema` is a non-strict `z.object`, and an
|
|
2495
|
+
undeclared key parses GREEN and is silently STRIPPED, so a deletion would have swallowed 1,162
|
|
2496
|
+
authored values in silence. The tombstone is what makes the refusal loud and by name.
|
|
2497
|
+
|
|
2498
|
+
- `label?: string` → `label?: never` on the interface, `retirementTombstone()` on the Zod mirror.
|
|
2499
|
+
Migration: delete the key. An input is identified by its `name` on every path that reaches it;
|
|
2500
|
+
nothing ever rendered a label for it.
|
|
2501
|
+
- `defaultValue?: any` → `defaultValue?: never` / `retirementTombstone()`. Migration: delete the
|
|
2502
|
+
key. The renderer's own fallback read IS the default; tell the author about it in `description`,
|
|
2503
|
+
which IS published. (Tightening the type to `unknown` was ruled out: it closes no error class,
|
|
2504
|
+
since nothing reads the value.)
|
|
2505
|
+
- `advanced?: boolean` → `advanced?: never` / `retirementTombstone()`. Migration: delete the key.
|
|
2506
|
+
No designer surface ever hid an "advanced" input; there is nothing to write instead.
|
|
2507
|
+
|
|
2508
|
+
The retirement kit: `?: never` on `ComponentInput` (`packages/types/src/base.ts`), so authoring one
|
|
2509
|
+
is a `tsc` error at the registration site; `retirementTombstone()` on `ComponentInputSchema`
|
|
2510
|
+
(`packages/types/src/zod/base.zod.ts`), so an authored value is REFUSED at parse time with
|
|
2511
|
+
`code: 'invalid_type'`, the key named in the issue `path`, and the migration note as the message
|
|
2512
|
+
(one string, both channels). Pinned in
|
|
2513
|
+
`packages/types/src/__tests__/component-input-retired-keys-7493.test.ts`, which also holds a
|
|
2514
|
+
tree-scoped absence census over every `inputs:` array under `packages/**` and `apps/**`.
|
|
2515
|
+
|
|
2516
|
+
Accept-set change, stated plainly for reviewers: a document that sets any of the three keys on a
|
|
2517
|
+
`ComponentInput` used to parse GREEN (the value was then dropped by the serializer) and now parses
|
|
2518
|
+
RED. Every in-repo authoring site — 1,199 keys across 110 registration files, the three standalone
|
|
2519
|
+
`ComponentInput[]` arrays and the two named input arrays `tsc` found included — is deleted in the same change, as the ruling's split rule
|
|
2520
|
+
requires; the `WidgetRegistry` seam no longer copies the widget-manifest values onto the synthesized
|
|
2521
|
+
`ComponentInput` (they fed nothing), and the data-source declaration `ELEMENT_DATA_SOURCE_INPUT`
|
|
2522
|
+
drops its `label`. The patch entries on the other packages record exactly that: their registrations
|
|
2523
|
+
stop authoring inert keys, with no runtime or published-manifest change.
|
|
2524
|
+
|
|
2525
|
+
The nine test files that read `defaultValue` off a registration were re-pinned against the
|
|
2526
|
+
renderer's ACTUAL default (its own fallback read, or the `defaultProps` it ships) instead of the
|
|
2527
|
+
declaration that went away; two assertions that only restated the shadow default were dropped with
|
|
2528
|
+
the reason on the line.
|
|
2529
|
+
|
|
2530
|
+
The in-repo zero is what was measured. Whether anything OUTSIDE this repository writes these keys
|
|
2531
|
+
is not measurable from here (the objectui#5674 limit); converting such a write from a silent drop
|
|
2532
|
+
into a named refusal is exactly what the tombstones buy. `WidgetInput`'s own `label` /
|
|
2533
|
+
`defaultValue` / `advanced` (the widget-manifest face) stay declared and writable — nothing has
|
|
2534
|
+
ruled on that face; that it now has no reader either is recorded as objectui#7911.
|
|
2535
|
+
- fee1da5: `ListView` no longer reads relational keys off a list column while the object
|
|
2536
|
+
definition is still loading (objectui#7531).
|
|
2537
|
+
|
|
2538
|
+
Until the definition arrives, the filter and sort candidates are built from the
|
|
2539
|
+
view's declared `columns`. That fallback used to read `reference_to` /
|
|
2540
|
+
`reference`, `display_field` / `reference_field` and `id_field` off each
|
|
2541
|
+
column. `ListColumnSchema` declares none of them in either casing, so a
|
|
2542
|
+
spec-compliant producer can never put one there, and the read only kept a
|
|
2543
|
+
second, undeclared route alive (AGENTS.md 0.1). The candidate now carries what
|
|
2544
|
+
a column declares; its relational target (`referenceTo`, `displayField`,
|
|
2545
|
+
`idField`) comes from the object definition once it loads, as it already did.
|
|
2546
|
+
|
|
2547
|
+
Nothing changes for a spec-compliant view. A column that carried one of these
|
|
2548
|
+
keys anyway now gets the same pre-load candidate as a column without it: the
|
|
2549
|
+
filter's lookup value picker waits for the definition instead of taking the
|
|
2550
|
+
column's word for the target.
|
|
2551
|
+
- 245c7b7: The list view's capability gate now resolves `chart` (objectui#7544).
|
|
2552
|
+
|
|
2553
|
+
A `grid` list view that declared a complete `chart:` block and whitelisted
|
|
2554
|
+
`appearance.allowedVisualizations: ['grid', 'chart']` was never offered the Chart
|
|
2555
|
+
toggle. `availableViews` builds the resolvable set from each visualization's binding
|
|
2556
|
+
and intersects it with the whitelist (ADR-0047, whitelist ∩ resolvable); seven
|
|
2557
|
+
visualizations had a capability check there — kanban, gallery, calendar, timeline,
|
|
2558
|
+
gantt, map, tree — and `chart` had none, so the author's own whitelist was filtered
|
|
2559
|
+
down to nothing and the view fell back to `['grid']`. `chart` entered the offered set
|
|
2560
|
+
only through the "always allow switching back to the schema's own viewType" leg, i.e.
|
|
2561
|
+
only when the view was already `viewType: 'chart'`.
|
|
2562
|
+
|
|
2563
|
+
Both halves were spec-legal and authorable the whole time: `chart` is a
|
|
2564
|
+
`VisualizationTypeSchema` switcher target and `chart:` is a list-view key
|
|
2565
|
+
(`ListChartConfigSchema`). Only the gate never asked. This is `map`'s objectui#5042 one
|
|
2566
|
+
visualization over, and is fixed the same way.
|
|
2567
|
+
|
|
2568
|
+
**What now resolves.** A chart block that binds to names the author wrote: the ADR-0021
|
|
2569
|
+
shape (`dataset` plus at least one measure in `values`), or the legacy inline shape (a
|
|
2570
|
+
declared category — `xAxisField` / `categoryField` — together with a declared measure —
|
|
2571
|
+
`yAxisFields[0]` / `valueField`), in the view-level `chart` block or the legacy
|
|
2572
|
+
`options.chart` bag. A block that declares no binding stays unoffered: reaching the
|
|
2573
|
+
renderer with nothing declared lands on the legacy branch's invented `'name'` /
|
|
2574
|
+
`'value'` floor, and the switcher must not offer a route into it. That floor itself is
|
|
2575
|
+
unchanged here (objectui#7547).
|
|
2576
|
+
|
|
2577
|
+
The gate and the `case 'chart'` render branch now read ONE resolver
|
|
2578
|
+
(`resolveListChartBinding`) rather than two copies of the condition, so they cannot
|
|
2579
|
+
drift about what a usable chart block is. `schema.chart` also joins the memo's
|
|
2580
|
+
dependency array, so a block that arrives on a later render is seen.
|
|
2581
|
+
- 5a211b2: `ObjectGallery` waits for the object definition instead of querying twice
|
|
2582
|
+
(objectui#7903).
|
|
2583
|
+
|
|
2584
|
+
It sat outside the set objectui#6482 converged on the shared settled-schema gate
|
|
2585
|
+
— `ObjectKanban`, `ObjectView`, `ObjectCalendar` and `ObjectTree` were named
|
|
2586
|
+
there, `ObjectGantt` was ask 2 of objectui#7225 and `ObjectTimeline` was
|
|
2587
|
+
objectui#7895 — and nothing marked it a deliberate exclusion. It still held the
|
|
2588
|
+
object definition in a local `useState` fed by its own metadata effect, and
|
|
2589
|
+
listed that definition in the record-fetch effect's dependency array.
|
|
2590
|
+
|
|
2591
|
+
**User-visible.** Every object-bound gallery load issued **two** `find` calls
|
|
2592
|
+
instead of one: the first before the definition landed, with `buildExpandFields`
|
|
2593
|
+
seeing no fields and therefore carrying no `$expand` at all, and a second one
|
|
2594
|
+
after. The first paint was therefore a grid of cards rendered from raw
|
|
2595
|
+
foreign-key ids. After this change the gallery paints once, from a query that
|
|
2596
|
+
already carries its expansion.
|
|
2597
|
+
|
|
2598
|
+
Measured on the component with an instrumented renderer, one mount per hold,
|
|
2599
|
+
`getObjectSchema` held 0/1/2/3/4/5/6/7/8/9/10/15/25/50/100 ms, with
|
|
2600
|
+
`ObjectCalendar` as a positive control in the same run: before, 2 `find` calls
|
|
2601
|
+
with expand sets `[null, ['owner']]` at every hold, the issue order always
|
|
2602
|
+
`schema:issued, find(unexpanded), schema:settled, find(expanded)`, two distinct
|
|
2603
|
+
painted states, 3 late writes into the card grid after the first paint, and a
|
|
2604
|
+
first-paint time flat at 3-7 ms across the whole sweep; after, 1 `find` carrying
|
|
2605
|
+
`['owner']`, one painted state, 0 late writes, and a first paint that tracks the
|
|
2606
|
+
hold (9 ms at +3, 15 ms at +10, 35 ms at +25, 106 ms at +100). The control read
|
|
2607
|
+
1 `find` carrying `['owner']` and a hold-tracking first paint both before and
|
|
2608
|
+
after.
|
|
2609
|
+
|
|
2610
|
+
The cost this component was paying is a **two**-step paint, not the three-step
|
|
2611
|
+
one `ObjectCalendar` and `ObjectTimeline` each measured: those make `loading` an
|
|
2612
|
+
unconditional early return, so their re-run drops back to a placeholder in
|
|
2613
|
+
between, while this component's early return is `loading && !items.length` — the
|
|
2614
|
+
raw ids were replaced in place. Measured here rather than inherited from the
|
|
2615
|
+
matching shape, which is objectui#6482's own per-component standard.
|
|
2616
|
+
|
|
2617
|
+
The resolution half is now `useSettledSchema` from `@object-ui/react`, which
|
|
2618
|
+
settles on **every** exit — no source, no `getObjectSchema`, no object name, and
|
|
2619
|
+
a read that threw alike. That is what makes the gate safe: the replaced effect
|
|
2620
|
+
returned without settling on all four, which cost nothing while nothing waited on
|
|
2621
|
+
it and would have held a gated query open forever. Pinned by
|
|
2622
|
+
`ObjectGallery.fetchGate-7903.test.tsx`, including a gallery whose adapter
|
|
2623
|
+
exposes no `getObjectSchema` and one whose definition read rejects — both still
|
|
2624
|
+
query, unexpanded.
|
|
2625
|
+
|
|
2626
|
+
Two departures, each judged for this component rather than copied. Like
|
|
2627
|
+
`ObjectTimeline` and unlike `ObjectCalendar` / `ObjectGantt`, the metadata read
|
|
2628
|
+
is **not** disabled for a gallery whose records were authored inline: this
|
|
2629
|
+
component reads the definition on every path, for cell semantics and for ADR-0079
|
|
2630
|
+
card titles, not only to expand a query. And the gate window now holds the
|
|
2631
|
+
loading placeholder rather than showing "No items to display", which the two
|
|
2632
|
+
siblings get from their initial `loading` state and this one did not.
|
|
2633
|
+
- abc1b18: Add `isEmptyValue` to `@object-ui/core` — the weakest common claim about "is
|
|
2634
|
+
this value empty": `null`, `undefined`, the empty string, the empty array, and
|
|
2635
|
+
never a fifth member (objectui#8496, director seat, decision batch #86).
|
|
2636
|
+
|
|
2637
|
+
Five surfaces had each grown their own copy of those four members, and
|
|
2638
|
+
objectui#8481 was the third rediscovery of the same hole. They now call the
|
|
2639
|
+
shared floor and state their own answer against it: `record:details`'
|
|
2640
|
+
`hasCellValue` and `RelatedList` extend it with a trim, `BooleanCellRenderer`
|
|
2641
|
+
with every non-boolean, the date cells with every falsy scalar; `JsonCellRenderer`
|
|
2642
|
+
declines its `[]` member out loud (the array literal is drawn on purpose) and
|
|
2643
|
+
`FileCellRenderer` states "0 files" instead.
|
|
2644
|
+
|
|
2645
|
+
Two visible fixes come with it: a gallery card and a kanban card holding an
|
|
2646
|
+
empty array in a card field now OMIT that field, as they already did for `null`,
|
|
2647
|
+
instead of drawing a labelled "No value" em-dash for it.
|
|
2648
|
+
- c10bc4c: fix(plugin-list): `convertFilterGroupToAST` folds the operator, so a canonical value-less row emits its node instead of being dropped (objectui#9359)
|
|
2649
|
+
|
|
2650
|
+
NOT cosmetic, and not a new defect: a filter row carrying the spec's canonical
|
|
2651
|
+
operator spelling QUERIED WITH NO FILTER AT ALL and returned every record,
|
|
2652
|
+
while the filter panel showed the condition applied. Nothing errored and the
|
|
2653
|
+
result set looked plausible.
|
|
2654
|
+
|
|
2655
|
+
`convertFilterGroupToAST` read the row's operator RAW, against
|
|
2656
|
+
`VALUELESS_FILTER_BUILDER_OPERATORS` — the FilterBuilder's six camelCase
|
|
2657
|
+
dropdown ids. The completeness test it falls through to
|
|
2658
|
+
(`isFilterValueComplete`) DOES fold, through the spec's
|
|
2659
|
+
`normalizeFilterOperator`, to decide arity. So the two halves of one predicate
|
|
2660
|
+
spoke different vocabularies: a row spelled `is_null` missed the value-less
|
|
2661
|
+
short-circuit, landed on `scalar`, had its `value: ''` read as an unfinished
|
|
2662
|
+
row, and was dropped. Measured through the real converter on one `text` column:
|
|
2663
|
+
|
|
2664
|
+
isNull (dropdown id) -> ["title","isnull",null]
|
|
2665
|
+
is_null (canonical) -> [] <- the defect
|
|
2666
|
+
equals + value "acme" -> ["title","=","acme"] <- control, fires
|
|
2667
|
+
|
|
2668
|
+
WHO REACHES THIS READER — measured, not assumed. Its one production caller is
|
|
2669
|
+
`buildEffectiveFilter`, and the argument it converts is the list toolbar's own
|
|
2670
|
+
FilterBuilder group: live in the session, or restored per browser by
|
|
2671
|
+
`writeListFilterState`. Both carry the dropdown's camelCase ids, so the
|
|
2672
|
+
canonical spelling has no measured producer into this reader today. A SAVED
|
|
2673
|
+
view does NOT arrive here — its stored `ViewFilterRule[]` travels
|
|
2674
|
+
`schema.filter` into the base-filter argument of that same call and is lowered
|
|
2675
|
+
by `@object-ui/core`'s `toFilterNode` / `viewFilterRuleToNode`, which already
|
|
2676
|
+
folds: `{ field: 'closed_at', operator: 'is_null', value: '' }` lowers to
|
|
2677
|
+
`["closed_at","is_null",""]`, accepted by `isFilterAST`. So what is repaired is
|
|
2678
|
+
not a live saved-view outage. It is a reader that contradicted its OWN declared
|
|
2679
|
+
contract — the comment above it states it accepts both the FilterBuilder
|
|
2680
|
+
vocabulary and the `@objectstack/spec` `ViewFilterRule` vocabulary, and it
|
|
2681
|
+
dropped a COMPLETE row of the second one, emitting no filter rather than an
|
|
2682
|
+
error. A reader that drops a complete row is a defect whether or not today's
|
|
2683
|
+
saved-view path happens to pre-fold.
|
|
2684
|
+
|
|
2685
|
+
This is the same failure objectui#4744 repaired for the dropdown's own
|
|
2686
|
+
spellings, reached by the other vocabulary. Both raw reads in this function now
|
|
2687
|
+
fold, as do the two `isEmpty` / `isNotEmpty` arms that resolve to a null
|
|
2688
|
+
comparison ahead of `mapOperator` — leaving those on literal ids would have
|
|
2689
|
+
fixed `is_null` and left `is_empty` emitting a different node from its twin,
|
|
2690
|
+
which is this defect rather than a repair of it. All four canonical spellings
|
|
2691
|
+
now emit exactly what their camelCase twins emit.
|
|
2692
|
+
|
|
2693
|
+
**The exported set is unchanged, deliberately.** It states a fact about what the
|
|
2694
|
+
BUILDER'S DROPDOWN draws, and `app-shell`'s `foldFilterGroupToSpecRules`
|
|
2695
|
+
documents its own value-less set as that set PLUS the canonical spellings only
|
|
2696
|
+
that layer sees. Widening the export would have made that layer's deliberate
|
|
2697
|
+
compensation redundant by side effect, in a file nobody is editing. The defect
|
|
2698
|
+
was a reader that forgot to normalize its input, so the reader is what was
|
|
2699
|
+
repaired — the same shape the sibling repair used at the builder's value-input
|
|
2700
|
+
gate (objectui#9302). No published type or export changes, and no stored
|
|
2701
|
+
operator is rewritten: converting is not migrating.
|
|
2702
|
+
|
|
2703
|
+
Which operator vocabulary should WIN is a separate, still-open question and is
|
|
2704
|
+
not decided here. The `contains` / `icontains` boundary is untouched and pinned:
|
|
2705
|
+
the fold this reader routes through maps neither onto the other.
|
|
2706
|
+
|
|
2707
|
+
Superseded in this release by objectui#9306: the list toolbar's FilterBuilder
|
|
2708
|
+
now emits the protocol's canonical ids, so the canonical spelling IS a measured
|
|
2709
|
+
producer into this reader, and `VALUELESS_FILTER_BUILDER_OPERATORS` holds
|
|
2710
|
+
`is_empty`, `is_not_empty`, `is_null`, `is_not_null`, `exists` and `notExists` —
|
|
2711
|
+
its membership changed in this release by that change, still one id per
|
|
2712
|
+
operator. A group restored per browser carries whatever ids it was written
|
|
2713
|
+
with, so a group written before objectui#9306 can still reach this reader in
|
|
2714
|
+
camelCase. This reader is unchanged and answers both spellings alike: over the
|
|
2715
|
+
former dropdown ids and the ids they became (the list reader's leg of
|
|
2716
|
+
objectui#9306's census), every pair emits the same node except
|
|
2717
|
+
`containsCaseInsensitive`, which the toolbar never offered and which the builder
|
|
2718
|
+
folds onto `icontains` before a row leaves it. The vocabulary
|
|
2719
|
+
question this entry calls open is answered: the dropdown speaks the protocol's
|
|
2720
|
+
ids, and camelCase is the deprecated alias form.
|
|
2721
|
+
|
|
2722
|
+
**Correction, 2026-10-03 (objectui#10813).** The two `isEmpty` / `isNotEmpty` arms that
|
|
2723
|
+
"resolve to a null comparison ahead of `mapOperator`" are gone in this release. The empty pair
|
|
2724
|
+
now takes the same value-less path as `is_null`, and `mapOperator` emits the spec's `isempty` /
|
|
2725
|
+
`isnotempty`, which the spec lowers to `$empty`; every spelling of the pair still emits one node
|
|
2726
|
+
(`.changeset/10813-list-view-empty-operator.md`).
|
|
2727
|
+
- c1006ed: `ListView`'s record-detail overlay heading resolves an inline locale map.
|
|
2728
|
+
|
|
2729
|
+
`ListViewSchema.label` is an `I18nLabel` — a plain string **or** an inline locale
|
|
2730
|
+
map such as `{ en: 'Accounts', 'zh-CN': '客户' }`. The `detailTitle` computation
|
|
2731
|
+
handed the raw member into the interpolation options of
|
|
2732
|
+
`detail.recordDetailWithLabel`, so a map-valued label reached the interpolator as
|
|
2733
|
+
an object and the heading rendered `[object Object] Detail`. That value is
|
|
2734
|
+
`NavigationOverlay`'s `title` prop, which objectui#3426 established is the visible
|
|
2735
|
+
heading of the record-detail drawer / modal / split / popover — so the corruption
|
|
2736
|
+
was on user-visible chrome, in every locale, on both interpolators (the i18next
|
|
2737
|
+
one and the provider-less fallback that stringifies with `String`).
|
|
2738
|
+
|
|
2739
|
+
No compiler run could have named the site: `createSafeTranslation`'s options bag
|
|
2740
|
+
is declared as a record of `unknown`, which accepts the map without a diagnostic.
|
|
2741
|
+
Nothing but an executed assertion holds it, which is why the repair ships with
|
|
2742
|
+
pins on both interpolator legs.
|
|
2743
|
+
|
|
2744
|
+
The label is now resolved through `resolveI18nLabel` on the same `displayLocale`
|
|
2745
|
+
this component already uses for the view label and the nested `aria` bag —
|
|
2746
|
+
**before** the truthiness test rather than inside the branch, because
|
|
2747
|
+
`resolveI18nLabel` answers `undefined` for a map with no usable entry and `''`
|
|
2748
|
+
for an empty entry, and both have to fall through to the `objectName` branch the
|
|
2749
|
+
way a missing label always did. Every object is truthy, so a resolution placed
|
|
2750
|
+
inside the branch cannot do that.
|
|
2751
|
+
|
|
2752
|
+
Output for a string-valued `label` is unchanged, byte for byte, in every locale
|
|
2753
|
+
and with no provider mounted.
|
|
2754
|
+
- a883ec2: fix(plugin-list): gallery cards now pass a field's declared `scale` to the shared cell renderer, so a number or percent field declaring `scale` renders padded (`25.00%`) exactly as Grid and Detail do, instead of unpadded (`25%`). (objectui#9575)
|
|
2755
|
+
- f5e2fcb: Refuse a page size the contract already refuses before it reaches any of
|
|
2756
|
+
`ListView`'s six consumers of the resolved value, instead of forwarding it to
|
|
2757
|
+
the wire (objectui#9897).
|
|
2758
|
+
|
|
2759
|
+
One authored `pagination.pageSize` is resolved once in this view and then feeds
|
|
2760
|
+
six places: the `$top` window it fetches, the `$skip` step that turns the page,
|
|
2761
|
+
the has-more gate behind the "showing first N" cap, the page size handed down to
|
|
2762
|
+
the child grid, the record cap printed in that banner, and the rows-per-page
|
|
2763
|
+
control's own displayed value. The resolution was a bare `??` chain, and `??`
|
|
2764
|
+
rejects only `null` and `undefined` — so an authored `pageSize: 0` was not
|
|
2765
|
+
nullish and survived as a real page size into all six.
|
|
2766
|
+
|
|
2767
|
+
What the surviving value actually did, measured in the real renderer over a
|
|
2768
|
+
twelve-row fixture rather than inferred from the sibling repair: `$top: 0` went
|
|
2769
|
+
out on the wire, the data source was asked for nothing, nothing came back, and
|
|
2770
|
+
the view drew its EMPTY STATE — the child grid never rendered at all, so there
|
|
2771
|
+
was no table, no record-count bar and no pager, and nothing on screen named the
|
|
2772
|
+
cause. A negative left the same way (`$top: -10`). A non-integer is worse than
|
|
2773
|
+
silent: `25.5` reached the wire, became the child grid's page size, and turning
|
|
2774
|
+
the page asked for a fractional `$skip`; at a size below the row count the
|
|
2775
|
+
refused number was printed to the reader as the record cap ("showing first 2.5
|
|
2776
|
+
records").
|
|
2777
|
+
|
|
2778
|
+
All of it now goes through one resolver, mirroring the shape objectui#9853
|
|
2779
|
+
landed on `ObjectGrid` — one resolver at every entry is what keeps the answer
|
|
2780
|
+
single. A value the contract refuses is dropped, this view's own default is
|
|
2781
|
+
used, and one `console.warn` on the channel this component already uses for
|
|
2782
|
+
"you declared it, the renderer dropped it" names the member and the value. The
|
|
2783
|
+
warning is conditional: an absent key and a usable page size both stay silent.
|
|
2784
|
+
The runtime choice is covered as well, not only the authored one — an author can
|
|
2785
|
+
put a refused number in `pageSizeOptions`, and picking it used to overwrite a
|
|
2786
|
+
perfectly good authored size.
|
|
2787
|
+
|
|
2788
|
+
Refusing these is not this renderer choosing a meaning. `@objectstack/spec`
|
|
2789
|
+
declares the view pagination member a positive integer and its own suite pins
|
|
2790
|
+
the refusals under the names `should reject zero pageSize` and `should reject
|
|
2791
|
+
negative pageSize`; the `limit` this package's `ElementDataSourceMapping` lowers
|
|
2792
|
+
`pagination.pageSize` into is declared positive as well.
|
|
2793
|
+
|
|
2794
|
+
The fallback literal is now a named constant. Whether it belongs next to
|
|
2795
|
+
`ObjectGrid`'s own three defaults is **not** decided here — changing it changes
|
|
2796
|
+
what every list with no authored `pagination` fetches, which is a product
|
|
2797
|
+
decision rather than an execution seat's, and it is handed back as a question on
|
|
2798
|
+
the issue.
|
|
2799
|
+
- 3a54c39: Deliver an authored map `style` on the list-view and object-view paths (objectui#9950).
|
|
2800
|
+
|
|
2801
|
+
`ObjectMapConfigSchema` declares `style` ("MapLibre style URL/spec (overrides the
|
|
2802
|
+
public demo default)"), and both view flatteners dropped it before the renderer ever
|
|
2803
|
+
saw it. A view authoring `map: { style: 'https://…/style.json' }` parsed green, nothing
|
|
2804
|
+
refused it, nothing warned, and the map rendered on MapLibre's **public demo tiles**.
|
|
2805
|
+
|
|
2806
|
+
Each flattener's key whitelist is now a TOTAL spelling table,
|
|
2807
|
+
`FLAT_MAP_CONFIG_SPELLING`, mapping every key `ObjectMapConfigSchema` declares to the
|
|
2808
|
+
name the internal flat form uses for it. Every entry is the identity except `style`,
|
|
2809
|
+
which is delivered as `mapStyle`.
|
|
2810
|
+
|
|
2811
|
+
`mapStyle`, not `style`, because the top-level `style` key is `BaseSchema.style` —
|
|
2812
|
+
inline CSS, legal on every node. Collapsing the two namespaces onto one key is the
|
|
2813
|
+
defect objectui#5177 closed and this change deliberately keeps closed: the flatten
|
|
2814
|
+
still never writes a top-level `style`. `mapStyle` is a declared member of
|
|
2815
|
+
`ObjectMapSchema` and is the first spelling `getMapConfig` reads
|
|
2816
|
+
(`schema.mapStyle || schema.map?.style`), so the authored style now reaches the
|
|
2817
|
+
renderer without inventing an undeclared transport key.
|
|
2818
|
+
|
|
2819
|
+
An undeclared key in the `map` block still never reaches the product — that half of
|
|
2820
|
+
objectui#5177 is unchanged and pinned in both packages' `mapFlatten` suites.
|
|
2821
|
+
|
|
2822
|
+
**Why the anti-drift pins did not catch this.** Both sites already pinned their hand
|
|
2823
|
+
list against the declaration, and both pins were green. They compared the list against
|
|
2824
|
+
`Object.keys(ObjectMapConfigSchema.shape).filter((key) => key !== 'style')` — the
|
|
2825
|
+
comparison set had the same key subtracted from it that the whitelist was missing, so
|
|
2826
|
+
the pin agreed with the omission. Those pins now measure the relation "every declared
|
|
2827
|
+
key is delivered, under its flat spelling" against the declaration read whole, and each
|
|
2828
|
+
suite carries a control that feeds the pre-fix whitelist to the same assertion and
|
|
2829
|
+
shows it rejected. The spelling table is additionally a `Record` over every
|
|
2830
|
+
`keyof ObjectMapConfig`, so a key added to the declaration fails `tsc` until it is
|
|
2831
|
+
given a flat spelling.
|
|
2832
|
+
|
|
2833
|
+
**Migration.** None. Views that never authored `map.style` are byte-identical in
|
|
2834
|
+
behaviour; views that did now render with the style they declared instead of the
|
|
2835
|
+
public demo tiles.
|
|
2836
|
+
- 15236e0: `list-view` harvests row-action predicate fields from the OBJECT's `userActions` block only — a view's toolbar policy can no longer shadow it.
|
|
2837
|
+
|
|
2838
|
+
`userActions` names two different blocks. On a **view** it is toolbar policy —
|
|
2839
|
+
the spec's `UserActionsConfigSchema` (`sort`, `search`, `filter`, `refresh`,
|
|
2840
|
+
`rowHeight`, `addRecordForm`, `editInline`, `buttons`), which rejects `edit` by
|
|
2841
|
+
name. On an **object** it is the CRUD-predicate block (`edit` / `delete` /
|
|
2842
|
+
`create` carrying `visibleWhen` / `disabledWhen`, objectui#2614) — and that is
|
|
2843
|
+
the only shape `listViewPredicates` can read, since its loop skips every
|
|
2844
|
+
non-object value.
|
|
2845
|
+
|
|
2846
|
+
`ListView` read the key view-first when building the `$select` projection
|
|
2847
|
+
(`(schema as any).userActions ?? (objectDef as any)?.userActions`). A view
|
|
2848
|
+
carrying a perfectly legal toolbar block therefore shadowed the object's CRUD
|
|
2849
|
+
predicates, the harvest found none, and the predicate's operand left the
|
|
2850
|
+
projection. CEL then faults on the absent key, fails closed, and the row
|
|
2851
|
+
Edit/Delete button disappears for everyone with nothing pointing at the
|
|
2852
|
+
projection — objectui#3501's failure, reached with a success receipt at every
|
|
2853
|
+
step.
|
|
2854
|
+
|
|
2855
|
+
This is the sibling of the `plugin-grid` read site fixed in objectui#5426, and
|
|
2856
|
+
it was the worse of the two: `app-shell`'s `ObjectView` builds the view-level
|
|
2857
|
+
`userActions` it hands down as an object literal of two spreads, so the left
|
|
2858
|
+
operand was `{}` at worst — never nullish. The `??` never fell through, and the
|
|
2859
|
+
object's CRUD predicates were never consumed at all on that path, whether or
|
|
2860
|
+
not an author wrote any toolbar policy.
|
|
2861
|
+
|
|
2862
|
+
The harvest now reads the object block only. Both `userActions` read sites in
|
|
2863
|
+
`ListView.tsx` carry a comment naming the collision, and
|
|
2864
|
+
`__tests__/ListView.userActionsCollision.test.tsx` pins each clause of it: the
|
|
2865
|
+
two shapes, a producer that manufactures the view one, the harvest's blindness
|
|
2866
|
+
to it, and the projection that must keep the object's operand with a toolbar
|
|
2867
|
+
block — or an empty block — present on the view.
|
|
2868
|
+
|
|
2869
|
+
Toolbar policy itself is untouched — it was never read through this path.
|
|
2870
|
+
- ec9fdaa: `ListView` now resolves the nested `aria.ariaLabel` against the audience's locale
|
|
2871
|
+
instead of casting it to a string (objectui#5134).
|
|
2872
|
+
|
|
2873
|
+
`@objectstack/spec`'s `AriaPropsSchema` types `ariaLabel` as `I18nLabel` — a plain
|
|
2874
|
+
string **or** an inline locale map (`{ en: 'Accounts', 'zh-CN': '客户' }`). The only
|
|
2875
|
+
read site in this repo spread it into the DOM as
|
|
2876
|
+
`{ 'aria-label': schema.aria.ariaLabel as string }`, and `as string` is a cast, not a
|
|
2877
|
+
conversion: a map-valued label reached the DOM as `aria-label="[object Object]"`, which
|
|
2878
|
+
a screen reader announces as the list view's accessible name — in every locale. The
|
|
2879
|
+
read now goes through the spec's own `resolveI18nLabel` (the resolver four other
|
|
2880
|
+
in-repo read sites already use) against `useDisplayLocale()`.
|
|
2881
|
+
|
|
2882
|
+
⚠️ **Dated note, 2026-09-29 — the read-site count above does not re-derive —
|
|
2883
|
+
objectui#10979.** "the resolver four other in-repo read sites already use" above does not
|
|
2884
|
+
hold even at this change's own base. On the commit this change landed on (`e40b8579bd`),
|
|
2885
|
+
ten non-test source files already imported `@objectstack/spec`'s `resolveI18nLabel` (each
|
|
2886
|
+
as `resolveInlineI18nLabel`) and called it on 17 lines. Re-measured on `main` at
|
|
2887
|
+
`2eaf5be27`, fourteen non-test files besides `ListView` imported it, calling it on 27
|
|
2888
|
+
lines. The text above is kept as the reading of this change.
|
|
2889
|
+
|
|
2890
|
+
Reachability, stated plainly: the path is **live but unexercised**. `I18nLabel` was a
|
|
2891
|
+
plain `string` through `@objectstack/spec` 17.0.0-rc.5, so no stored map-valued label
|
|
2892
|
+
predates rc.6, and no measured author writes one today — but map values are legitimate
|
|
2893
|
+
and arrive via API/import, so an imported list view carrying
|
|
2894
|
+
`aria: { ariaLabel: { en: …, 'zh-CN': … } }` is spec-valid metadata that renders a wrong
|
|
2895
|
+
accessible name. This is the map form working as declared, not a defect users are
|
|
2896
|
+
currently hitting.
|
|
2897
|
+
|
|
2898
|
+
Behaviour on the string arm is byte-identical, including `''` (falsy before and after,
|
|
2899
|
+
so no attribute). One edge changes for the better: a map that matches no locale used to
|
|
2900
|
+
render `aria-label="[object Object]"` (`{}` is truthy) and now omits the attribute — an
|
|
2901
|
+
unnamed region beats a garbage-named one.
|
|
2902
|
+
|
|
2903
|
+
The **flat** `schema.ariaLabel` is deliberately untouched: it carries a different
|
|
2904
|
+
vocabulary (objectui's keyed `{ key, defaultValue?, params? }` ref, resolved by
|
|
2905
|
+
`SchemaRenderer`'s `resolveKeyedI18nLabel`), and neither resolver accepts the other's
|
|
2906
|
+
shape.
|
|
2907
|
+
- d6613a2: Two comment corrections in `ListView.tsx` (objectui#4559, objectui#4966). No runtime
|
|
2908
|
+
behaviour changes and the emitted bundle is byte-identical; the published `.d.ts` does
|
|
2909
|
+
change, which is why this is a `patch` rather than an empty frontmatter.
|
|
2910
|
+
|
|
2911
|
+
**objectui#4559 — the sort rationale stopped prescribing a formula field.** The comment
|
|
2912
|
+
block above the `sortFields` memo still called a formula field "the supported
|
|
2913
|
+
alternative (… which sorts like any text column)". Since objectui#4294 the
|
|
2914
|
+
`list.sortRelationalHint` string in this same file says the opposite ("Not a formula
|
|
2915
|
+
field: it is virtual, so no column is stored for it and the server refuses to sort by
|
|
2916
|
+
one"), the memo underneath filters formula out via `UNMATERIALIZED_FIELD_TYPES`, and the
|
|
2917
|
+
server answers such a sort with `400 INVALID_SORT` (objectstack#6994). The parenthetical
|
|
2918
|
+
now names the remedy the hint, the server's refusal and the README already share — a
|
|
2919
|
+
stored field that denormalizes the name onto this object, written when the source
|
|
2920
|
+
changes. This was the last copy of the retired advice in the repo.
|
|
2921
|
+
|
|
2922
|
+
**objectui#4966 — `formatActionLabel`'s docblock now sits above `formatActionLabel`.**
|
|
2923
|
+
It had drifted two declarations up, so the exported `parseSortConfig` carried two
|
|
2924
|
+
stacked leading comments and the helper carried none. This one was not cosmetic: because
|
|
2925
|
+
`parseSortConfig` is exported, `vite-plugin-dts` copied the misattributed block into
|
|
2926
|
+
`dist/ListView.d.ts`, so every consumer's editor hover and TypeDoc introduced the sort
|
|
2927
|
+
parser with a sentence about action labels. Moving the block removes it from the `.d.ts`;
|
|
2928
|
+
`formatActionLabel` is module-private, so its now-correct docblock does not appear there.
|
|
2929
|
+
It also matters to `scripts/check-spec-symbol-derivation.mjs`, whose rule 2 reads the
|
|
2930
|
+
comment block *attached* to a declaration — a misattributed docblock is the mechanism by
|
|
2931
|
+
which a claim gets scored against the wrong symbol. This block carries no spec-alignment
|
|
2932
|
+
phrase, so nothing fired today.
|
|
2933
|
+
|
|
2934
|
+
No tests accompany this change and none could: both edits are comment-only, and there is
|
|
2935
|
+
no runtime behaviour to pin. The `.d.ts` delta was measured with the package's real
|
|
2936
|
+
`vite build` before and after, not asserted.
|
|
2937
|
+
- fb336df: Move `lucide-react` from `^1.31.0` to `^1.43.0` in every package that declares it, and
|
|
2938
|
+
repair what the jump breaks, so icons resolved from a STRING keep drawing a glyph.
|
|
2939
|
+
|
|
2940
|
+
Measured once against the installed 1.43.0 artifact when this change was made; nothing in
|
|
2941
|
+
the repository re-derives these readings. Across the jump lucide removes no runtime export,
|
|
2942
|
+
no public type name and no `lucide-react/dynamic.mjs` name, and every name this repository
|
|
2943
|
+
imports from `lucide-react` resolves. Exactly one key leaves the runtime `icons` record:
|
|
2944
|
+
`Trash2`, retired in favour of `trash`.
|
|
2945
|
+
|
|
2946
|
+
What a user sees change:
|
|
2947
|
+
|
|
2948
|
+
- Icons the lazy icon seam draws (`resolveIcon`, objectui#9251) get their path data again.
|
|
2949
|
+
lucide 1.43.0 icon modules export their path data inside `__iconData` and no longer
|
|
2950
|
+
export `__iconNode`; the seam now reads both. Reading only `__iconNode` against 1.43.0
|
|
2951
|
+
leaves every such glyph an empty box, with nothing thrown or logged.
|
|
2952
|
+
- `DetailView`'s delete action and three schema-catalog examples spell their icon
|
|
2953
|
+
`trash`, not `trash-2`, so they keep resolving. The glyph is unchanged: 1.43.0's `trash`
|
|
2954
|
+
path data is byte-identical to the `trash-2` path data of 1.31.0 and 1.35.0. Anything
|
|
2955
|
+
that already authored `trash` now draws that same artwork, because lucide moved it under
|
|
2956
|
+
the `trash` name; a few other glyphs were also redrawn upstream.
|
|
2957
|
+
- Icons imported as COMPONENTS carry lucide 1.43.0's own classes: one class per declared
|
|
2958
|
+
alias (a spinner renders `class="lucide lucide-loader-circle lucide-loader-2 ..."`), and
|
|
2959
|
+
no longer the class lucide used to derive from the PascalCase key where that differs
|
|
2960
|
+
(`ArrowDown01` no longer carries `lucide-arrow-down01`). The canonical
|
|
2961
|
+
`lucide-<icon name>` class is still there, so a `.lucide-loader-circle` selector still
|
|
2962
|
+
matches.
|
|
2963
|
+
- Icons resolved from a STRING through the seam keep the classes they had: `lucide`, the
|
|
2964
|
+
canonical `lucide-<icon name>` class and the key-derived class. They do not carry
|
|
2965
|
+
lucide's per-alias classes. That is a maintainer ruling (objectui#8941, option C): the
|
|
2966
|
+
alias list lives only inside each lazily loaded icon module, and it was not moved into
|
|
2967
|
+
the eager name list. A selector that targets an alias class matches a component-imported
|
|
2968
|
+
icon and not a string-resolved one. This also dates the example in the objectui#9251
|
|
2969
|
+
entry: `trash-2` no longer resolves as a string, so no seam-drawn glyph carries
|
|
2970
|
+
`lucide-trash2 lucide-trash-2`; a digit-bearing name that still resolves, such as
|
|
2971
|
+
`arrow-down-0-1`, carries `lucide-arrow-down01 lucide-arrow-down-0-1`.
|
|
2972
|
+
- 6c6cee7: A RETIRED field-type spelling is now refused — out loud, once — by every
|
|
2973
|
+
field-type predicate in the renderer, not just by the widget road
|
|
2974
|
+
(objectui#4914, maintainer ruling B of 2026-08-18).
|
|
2975
|
+
|
|
2976
|
+
`@object-ui/fields` exports a single `isRetiredFieldType(t)` gate, and it runs
|
|
2977
|
+
ahead of six predicate faces that previously granted a retired spelling
|
|
2978
|
+
first-class treatment: the filter builder's operator buckets and its value
|
|
2979
|
+
control (`@object-ui/components`), the detail page's highlight-strip picker
|
|
2980
|
+
(`@object-ui/plugin-detail`), `normalizeFieldType` (`@object-ui/plugin-view`),
|
|
2981
|
+
the dashboard's `$expand` whitelist and `isLookupType`
|
|
2982
|
+
(`@object-ui/plugin-dashboard`), and the list toolbar's lookup-like filter
|
|
2983
|
+
control (`@object-ui/plugin-list`). Each one now fires the migration
|
|
2984
|
+
prescription on the console — once per spelling across all of them, never once
|
|
2985
|
+
per predicate — and then answers as it would for a spelling it does not
|
|
2986
|
+
recognise.
|
|
2987
|
+
|
|
2988
|
+
This closes the whole CLASS rather than one word: the gate is quantified over
|
|
2989
|
+
`RETIRED_FIELD_TYPES`, so the next retirement covers all seven consumers on the
|
|
2990
|
+
day it lands. It is the shape objectui#4932 and objectui#4942 already
|
|
2991
|
+
established for the form and inline-edit roads.
|
|
2992
|
+
|
|
2993
|
+
Measured before the change, and the reason the fix is a gate rather than a
|
|
2994
|
+
deletion: `owner` was not dead in these faces. `operatorsForFieldType('owner')`
|
|
2995
|
+
equalled the `user` bucket item for item, `computeLookupExpand` actively
|
|
2996
|
+
requested `$expand` for it, `isLookupType('owner')` was `true` alongside
|
|
2997
|
+
`reference`, and `normalizeFieldType('owner')` answered `'select'` exactly as
|
|
2998
|
+
`picklist` does. Deleting the members alone would have traded a visible
|
|
2999
|
+
contradiction for a SILENT degradation — a filter picker collapsing to a bare id
|
|
3000
|
+
box, `$expand` quietly stopping so cells show raw foreign-key ids — which is
|
|
3001
|
+
verbatim the failure mode `RETIRED_FIELD_TYPES`' own docblock exists to prevent.
|
|
3002
|
+
The gate keeps that fallback and adds the half that was missing: the author is
|
|
3003
|
+
told.
|
|
3004
|
+
|
|
3005
|
+
The boundary question is answered on record: `owner` arriving through a
|
|
3006
|
+
backend-vocabulary normalizer is an authoring error to refuse loudly, not
|
|
3007
|
+
legitimate foreign input to tolerate. The open backend vocabulary those
|
|
3008
|
+
normalizers exist for is untouched — `reference`, `picklist`, `money`, `int`,
|
|
3009
|
+
`datetime_tz` and the rest are equally absent from the spec's closed `FieldType`
|
|
3010
|
+
and are equally unretired, so they classify exactly as before.
|
|
3011
|
+
|
|
3012
|
+
`RETIRED_FIELD_TYPES`, `reportRetiredFieldType` and `resetRetiredFieldTypeReports`
|
|
3013
|
+
move to `@object-ui/core` and are re-exported from `@object-ui/fields`, so that
|
|
3014
|
+
package's published surface is unchanged apart from the newly ruled gate.
|
|
3015
|
+
`@object-ui/components` is a consumer of the gate and `@object-ui/fields`
|
|
3016
|
+
depends on it, so a single shared table could not live in `fields` — and a
|
|
3017
|
+
second copy would have meant a second dedupe set and two console lines for one
|
|
3018
|
+
spelling. No package gained a new dependency.
|
|
3019
|
+
|
|
3020
|
+
A retired spelling never loses a stored value: `retypeFilterValue` is
|
|
3021
|
+
deliberately not gated, and the refused filter row stays operable rather than
|
|
3022
|
+
drawing a blank operator trigger.
|
|
3023
|
+
- 42887e0: Repair five retired lucide icon spellings that reach a record-reading resolver, and pin
|
|
3024
|
+
the names against the runtime `icons` record so the next lucide bump goes red instead of
|
|
3025
|
+
silently blanking a glyph (objectui#5622).
|
|
3026
|
+
|
|
3027
|
+
lucide retires a spelling by dropping it from its runtime `icons` record while KEEPING it
|
|
3028
|
+
as a deprecated named export. A retired name therefore still imports, still type-checks,
|
|
3029
|
+
and still renders wherever it is used as a COMPONENT — and resolves to `null` wherever it
|
|
3030
|
+
is used as a STRING, because every string lookup here reads that record. Nothing goes red
|
|
3031
|
+
either way. Measured against the installed `lucide-react@1.31.0` (1767 record entries) at
|
|
3032
|
+
implementation time.
|
|
3033
|
+
|
|
3034
|
+
What a user sees change:
|
|
3035
|
+
|
|
3036
|
+
- `DetailView`'s mobile Edit action (`icon: 'edit'` → `'square-pen'`) draws its icon
|
|
3037
|
+
again. Its items become an `action:bar` schema whose renderers resolve `icon` through
|
|
3038
|
+
`renderers/action/resolve-icon.ts`, so the touch-breakpoint edit affordance had been
|
|
3039
|
+
drawing a label with nothing beside it. `Edit === SquarePen`, so the glyph is unchanged.
|
|
3040
|
+
- The `ui:icon` renderer's own declared default (`'smile'` → `'face-slightly-smiling'`, in
|
|
3041
|
+
both the registration `icon` and the `name` input's `defaultValue`) resolves again: the
|
|
3042
|
+
designer palette entry's glyph was blank, and an `icon` dropped from that palette
|
|
3043
|
+
rendered nothing plus a `console.warn`. `Smile === FaceSlightlySmiling`, so the palette
|
|
3044
|
+
looks exactly as it did.
|
|
3045
|
+
- `plugin-list`'s `ViewSwitcher` moves `Grid` → `Grid3x3`, `BarChart3` → `ChartColumn`
|
|
3046
|
+
(both identical objects, no visual change) and `GanttChartSquare` → `ChartGantt`. The
|
|
3047
|
+
gantt one IS a glyph change: it matches the spelling the sibling `plugin-view` switcher
|
|
3048
|
+
landed in objectui#5586, so one view type no longer draws two different icons depending
|
|
3049
|
+
on which switcher is on screen.
|
|
3050
|
+
|
|
3051
|
+
Four resolvability pins are added — in `plugin-detail`, `plugin-list`, `components` and
|
|
3052
|
+
alongside the `DeclaredActionsBar` fixtures. Each asserts `icons`-record MEMBERSHIP rather
|
|
3053
|
+
than resolvability, because every retired spelling repaired here is the SAME component
|
|
3054
|
+
object as its replacement (`Edit === SquarePen`, `Smile === FaceSlightlySmiling`,
|
|
3055
|
+
`Grid === Grid3x3`, `BarChart3 === ChartColumn`, `CheckCircle === CircleCheckBig`,
|
|
3056
|
+
`XCircle === CircleX` are all true): a pin that rendered the glyph, or reached for the
|
|
3057
|
+
export, would pass on the broken name. That is the blindness that let this ship.
|
|
3058
|
+
- Updated dependencies [7b10bef]
|
|
3059
|
+
- Updated dependencies [97abedc]
|
|
3060
|
+
- Updated dependencies [b46c58f]
|
|
3061
|
+
- Updated dependencies [a507334]
|
|
3062
|
+
- Updated dependencies [ad694ac]
|
|
3063
|
+
- Updated dependencies [6f96fca]
|
|
3064
|
+
- Updated dependencies [afb2284]
|
|
3065
|
+
- Updated dependencies [3ac2de8]
|
|
3066
|
+
- Updated dependencies [0aacecc]
|
|
3067
|
+
- Updated dependencies [63f4f92]
|
|
3068
|
+
- Updated dependencies [777fca2]
|
|
3069
|
+
- Updated dependencies [c131d9e]
|
|
3070
|
+
- Updated dependencies [5f00ff4]
|
|
3071
|
+
- Updated dependencies [c9e073a]
|
|
3072
|
+
- Updated dependencies [0361d6b]
|
|
3073
|
+
- Updated dependencies [7b395d8]
|
|
3074
|
+
- Updated dependencies [0879812]
|
|
3075
|
+
- Updated dependencies [8cedb0d]
|
|
3076
|
+
- Updated dependencies [6ce001a]
|
|
3077
|
+
- Updated dependencies [162621b]
|
|
3078
|
+
- Updated dependencies [4c6f549]
|
|
3079
|
+
- Updated dependencies [80c5412]
|
|
3080
|
+
- Updated dependencies [6cc910b]
|
|
3081
|
+
- Updated dependencies [061f5e8]
|
|
3082
|
+
- Updated dependencies [2dd4d3f]
|
|
3083
|
+
- Updated dependencies [e686f4d]
|
|
3084
|
+
- Updated dependencies [212c451]
|
|
3085
|
+
- Updated dependencies [a04b06d]
|
|
3086
|
+
- Updated dependencies [4ab4f1b]
|
|
3087
|
+
- Updated dependencies [b57107d]
|
|
3088
|
+
- Updated dependencies [e3ea4f9]
|
|
3089
|
+
- Updated dependencies [65f1e8d]
|
|
3090
|
+
- Updated dependencies [1f8ef0a]
|
|
3091
|
+
- Updated dependencies [bb5d4ee]
|
|
3092
|
+
- Updated dependencies [dc666f7]
|
|
3093
|
+
- Updated dependencies [3335767]
|
|
3094
|
+
- Updated dependencies [8b1f066]
|
|
3095
|
+
- Updated dependencies [af243c1]
|
|
3096
|
+
- Updated dependencies [cff4b77]
|
|
3097
|
+
- Updated dependencies [31938f0]
|
|
3098
|
+
- Updated dependencies [961ceaa]
|
|
3099
|
+
- Updated dependencies [f3f4e4c]
|
|
3100
|
+
- Updated dependencies [a05c350]
|
|
3101
|
+
- Updated dependencies [1dbb993]
|
|
3102
|
+
- Updated dependencies [2b5f509]
|
|
3103
|
+
- Updated dependencies [808f339]
|
|
3104
|
+
- Updated dependencies [6cf5999]
|
|
3105
|
+
- Updated dependencies [274e14a]
|
|
3106
|
+
- Updated dependencies [8c10f4f]
|
|
3107
|
+
- Updated dependencies [90dac98]
|
|
3108
|
+
- Updated dependencies [6096f20]
|
|
3109
|
+
- Updated dependencies [544aca2]
|
|
3110
|
+
- Updated dependencies [ea02938]
|
|
3111
|
+
- Updated dependencies [a14fb23]
|
|
3112
|
+
- Updated dependencies [ae98f1d]
|
|
3113
|
+
- Updated dependencies [f98eddf]
|
|
3114
|
+
- Updated dependencies [ce6bd99]
|
|
3115
|
+
- Updated dependencies [a5b08c9]
|
|
3116
|
+
- Updated dependencies [86982ac]
|
|
3117
|
+
- Updated dependencies [8c929e6]
|
|
3118
|
+
- Updated dependencies [cdefa2a]
|
|
3119
|
+
- Updated dependencies [2fc2a24]
|
|
3120
|
+
- Updated dependencies [0961d5e]
|
|
3121
|
+
- Updated dependencies [9d25b9b]
|
|
3122
|
+
- Updated dependencies [6276478]
|
|
3123
|
+
- Updated dependencies [5e67837]
|
|
3124
|
+
- Updated dependencies [ff14e29]
|
|
3125
|
+
- Updated dependencies [9b28151]
|
|
3126
|
+
- Updated dependencies [64563a9]
|
|
3127
|
+
- Updated dependencies [1a5003f]
|
|
3128
|
+
- Updated dependencies [09ab32b]
|
|
3129
|
+
- Updated dependencies [8acc51b]
|
|
3130
|
+
- Updated dependencies [ea9d17f]
|
|
3131
|
+
- Updated dependencies [45362a3]
|
|
3132
|
+
- Updated dependencies [2a943bf]
|
|
3133
|
+
- Updated dependencies [111fa4c]
|
|
3134
|
+
- Updated dependencies [93a689d]
|
|
3135
|
+
- Updated dependencies [4357a27]
|
|
3136
|
+
- Updated dependencies [e5f4343]
|
|
3137
|
+
- Updated dependencies [3261e64]
|
|
3138
|
+
- Updated dependencies [f5178a2]
|
|
3139
|
+
- Updated dependencies [2ad3671]
|
|
3140
|
+
- Updated dependencies [39bf246]
|
|
3141
|
+
- Updated dependencies [8740e86]
|
|
3142
|
+
- Updated dependencies [d22b37b]
|
|
3143
|
+
- Updated dependencies [8c0e550]
|
|
3144
|
+
- Updated dependencies [fde4caf]
|
|
3145
|
+
- Updated dependencies [caf0ed0]
|
|
3146
|
+
- Updated dependencies [1daf477]
|
|
3147
|
+
- Updated dependencies [3d614ea]
|
|
3148
|
+
- Updated dependencies [a7df45f]
|
|
3149
|
+
- Updated dependencies [6c2f3c5]
|
|
3150
|
+
- Updated dependencies [fb13e85]
|
|
3151
|
+
- Updated dependencies [c2d8659]
|
|
3152
|
+
- Updated dependencies [6516320]
|
|
3153
|
+
- Updated dependencies [98b1a7c]
|
|
3154
|
+
- Updated dependencies [e0f8202]
|
|
3155
|
+
- Updated dependencies [ac2d6f1]
|
|
3156
|
+
- Updated dependencies [9fbbb17]
|
|
3157
|
+
- Updated dependencies [c3a26cc]
|
|
3158
|
+
- Updated dependencies [a66e58e]
|
|
3159
|
+
- Updated dependencies [d89492c]
|
|
3160
|
+
- Updated dependencies [9a5f998]
|
|
3161
|
+
- Updated dependencies [1c5ee33]
|
|
3162
|
+
- Updated dependencies [9327397]
|
|
3163
|
+
- Updated dependencies [17cc3a3]
|
|
3164
|
+
- Updated dependencies [a9c5ea0]
|
|
3165
|
+
- Updated dependencies [02e6d36]
|
|
3166
|
+
- Updated dependencies [4758b33]
|
|
3167
|
+
- Updated dependencies [4758b33]
|
|
3168
|
+
- Updated dependencies [4758b33]
|
|
3169
|
+
- Updated dependencies [f905090]
|
|
3170
|
+
- Updated dependencies [3cd6c5e]
|
|
3171
|
+
- Updated dependencies [5bb855d]
|
|
3172
|
+
- Updated dependencies [9c78ebe]
|
|
3173
|
+
- Updated dependencies [12809a5]
|
|
3174
|
+
- Updated dependencies [9c78ebe]
|
|
3175
|
+
- Updated dependencies [12809a5]
|
|
3176
|
+
- Updated dependencies [7afc81d]
|
|
3177
|
+
- Updated dependencies [f9c06ef]
|
|
3178
|
+
- Updated dependencies [5ad3b88]
|
|
3179
|
+
- Updated dependencies [f9d772b]
|
|
3180
|
+
- Updated dependencies [bf14b64]
|
|
3181
|
+
- Updated dependencies [4345558]
|
|
3182
|
+
- Updated dependencies [97b6c21]
|
|
3183
|
+
- Updated dependencies [26ca2ad]
|
|
3184
|
+
- Updated dependencies [41ae65b]
|
|
3185
|
+
- Updated dependencies [baac95a]
|
|
3186
|
+
- Updated dependencies [13220af]
|
|
3187
|
+
- Updated dependencies [29b45f6]
|
|
3188
|
+
- Updated dependencies [39b8d51]
|
|
3189
|
+
- Updated dependencies [17b323e]
|
|
3190
|
+
- Updated dependencies [b956e69]
|
|
3191
|
+
- Updated dependencies [b32e7de]
|
|
3192
|
+
- Updated dependencies [ff94a12]
|
|
3193
|
+
- Updated dependencies [33e58d8]
|
|
3194
|
+
- Updated dependencies [256b4c9]
|
|
3195
|
+
- Updated dependencies [97672ba]
|
|
3196
|
+
- Updated dependencies [c5ec15c]
|
|
3197
|
+
- Updated dependencies [fec3b1a]
|
|
3198
|
+
- Updated dependencies [b8e0941]
|
|
3199
|
+
- Updated dependencies [0c50f18]
|
|
3200
|
+
- Updated dependencies [1dae95a]
|
|
3201
|
+
- Updated dependencies [e32dae1]
|
|
3202
|
+
- Updated dependencies [d0c0c7f]
|
|
3203
|
+
- Updated dependencies [30b11ad]
|
|
3204
|
+
- Updated dependencies [4aebea0]
|
|
3205
|
+
- Updated dependencies [f976774]
|
|
3206
|
+
- Updated dependencies [3d6badf]
|
|
3207
|
+
- Updated dependencies [25cb364]
|
|
3208
|
+
- Updated dependencies [a60539b]
|
|
3209
|
+
- Updated dependencies [de1b879]
|
|
3210
|
+
- Updated dependencies [c6678b1]
|
|
3211
|
+
- Updated dependencies [0638322]
|
|
3212
|
+
- Updated dependencies [e3782d2]
|
|
3213
|
+
- Updated dependencies [db0beb2]
|
|
3214
|
+
- Updated dependencies [997ce38]
|
|
3215
|
+
- Updated dependencies [ae0b9d3]
|
|
3216
|
+
- Updated dependencies [ad1785c]
|
|
3217
|
+
- Updated dependencies [3b469c8]
|
|
3218
|
+
- Updated dependencies [990a2d6]
|
|
3219
|
+
- Updated dependencies [990a2d6]
|
|
3220
|
+
- Updated dependencies [1c84036]
|
|
3221
|
+
- Updated dependencies [1c84036]
|
|
3222
|
+
- Updated dependencies [6650259]
|
|
3223
|
+
- Updated dependencies [4f8b7f8]
|
|
3224
|
+
- Updated dependencies [9e6619f]
|
|
3225
|
+
- Updated dependencies [f6ae5e2]
|
|
3226
|
+
- Updated dependencies [eb97ce6]
|
|
3227
|
+
- Updated dependencies [7343376]
|
|
3228
|
+
- Updated dependencies [b2683a2]
|
|
3229
|
+
- Updated dependencies [dded788]
|
|
3230
|
+
- Updated dependencies [b45d463]
|
|
3231
|
+
- Updated dependencies [54a7830]
|
|
3232
|
+
- Updated dependencies [f3135a4]
|
|
3233
|
+
- Updated dependencies [b5696d3]
|
|
3234
|
+
- Updated dependencies [3f9d926]
|
|
3235
|
+
- Updated dependencies [e978ed5]
|
|
3236
|
+
- Updated dependencies [6a7f24e]
|
|
3237
|
+
- Updated dependencies [b3c96d6]
|
|
3238
|
+
- Updated dependencies [8d0ca91]
|
|
3239
|
+
- Updated dependencies [c30c8dd]
|
|
3240
|
+
- Updated dependencies [244d516]
|
|
3241
|
+
- Updated dependencies [deca847]
|
|
3242
|
+
- Updated dependencies [ac15833]
|
|
3243
|
+
- Updated dependencies [ac15833]
|
|
3244
|
+
- Updated dependencies [e2dffc9]
|
|
3245
|
+
- Updated dependencies [328abeb]
|
|
3246
|
+
- Updated dependencies [dd0d78f]
|
|
3247
|
+
- Updated dependencies [24d3e65]
|
|
3248
|
+
- Updated dependencies [95a7c8d]
|
|
3249
|
+
- Updated dependencies [e227156]
|
|
3250
|
+
- Updated dependencies [cc4e476]
|
|
3251
|
+
- Updated dependencies [92970c4]
|
|
3252
|
+
- Updated dependencies [d570eaa]
|
|
3253
|
+
- Updated dependencies [4b742f4]
|
|
3254
|
+
- Updated dependencies [42687ba]
|
|
3255
|
+
- Updated dependencies [6cd8f66]
|
|
3256
|
+
- Updated dependencies [24a0f14]
|
|
3257
|
+
- Updated dependencies [797a30f]
|
|
3258
|
+
- Updated dependencies [b4075c0]
|
|
3259
|
+
- Updated dependencies [9b85600]
|
|
3260
|
+
- Updated dependencies [e327c89]
|
|
3261
|
+
- Updated dependencies [2eaf5be]
|
|
3262
|
+
- Updated dependencies [bf7ab35]
|
|
3263
|
+
- Updated dependencies [99878d8]
|
|
3264
|
+
- Updated dependencies [3c13675]
|
|
3265
|
+
- Updated dependencies [63ab761]
|
|
3266
|
+
- Updated dependencies [0eb9f36]
|
|
3267
|
+
- Updated dependencies [ae582b7]
|
|
3268
|
+
- Updated dependencies [51c2949]
|
|
3269
|
+
- Updated dependencies [a4b017e]
|
|
3270
|
+
- Updated dependencies [8732846]
|
|
3271
|
+
- Updated dependencies [559a2e2]
|
|
3272
|
+
- Updated dependencies [db11afd]
|
|
3273
|
+
- Updated dependencies [154075a]
|
|
3274
|
+
- Updated dependencies [582edef]
|
|
3275
|
+
- Updated dependencies [19f484f]
|
|
3276
|
+
- Updated dependencies [0a78a20]
|
|
3277
|
+
- Updated dependencies [615346d]
|
|
3278
|
+
- Updated dependencies [75dcc81]
|
|
3279
|
+
- Updated dependencies [55a12a8]
|
|
3280
|
+
- Updated dependencies [edfcf5a]
|
|
3281
|
+
- Updated dependencies [0a3e540]
|
|
3282
|
+
- Updated dependencies [c021b35]
|
|
3283
|
+
- Updated dependencies [f61dab1]
|
|
3284
|
+
- Updated dependencies [b0a05dd]
|
|
3285
|
+
- Updated dependencies [dd5ff19]
|
|
3286
|
+
- Updated dependencies [54997ff]
|
|
3287
|
+
- Updated dependencies [81f8498]
|
|
3288
|
+
- Updated dependencies [a782fa7]
|
|
3289
|
+
- Updated dependencies [1d6a23d]
|
|
3290
|
+
- Updated dependencies [0645133]
|
|
3291
|
+
- Updated dependencies [76e9df0]
|
|
3292
|
+
- Updated dependencies [0ecaa7d]
|
|
3293
|
+
- Updated dependencies [b24f93a]
|
|
3294
|
+
- Updated dependencies [6158e4c]
|
|
3295
|
+
- Updated dependencies [cff8641]
|
|
3296
|
+
- Updated dependencies [c27b575]
|
|
3297
|
+
- Updated dependencies [84b275c]
|
|
3298
|
+
- Updated dependencies [0e6e76b]
|
|
3299
|
+
- Updated dependencies [bf43afa]
|
|
3300
|
+
- Updated dependencies [385ebc5]
|
|
3301
|
+
- Updated dependencies [3a0e7ab]
|
|
3302
|
+
- Updated dependencies [858eafb]
|
|
3303
|
+
- Updated dependencies [a8c5509]
|
|
3304
|
+
- Updated dependencies [c2a8d23]
|
|
3305
|
+
- Updated dependencies [0ffc423]
|
|
3306
|
+
- Updated dependencies [3cc4fe5]
|
|
3307
|
+
- Updated dependencies [cd5b19a]
|
|
3308
|
+
- Updated dependencies [cd5b19a]
|
|
3309
|
+
- Updated dependencies [17dc167]
|
|
3310
|
+
- Updated dependencies [20d23be]
|
|
3311
|
+
- Updated dependencies [20d23be]
|
|
3312
|
+
- Updated dependencies [2e3da72]
|
|
3313
|
+
- Updated dependencies [1263e40]
|
|
3314
|
+
- Updated dependencies [e6bc087]
|
|
3315
|
+
- Updated dependencies [9419df1]
|
|
3316
|
+
- Updated dependencies [8bab157]
|
|
3317
|
+
- Updated dependencies [a7557a7]
|
|
3318
|
+
- Updated dependencies [7d074ba]
|
|
3319
|
+
- Updated dependencies [6158e4c]
|
|
3320
|
+
- Updated dependencies [6158e4c]
|
|
3321
|
+
- Updated dependencies [52aad5c]
|
|
3322
|
+
- Updated dependencies [18d1a0a]
|
|
3323
|
+
- Updated dependencies [7fed09d]
|
|
3324
|
+
- Updated dependencies [c1763e5]
|
|
3325
|
+
- Updated dependencies [58da8ae]
|
|
3326
|
+
- Updated dependencies [a8b9889]
|
|
3327
|
+
- Updated dependencies [138ad45]
|
|
3328
|
+
- Updated dependencies [138ad45]
|
|
3329
|
+
- Updated dependencies [5262f7d]
|
|
3330
|
+
- Updated dependencies [6aa029b]
|
|
3331
|
+
- Updated dependencies [2124d04]
|
|
3332
|
+
- Updated dependencies [be52115]
|
|
3333
|
+
- Updated dependencies [770cc5b]
|
|
3334
|
+
- Updated dependencies [1a88ce2]
|
|
3335
|
+
- Updated dependencies [a1a44d6]
|
|
3336
|
+
- Updated dependencies [e0a9c67]
|
|
3337
|
+
- Updated dependencies [5638529]
|
|
3338
|
+
- Updated dependencies [5638529]
|
|
3339
|
+
- Updated dependencies [d0fba91]
|
|
3340
|
+
- Updated dependencies [c476be0]
|
|
3341
|
+
- Updated dependencies [c82ff39]
|
|
3342
|
+
- Updated dependencies [6c3da53]
|
|
3343
|
+
- Updated dependencies [31987bd]
|
|
3344
|
+
- Updated dependencies [3c3ce15]
|
|
3345
|
+
- Updated dependencies [063119f]
|
|
3346
|
+
- Updated dependencies [e100589]
|
|
3347
|
+
- Updated dependencies [304f611]
|
|
3348
|
+
- Updated dependencies [e46ee77]
|
|
3349
|
+
- Updated dependencies [f9c8c4e]
|
|
3350
|
+
- Updated dependencies [6e9c8d2]
|
|
3351
|
+
- Updated dependencies [9547063]
|
|
3352
|
+
- Updated dependencies [3f6efd6]
|
|
3353
|
+
- Updated dependencies [52c95a1]
|
|
3354
|
+
- Updated dependencies [55d18c6]
|
|
3355
|
+
- Updated dependencies [c4ab6d0]
|
|
3356
|
+
- Updated dependencies [0e9058b]
|
|
3357
|
+
- Updated dependencies [c4ab6d0]
|
|
3358
|
+
- Updated dependencies [5988b6b]
|
|
3359
|
+
- Updated dependencies [6158e4c]
|
|
3360
|
+
- Updated dependencies [00ccdf7]
|
|
3361
|
+
- Updated dependencies [9d9ed54]
|
|
3362
|
+
- Updated dependencies [6007dd4]
|
|
3363
|
+
- Updated dependencies [4a1adb7]
|
|
3364
|
+
- Updated dependencies [4a1adb7]
|
|
3365
|
+
- Updated dependencies [4a1adb7]
|
|
3366
|
+
- Updated dependencies [185b7a0]
|
|
3367
|
+
- Updated dependencies [50c73fe]
|
|
3368
|
+
- Updated dependencies [ca3de72]
|
|
3369
|
+
- Updated dependencies [83e3f83]
|
|
3370
|
+
- Updated dependencies [401611b]
|
|
3371
|
+
- Updated dependencies [2c0ddf2]
|
|
3372
|
+
- Updated dependencies [4abc0aa]
|
|
3373
|
+
- Updated dependencies [f560ded]
|
|
3374
|
+
- Updated dependencies [2b188fa]
|
|
3375
|
+
- Updated dependencies [b654d4e]
|
|
3376
|
+
- Updated dependencies [f68e0a0]
|
|
3377
|
+
- Updated dependencies [aea682a]
|
|
3378
|
+
- Updated dependencies [fcdc8ec]
|
|
3379
|
+
- Updated dependencies [2d576e4]
|
|
3380
|
+
- Updated dependencies [8366acc]
|
|
3381
|
+
- Updated dependencies [95e58a3]
|
|
3382
|
+
- Updated dependencies [9d7419b]
|
|
3383
|
+
- Updated dependencies [fc7db05]
|
|
3384
|
+
- Updated dependencies [9ed8d0f]
|
|
3385
|
+
- Updated dependencies [c73cdb5]
|
|
3386
|
+
- Updated dependencies [6f5719e]
|
|
3387
|
+
- Updated dependencies [d0097af]
|
|
3388
|
+
- Updated dependencies [432882b]
|
|
3389
|
+
- Updated dependencies [64dae8e]
|
|
3390
|
+
- Updated dependencies [b06e374]
|
|
3391
|
+
- Updated dependencies [06a8af5]
|
|
3392
|
+
- Updated dependencies [6a91586]
|
|
3393
|
+
- Updated dependencies [a04d7c6]
|
|
3394
|
+
- Updated dependencies [5ccc500]
|
|
3395
|
+
- Updated dependencies [f3c2bb0]
|
|
3396
|
+
- Updated dependencies [978507b]
|
|
3397
|
+
- Updated dependencies [778138e]
|
|
3398
|
+
- Updated dependencies [9801765]
|
|
3399
|
+
- Updated dependencies [9cebfca]
|
|
3400
|
+
- Updated dependencies [460575f]
|
|
3401
|
+
- Updated dependencies [d796c8d]
|
|
3402
|
+
- Updated dependencies [594704f]
|
|
3403
|
+
- Updated dependencies [d3995fe]
|
|
3404
|
+
- Updated dependencies [1b1d772]
|
|
3405
|
+
- Updated dependencies [d88e20f]
|
|
3406
|
+
- Updated dependencies [2d7304d]
|
|
3407
|
+
- Updated dependencies [636b236]
|
|
3408
|
+
- Updated dependencies [4172589]
|
|
3409
|
+
- Updated dependencies [d6d8fb9]
|
|
3410
|
+
- Updated dependencies [64d624d]
|
|
3411
|
+
- Updated dependencies [053fdc8]
|
|
3412
|
+
- Updated dependencies [41b7ce3]
|
|
3413
|
+
- Updated dependencies [ae476b8]
|
|
3414
|
+
- Updated dependencies [39f4309]
|
|
3415
|
+
- Updated dependencies [95bad12]
|
|
3416
|
+
- Updated dependencies [d2fb6ef]
|
|
3417
|
+
- Updated dependencies [7cd3987]
|
|
3418
|
+
- Updated dependencies [ee3b878]
|
|
3419
|
+
- Updated dependencies [e304a4e]
|
|
3420
|
+
- Updated dependencies [fda49e5]
|
|
3421
|
+
- Updated dependencies [490d9a9]
|
|
3422
|
+
- Updated dependencies [fc62bb4]
|
|
3423
|
+
- Updated dependencies [41df893]
|
|
3424
|
+
- Updated dependencies [0cba1b7]
|
|
3425
|
+
- Updated dependencies [00f3eb5]
|
|
3426
|
+
- Updated dependencies [1ec291c]
|
|
3427
|
+
- Updated dependencies [453dbaa]
|
|
3428
|
+
- Updated dependencies [95f8704]
|
|
3429
|
+
- Updated dependencies [f8cdbf2]
|
|
3430
|
+
- Updated dependencies [69a2163]
|
|
3431
|
+
- Updated dependencies [24e027e]
|
|
3432
|
+
- Updated dependencies [2c3cd1b]
|
|
3433
|
+
- Updated dependencies [e176053]
|
|
3434
|
+
- Updated dependencies [e30ed15]
|
|
3435
|
+
- Updated dependencies [90665e0]
|
|
3436
|
+
- Updated dependencies [8d3a529]
|
|
3437
|
+
- Updated dependencies [5ac2e2c]
|
|
3438
|
+
- Updated dependencies [194fae1]
|
|
3439
|
+
- Updated dependencies [7e19d03]
|
|
3440
|
+
- Updated dependencies [b08b7eb]
|
|
3441
|
+
- Updated dependencies [1e946c9]
|
|
3442
|
+
- Updated dependencies [546ddf7]
|
|
3443
|
+
- Updated dependencies [864154e]
|
|
3444
|
+
- Updated dependencies [b023625]
|
|
3445
|
+
- Updated dependencies [75bd83d]
|
|
3446
|
+
- Updated dependencies [44d075b]
|
|
3447
|
+
- Updated dependencies [40c479a]
|
|
3448
|
+
- Updated dependencies [971d387]
|
|
3449
|
+
- Updated dependencies [ee851c3]
|
|
3450
|
+
- Updated dependencies [6414dfd]
|
|
3451
|
+
- Updated dependencies [a8d5c71]
|
|
3452
|
+
- Updated dependencies [905b21f]
|
|
3453
|
+
- Updated dependencies [88e9109]
|
|
3454
|
+
- Updated dependencies [2c45966]
|
|
3455
|
+
- Updated dependencies [db3a600]
|
|
3456
|
+
- Updated dependencies [3a3db76]
|
|
3457
|
+
- Updated dependencies [0d723a3]
|
|
3458
|
+
- Updated dependencies [0c95d3d]
|
|
3459
|
+
- Updated dependencies [3e4fa2c]
|
|
3460
|
+
- Updated dependencies [b5b928a]
|
|
3461
|
+
- Updated dependencies [6fd2cf7]
|
|
3462
|
+
- Updated dependencies [5fa06c4]
|
|
3463
|
+
- Updated dependencies [52a43de]
|
|
3464
|
+
- Updated dependencies [195052f]
|
|
3465
|
+
- Updated dependencies [e4559d1]
|
|
3466
|
+
- Updated dependencies [2c71482]
|
|
3467
|
+
- Updated dependencies [129bcc5]
|
|
3468
|
+
- Updated dependencies [a26b9e4]
|
|
3469
|
+
- Updated dependencies [5ef9c4f]
|
|
3470
|
+
- Updated dependencies [46f0bb4]
|
|
3471
|
+
- Updated dependencies [06b82b8]
|
|
3472
|
+
- Updated dependencies [8ec11e1]
|
|
3473
|
+
- Updated dependencies [6f81384]
|
|
3474
|
+
- Updated dependencies [22ba927]
|
|
3475
|
+
- Updated dependencies [8631c32]
|
|
3476
|
+
- Updated dependencies [f8c70f4]
|
|
3477
|
+
- Updated dependencies [5d3a2d1]
|
|
3478
|
+
- Updated dependencies [8f1d995]
|
|
3479
|
+
- Updated dependencies [b362c1b]
|
|
3480
|
+
- Updated dependencies [f9c34df]
|
|
3481
|
+
- Updated dependencies [dddb942]
|
|
3482
|
+
- Updated dependencies [00c665e]
|
|
3483
|
+
- Updated dependencies [29754cf]
|
|
3484
|
+
- Updated dependencies [d7de534]
|
|
3485
|
+
- Updated dependencies [3c2b6f7]
|
|
3486
|
+
- Updated dependencies [6e88630]
|
|
3487
|
+
- Updated dependencies [b84dc18]
|
|
3488
|
+
- Updated dependencies [ac8abb0]
|
|
3489
|
+
- Updated dependencies [9d86e1d]
|
|
3490
|
+
- Updated dependencies [3a5817f]
|
|
3491
|
+
- Updated dependencies [99a3c2d]
|
|
3492
|
+
- Updated dependencies [5961030]
|
|
3493
|
+
- Updated dependencies [f24de8b]
|
|
3494
|
+
- Updated dependencies [c8ea8af]
|
|
3495
|
+
- Updated dependencies [9602dc8]
|
|
3496
|
+
- Updated dependencies [3190414]
|
|
3497
|
+
- Updated dependencies [4e480f5]
|
|
3498
|
+
- Updated dependencies [38a123c]
|
|
3499
|
+
- Updated dependencies [299102e]
|
|
3500
|
+
- Updated dependencies [30c73cd]
|
|
3501
|
+
- Updated dependencies [830ed58]
|
|
3502
|
+
- Updated dependencies [d7acad6]
|
|
3503
|
+
- Updated dependencies [45a9aeb]
|
|
3504
|
+
- Updated dependencies [713db46]
|
|
3505
|
+
- Updated dependencies [c71e14d]
|
|
3506
|
+
- Updated dependencies [bf3a03c]
|
|
3507
|
+
- Updated dependencies [cb55718]
|
|
3508
|
+
- Updated dependencies [748494b]
|
|
3509
|
+
- Updated dependencies [5967be0]
|
|
3510
|
+
- Updated dependencies [831be72]
|
|
3511
|
+
- Updated dependencies [29cb85b]
|
|
3512
|
+
- Updated dependencies [3e028c8]
|
|
3513
|
+
- Updated dependencies [d0889e2]
|
|
3514
|
+
- Updated dependencies [ce503e5]
|
|
3515
|
+
- Updated dependencies [f20dcf0]
|
|
3516
|
+
- Updated dependencies [12402a9]
|
|
3517
|
+
- Updated dependencies [aff3d7a]
|
|
3518
|
+
- Updated dependencies [4ca30d0]
|
|
3519
|
+
- Updated dependencies [7a5da14]
|
|
3520
|
+
- Updated dependencies [fff9645]
|
|
3521
|
+
- Updated dependencies [9c3b7ce]
|
|
3522
|
+
- Updated dependencies [2c1c967]
|
|
3523
|
+
- Updated dependencies [9486ac6]
|
|
3524
|
+
- Updated dependencies [9486ac6]
|
|
3525
|
+
- Updated dependencies [4d5f9b4]
|
|
3526
|
+
- Updated dependencies [d6ceb8d]
|
|
3527
|
+
- Updated dependencies [dc4365c]
|
|
3528
|
+
- Updated dependencies [e321d52]
|
|
3529
|
+
- Updated dependencies [969ba84]
|
|
3530
|
+
- Updated dependencies [98188c2]
|
|
3531
|
+
- Updated dependencies [4c68077]
|
|
3532
|
+
- Updated dependencies [7977ff9]
|
|
3533
|
+
- Updated dependencies [3beef6d]
|
|
3534
|
+
- Updated dependencies [06b8c42]
|
|
3535
|
+
- Updated dependencies [46b9bc9]
|
|
3536
|
+
- Updated dependencies [f46bd39]
|
|
3537
|
+
- Updated dependencies [b98352a]
|
|
3538
|
+
- Updated dependencies [b76ca67]
|
|
3539
|
+
- Updated dependencies [45ac2cb]
|
|
3540
|
+
- Updated dependencies [b97790a]
|
|
3541
|
+
- Updated dependencies [dbd5194]
|
|
3542
|
+
- Updated dependencies [7c9b044]
|
|
3543
|
+
- Updated dependencies [e552c31]
|
|
3544
|
+
- Updated dependencies [d47de51]
|
|
3545
|
+
- Updated dependencies [3fe6463]
|
|
3546
|
+
- Updated dependencies [b392674]
|
|
3547
|
+
- Updated dependencies [4f3a1e2]
|
|
3548
|
+
- Updated dependencies [31ab372]
|
|
3549
|
+
- Updated dependencies [846889b]
|
|
3550
|
+
- Updated dependencies [2acd8e1]
|
|
3551
|
+
- Updated dependencies [7b90231]
|
|
3552
|
+
- Updated dependencies [26896c6]
|
|
3553
|
+
- Updated dependencies [67fc3b0]
|
|
3554
|
+
- Updated dependencies [8579e34]
|
|
3555
|
+
- Updated dependencies [d57db5d]
|
|
3556
|
+
- Updated dependencies [33a3b3c]
|
|
3557
|
+
- Updated dependencies [b87f15b]
|
|
3558
|
+
- Updated dependencies [045d20b]
|
|
3559
|
+
- Updated dependencies [a2d2515]
|
|
3560
|
+
- Updated dependencies [c18d099]
|
|
3561
|
+
- Updated dependencies [0caacca]
|
|
3562
|
+
- Updated dependencies [adb2a86]
|
|
3563
|
+
- Updated dependencies [03380aa]
|
|
3564
|
+
- Updated dependencies [4562ea5]
|
|
3565
|
+
- Updated dependencies [3619792]
|
|
3566
|
+
- Updated dependencies [3561bd2]
|
|
3567
|
+
- Updated dependencies [bf97b98]
|
|
3568
|
+
- Updated dependencies [320374d]
|
|
3569
|
+
- Updated dependencies [b0d308d]
|
|
3570
|
+
- Updated dependencies [b458300]
|
|
3571
|
+
- Updated dependencies [40f34b4]
|
|
3572
|
+
- Updated dependencies [bd0376d]
|
|
3573
|
+
- Updated dependencies [8063bcb]
|
|
3574
|
+
- Updated dependencies [b74a859]
|
|
3575
|
+
- Updated dependencies [d4493fd]
|
|
3576
|
+
- Updated dependencies [240b80f]
|
|
3577
|
+
- Updated dependencies [77cb489]
|
|
3578
|
+
- Updated dependencies [bfaa158]
|
|
3579
|
+
- Updated dependencies [777e5c6]
|
|
3580
|
+
- Updated dependencies [0c386dd]
|
|
3581
|
+
- Updated dependencies [39d69ad]
|
|
3582
|
+
- Updated dependencies [9e37d9b]
|
|
3583
|
+
- Updated dependencies [5ad86dd]
|
|
3584
|
+
- Updated dependencies [16a725f]
|
|
3585
|
+
- Updated dependencies [4dfdcc3]
|
|
3586
|
+
- Updated dependencies [6a449fc]
|
|
3587
|
+
- Updated dependencies [446d93d]
|
|
3588
|
+
- Updated dependencies [ecd9cb2]
|
|
3589
|
+
- Updated dependencies [f08bcd9]
|
|
3590
|
+
- Updated dependencies [98d4108]
|
|
3591
|
+
- Updated dependencies [0e3b3be]
|
|
3592
|
+
- Updated dependencies [a29ae2d]
|
|
3593
|
+
- Updated dependencies [220c18d]
|
|
3594
|
+
- Updated dependencies [00d3f09]
|
|
3595
|
+
- Updated dependencies [4388f71]
|
|
3596
|
+
- Updated dependencies [0b1ac58]
|
|
3597
|
+
- Updated dependencies [c93b4d5]
|
|
3598
|
+
- Updated dependencies [c1fe272]
|
|
3599
|
+
- Updated dependencies [3cab570]
|
|
3600
|
+
- Updated dependencies [8ad218d]
|
|
3601
|
+
- Updated dependencies [3e41187]
|
|
3602
|
+
- Updated dependencies [5f78953]
|
|
3603
|
+
- Updated dependencies [639114c]
|
|
3604
|
+
- Updated dependencies [639114c]
|
|
3605
|
+
- Updated dependencies [1490691]
|
|
3606
|
+
- Updated dependencies [e8e4c4d]
|
|
3607
|
+
- Updated dependencies [1f31d3a]
|
|
3608
|
+
- Updated dependencies [d1842ab]
|
|
3609
|
+
- Updated dependencies [78ca238]
|
|
3610
|
+
- Updated dependencies [d8ec8d6]
|
|
3611
|
+
- Updated dependencies [351eb31]
|
|
3612
|
+
- Updated dependencies [866cd1d]
|
|
3613
|
+
- Updated dependencies [20c04b2]
|
|
3614
|
+
- Updated dependencies [01c9023]
|
|
3615
|
+
- Updated dependencies [48c19bd]
|
|
3616
|
+
- Updated dependencies [a6d8b8d]
|
|
3617
|
+
- Updated dependencies [4b5bb95]
|
|
3618
|
+
- Updated dependencies [b652514]
|
|
3619
|
+
- Updated dependencies [adbda1b]
|
|
3620
|
+
- Updated dependencies [adbda1b]
|
|
3621
|
+
- Updated dependencies [8952395]
|
|
3622
|
+
- Updated dependencies [e2b3826]
|
|
3623
|
+
- Updated dependencies [0348bc9]
|
|
3624
|
+
- Updated dependencies [e8c553b]
|
|
3625
|
+
- Updated dependencies [2e32ed4]
|
|
3626
|
+
- Updated dependencies [554e647]
|
|
3627
|
+
- Updated dependencies [3ed3eec]
|
|
3628
|
+
- Updated dependencies [7c3df8f]
|
|
3629
|
+
- Updated dependencies [a4514e8]
|
|
3630
|
+
- Updated dependencies [db3896c]
|
|
3631
|
+
- Updated dependencies [b9f5ff1]
|
|
3632
|
+
- Updated dependencies [e75f4c9]
|
|
3633
|
+
- Updated dependencies [19f1639]
|
|
3634
|
+
- Updated dependencies [4704aa4]
|
|
3635
|
+
- Updated dependencies [47547d0]
|
|
3636
|
+
- Updated dependencies [1bee5d0]
|
|
3637
|
+
- Updated dependencies [858cd72]
|
|
3638
|
+
- Updated dependencies [cfc9b6d]
|
|
3639
|
+
- Updated dependencies [554f2b6]
|
|
3640
|
+
- Updated dependencies [72f55c9]
|
|
3641
|
+
- Updated dependencies [26e06d7]
|
|
3642
|
+
- Updated dependencies [669d71b]
|
|
3643
|
+
- Updated dependencies [ed27d7c]
|
|
3644
|
+
- Updated dependencies [52c8cf7]
|
|
3645
|
+
- Updated dependencies [2ceb43a]
|
|
3646
|
+
- Updated dependencies [7cdd2b9]
|
|
3647
|
+
- Updated dependencies [52c8cf7]
|
|
3648
|
+
- Updated dependencies [3399704]
|
|
3649
|
+
- Updated dependencies [71a4a53]
|
|
3650
|
+
- Updated dependencies [7bf244b]
|
|
3651
|
+
- Updated dependencies [f0bb9fa]
|
|
3652
|
+
- Updated dependencies [81a2eb1]
|
|
3653
|
+
- Updated dependencies [caa0cd3]
|
|
3654
|
+
- Updated dependencies [caa0cd3]
|
|
3655
|
+
- Updated dependencies [20cb8db]
|
|
3656
|
+
- Updated dependencies [25c7d58]
|
|
3657
|
+
- Updated dependencies [00d2fa6]
|
|
3658
|
+
- Updated dependencies [77b2a18]
|
|
3659
|
+
- Updated dependencies [c6198c2]
|
|
3660
|
+
- Updated dependencies [721d1e0]
|
|
3661
|
+
- Updated dependencies [c2f0f48]
|
|
3662
|
+
- Updated dependencies [1237ae4]
|
|
3663
|
+
- Updated dependencies [2f61238]
|
|
3664
|
+
- Updated dependencies [51eb515]
|
|
3665
|
+
- Updated dependencies [c354ce5]
|
|
3666
|
+
- Updated dependencies [8fe8e5c]
|
|
3667
|
+
- Updated dependencies [feac439]
|
|
3668
|
+
- Updated dependencies [9ae871d]
|
|
3669
|
+
- Updated dependencies [efbd566]
|
|
3670
|
+
- Updated dependencies [2a5bf45]
|
|
3671
|
+
- Updated dependencies [9587fc9]
|
|
3672
|
+
- Updated dependencies [e62c44e]
|
|
3673
|
+
- Updated dependencies [daf9d57]
|
|
3674
|
+
- Updated dependencies [23b9958]
|
|
3675
|
+
- Updated dependencies [c15d7ec]
|
|
3676
|
+
- Updated dependencies [5d0876c]
|
|
3677
|
+
- Updated dependencies [f7ace0a]
|
|
3678
|
+
- Updated dependencies [b041b9c]
|
|
3679
|
+
- Updated dependencies [ce2aaef]
|
|
3680
|
+
- Updated dependencies [544ecba]
|
|
3681
|
+
- Updated dependencies [2ce2612]
|
|
3682
|
+
- Updated dependencies [bc640ec]
|
|
3683
|
+
- Updated dependencies [1e215c4]
|
|
3684
|
+
- Updated dependencies [da6e191]
|
|
3685
|
+
- Updated dependencies [3e377c9]
|
|
3686
|
+
- Updated dependencies [a3eb5d0]
|
|
3687
|
+
- Updated dependencies [4ce14f1]
|
|
3688
|
+
- Updated dependencies [aef97e5]
|
|
3689
|
+
- Updated dependencies [2af1fa7]
|
|
3690
|
+
- Updated dependencies [c14d3a0]
|
|
3691
|
+
- Updated dependencies [a137d0c]
|
|
3692
|
+
- Updated dependencies [caf477f]
|
|
3693
|
+
- Updated dependencies [f6375da]
|
|
3694
|
+
- Updated dependencies [967e5d8]
|
|
3695
|
+
- Updated dependencies [c907a9c]
|
|
3696
|
+
- Updated dependencies [a4611b3]
|
|
3697
|
+
- Updated dependencies [20316ba]
|
|
3698
|
+
- Updated dependencies [d3499b3]
|
|
3699
|
+
- Updated dependencies [91f9276]
|
|
3700
|
+
- Updated dependencies [309c75e]
|
|
3701
|
+
- Updated dependencies [c9f9bae]
|
|
3702
|
+
- Updated dependencies [18897a4]
|
|
3703
|
+
- Updated dependencies [8b7ea39]
|
|
3704
|
+
- Updated dependencies [a915064]
|
|
3705
|
+
- Updated dependencies [dcbf0b2]
|
|
3706
|
+
- Updated dependencies [52cac38]
|
|
3707
|
+
- Updated dependencies [93fea2e]
|
|
3708
|
+
- Updated dependencies [1422a92]
|
|
3709
|
+
- Updated dependencies [d05fe17]
|
|
3710
|
+
- Updated dependencies [a480f79]
|
|
3711
|
+
- Updated dependencies [f08d1a8]
|
|
3712
|
+
- Updated dependencies [64a252d]
|
|
3713
|
+
- Updated dependencies [786bc91]
|
|
3714
|
+
- Updated dependencies [75fca96]
|
|
3715
|
+
- Updated dependencies [7ca6ddd]
|
|
3716
|
+
- Updated dependencies [f1cd290]
|
|
3717
|
+
- Updated dependencies [5a41ce7]
|
|
3718
|
+
- Updated dependencies [8d50bc2]
|
|
3719
|
+
- Updated dependencies [604476d]
|
|
3720
|
+
- Updated dependencies [d1bebb0]
|
|
3721
|
+
- Updated dependencies [95bf128]
|
|
3722
|
+
- Updated dependencies [335abea]
|
|
3723
|
+
- Updated dependencies [edea22a]
|
|
3724
|
+
- Updated dependencies [0f5cadf]
|
|
3725
|
+
- Updated dependencies [4f9f1ee]
|
|
3726
|
+
- Updated dependencies [66e8b2a]
|
|
3727
|
+
- Updated dependencies [aa083cd]
|
|
3728
|
+
- Updated dependencies [12b5992]
|
|
3729
|
+
- Updated dependencies [b93e245]
|
|
3730
|
+
- Updated dependencies [c842594]
|
|
3731
|
+
- Updated dependencies [290de37]
|
|
3732
|
+
- Updated dependencies [1cfdff8]
|
|
3733
|
+
- Updated dependencies [e1c27e4]
|
|
3734
|
+
- Updated dependencies [8c8da45]
|
|
3735
|
+
- Updated dependencies [8cd8eb5]
|
|
3736
|
+
- Updated dependencies [f52a9d7]
|
|
3737
|
+
- Updated dependencies [cf1d29e]
|
|
3738
|
+
- Updated dependencies [1bd79c8]
|
|
3739
|
+
- Updated dependencies [af9e957]
|
|
3740
|
+
- Updated dependencies [b1030c7]
|
|
3741
|
+
- Updated dependencies [c974edf]
|
|
3742
|
+
- Updated dependencies [ad852b6]
|
|
3743
|
+
- Updated dependencies [6778809]
|
|
3744
|
+
- Updated dependencies [3a3dd28]
|
|
3745
|
+
- Updated dependencies [7fb22a1]
|
|
3746
|
+
- Updated dependencies [ad66d79]
|
|
3747
|
+
- Updated dependencies [0758bd8]
|
|
3748
|
+
- Updated dependencies [ee4d19f]
|
|
3749
|
+
- Updated dependencies [496d31d]
|
|
3750
|
+
- Updated dependencies [7ed9808]
|
|
3751
|
+
- Updated dependencies [0ea7054]
|
|
3752
|
+
- Updated dependencies [ac0e39a]
|
|
3753
|
+
- Updated dependencies [9a853f2]
|
|
3754
|
+
- Updated dependencies [cb847fd]
|
|
3755
|
+
- Updated dependencies [ee70287]
|
|
3756
|
+
- Updated dependencies [3e98e13]
|
|
3757
|
+
- Updated dependencies [a695f50]
|
|
3758
|
+
- Updated dependencies [fc32921]
|
|
3759
|
+
- Updated dependencies [4eaa835]
|
|
3760
|
+
- Updated dependencies [8f9d87a]
|
|
3761
|
+
- Updated dependencies [b1777ae]
|
|
3762
|
+
- Updated dependencies [24845c4]
|
|
3763
|
+
- Updated dependencies [6f864cf]
|
|
3764
|
+
- Updated dependencies [24d1edd]
|
|
3765
|
+
- Updated dependencies [645087c]
|
|
3766
|
+
- Updated dependencies [33f4a19]
|
|
3767
|
+
- Updated dependencies [6e9a3d4]
|
|
3768
|
+
- Updated dependencies [4a292d2]
|
|
3769
|
+
- Updated dependencies [5323168]
|
|
3770
|
+
- Updated dependencies [841dd2b]
|
|
3771
|
+
- Updated dependencies [3014fc0]
|
|
3772
|
+
- Updated dependencies [dacb402]
|
|
3773
|
+
- Updated dependencies [846cec0]
|
|
3774
|
+
- Updated dependencies [91facae]
|
|
3775
|
+
- Updated dependencies [b38014e]
|
|
3776
|
+
- Updated dependencies [474797d]
|
|
3777
|
+
- Updated dependencies [704e695]
|
|
3778
|
+
- Updated dependencies [a407bd6]
|
|
3779
|
+
- Updated dependencies [317dbce]
|
|
3780
|
+
- Updated dependencies [309728c]
|
|
3781
|
+
- Updated dependencies [c03d03b]
|
|
3782
|
+
- Updated dependencies [aa08d7e]
|
|
3783
|
+
- Updated dependencies [3a43a15]
|
|
3784
|
+
- Updated dependencies [868e825]
|
|
3785
|
+
- Updated dependencies [f76f436]
|
|
3786
|
+
- Updated dependencies [ce45a03]
|
|
3787
|
+
- Updated dependencies [421544b]
|
|
3788
|
+
- Updated dependencies [fb01022]
|
|
3789
|
+
- Updated dependencies [e9d9212]
|
|
3790
|
+
- Updated dependencies [ecfb693]
|
|
3791
|
+
- Updated dependencies [639ca9d]
|
|
3792
|
+
- Updated dependencies [2152962]
|
|
3793
|
+
- Updated dependencies [abc1b18]
|
|
3794
|
+
- Updated dependencies [81a51db]
|
|
3795
|
+
- Updated dependencies [67749c7]
|
|
3796
|
+
- Updated dependencies [507b61b]
|
|
3797
|
+
- Updated dependencies [512c84b]
|
|
3798
|
+
- Updated dependencies [c300267]
|
|
3799
|
+
- Updated dependencies [fb3a101]
|
|
3800
|
+
- Updated dependencies [d4733f2]
|
|
3801
|
+
- Updated dependencies [7c9145f]
|
|
3802
|
+
- Updated dependencies [1570eac]
|
|
3803
|
+
- Updated dependencies [f391ede]
|
|
3804
|
+
- Updated dependencies [f5cfbbd]
|
|
3805
|
+
- Updated dependencies [f5cfbbd]
|
|
3806
|
+
- Updated dependencies [8b532cb]
|
|
3807
|
+
- Updated dependencies [64c3cdd]
|
|
3808
|
+
- Updated dependencies [4d65991]
|
|
3809
|
+
- Updated dependencies [c42554e]
|
|
3810
|
+
- Updated dependencies [3e71b26]
|
|
3811
|
+
- Updated dependencies [b89583b]
|
|
3812
|
+
- Updated dependencies [70c4523]
|
|
3813
|
+
- Updated dependencies [555b4ec]
|
|
3814
|
+
- Updated dependencies [64f1cf1]
|
|
3815
|
+
- Updated dependencies [da5e4f6]
|
|
3816
|
+
- Updated dependencies [1ccfc23]
|
|
3817
|
+
- Updated dependencies [542718f]
|
|
3818
|
+
- Updated dependencies [7f27bc5]
|
|
3819
|
+
- Updated dependencies [0a174f3]
|
|
3820
|
+
- Updated dependencies [676f677]
|
|
3821
|
+
- Updated dependencies [f95b140]
|
|
3822
|
+
- Updated dependencies [541ce4e]
|
|
3823
|
+
- Updated dependencies [6479086]
|
|
3824
|
+
- Updated dependencies [d79f525]
|
|
3825
|
+
- Updated dependencies [d1865d2]
|
|
3826
|
+
- Updated dependencies [88a629d]
|
|
3827
|
+
- Updated dependencies [cdd2542]
|
|
3828
|
+
- Updated dependencies [ab77513]
|
|
3829
|
+
- Updated dependencies [55ba3ff]
|
|
3830
|
+
- Updated dependencies [3ecc369]
|
|
3831
|
+
- Updated dependencies [f1190b0]
|
|
3832
|
+
- Updated dependencies [561abef]
|
|
3833
|
+
- Updated dependencies [ef52001]
|
|
3834
|
+
- Updated dependencies [6a4680b]
|
|
3835
|
+
- Updated dependencies [c3a4273]
|
|
3836
|
+
- Updated dependencies [abf710d]
|
|
3837
|
+
- Updated dependencies [093af32]
|
|
3838
|
+
- Updated dependencies [1bd1be7]
|
|
3839
|
+
- Updated dependencies [d234fa9]
|
|
3840
|
+
- Updated dependencies [adf5812]
|
|
3841
|
+
- Updated dependencies [0eaed83]
|
|
3842
|
+
- Updated dependencies [e36acd4]
|
|
3843
|
+
- Updated dependencies [5058336]
|
|
3844
|
+
- Updated dependencies [2f6b2bf]
|
|
3845
|
+
- Updated dependencies [2028b31]
|
|
3846
|
+
- Updated dependencies [63601ab]
|
|
3847
|
+
- Updated dependencies [c372b29]
|
|
3848
|
+
- Updated dependencies [152f0a7]
|
|
3849
|
+
- Updated dependencies [8693b85]
|
|
3850
|
+
- Updated dependencies [e82dad1]
|
|
3851
|
+
- Updated dependencies [58b7b3d]
|
|
3852
|
+
- Updated dependencies [84defab]
|
|
3853
|
+
- Updated dependencies [681d3f1]
|
|
3854
|
+
- Updated dependencies [969d4f2]
|
|
3855
|
+
- Updated dependencies [f3bc481]
|
|
3856
|
+
- Updated dependencies [b79aac2]
|
|
3857
|
+
- Updated dependencies [93fc0e7]
|
|
3858
|
+
- Updated dependencies [a4b723f]
|
|
3859
|
+
- Updated dependencies [2b10ca0]
|
|
3860
|
+
- Updated dependencies [7db4a81]
|
|
3861
|
+
- Updated dependencies [19a0b0e]
|
|
3862
|
+
- Updated dependencies [526fc11]
|
|
3863
|
+
- Updated dependencies [f8e3e9a]
|
|
3864
|
+
- Updated dependencies [b9d47ec]
|
|
3865
|
+
- Updated dependencies [3b6bc69]
|
|
3866
|
+
- Updated dependencies [6214db6]
|
|
3867
|
+
- Updated dependencies [6732df4]
|
|
3868
|
+
- Updated dependencies [fe9e0d0]
|
|
3869
|
+
- Updated dependencies [63fb72c]
|
|
3870
|
+
- Updated dependencies [804831c]
|
|
3871
|
+
- Updated dependencies [279e48e]
|
|
3872
|
+
- Updated dependencies [8700d6d]
|
|
3873
|
+
- Updated dependencies [8db2a0f]
|
|
3874
|
+
- Updated dependencies [689953a]
|
|
3875
|
+
- Updated dependencies [30443fb]
|
|
3876
|
+
- Updated dependencies [8d3dbb2]
|
|
3877
|
+
- Updated dependencies [efc1c9c]
|
|
3878
|
+
- Updated dependencies [da45e6b]
|
|
3879
|
+
- Updated dependencies [7533465]
|
|
3880
|
+
- Updated dependencies [835f0f3]
|
|
3881
|
+
- Updated dependencies [6d5db7b]
|
|
3882
|
+
- Updated dependencies [a9d97be]
|
|
3883
|
+
- Updated dependencies [ed35b44]
|
|
3884
|
+
- Updated dependencies [9ba7e9c]
|
|
3885
|
+
- Updated dependencies [729e851]
|
|
3886
|
+
- Updated dependencies [96919a4]
|
|
3887
|
+
- Updated dependencies [345e24a]
|
|
3888
|
+
- Updated dependencies [20b507a]
|
|
3889
|
+
- Updated dependencies [2e471dc]
|
|
3890
|
+
- Updated dependencies [6748587]
|
|
3891
|
+
- Updated dependencies [be50942]
|
|
3892
|
+
- Updated dependencies [775e079]
|
|
3893
|
+
- Updated dependencies [7e8b3c0]
|
|
3894
|
+
- Updated dependencies [53374dc]
|
|
3895
|
+
- Updated dependencies [f6fb83f]
|
|
3896
|
+
- Updated dependencies [2049b03]
|
|
3897
|
+
- Updated dependencies [2bf34f7]
|
|
3898
|
+
- Updated dependencies [15b33ae]
|
|
3899
|
+
- Updated dependencies [7cbc724]
|
|
3900
|
+
- Updated dependencies [4a94c38]
|
|
3901
|
+
- Updated dependencies [7098eed]
|
|
3902
|
+
- Updated dependencies [3df7c5c]
|
|
3903
|
+
- Updated dependencies [fb91ac9]
|
|
3904
|
+
- Updated dependencies [fb91ac9]
|
|
3905
|
+
- Updated dependencies [641fb55]
|
|
3906
|
+
- Updated dependencies [8524372]
|
|
3907
|
+
- Updated dependencies [7cbefa5]
|
|
3908
|
+
- Updated dependencies [72d6587]
|
|
3909
|
+
- Updated dependencies [a272a4f]
|
|
3910
|
+
- Updated dependencies [55f39ee]
|
|
3911
|
+
- Updated dependencies [0ce32d5]
|
|
3912
|
+
- Updated dependencies [0970a0e]
|
|
3913
|
+
- Updated dependencies [e427e9c]
|
|
3914
|
+
- Updated dependencies [bbc9dc3]
|
|
3915
|
+
- Updated dependencies [02f1813]
|
|
3916
|
+
- Updated dependencies [1ef89c0]
|
|
3917
|
+
- Updated dependencies [ba0b61a]
|
|
3918
|
+
- Updated dependencies [ba0b61a]
|
|
3919
|
+
- Updated dependencies [ac716ff]
|
|
3920
|
+
- Updated dependencies [e05553c]
|
|
3921
|
+
- Updated dependencies [f0f3cd5]
|
|
3922
|
+
- Updated dependencies [ab856ed]
|
|
3923
|
+
- Updated dependencies [f0f2046]
|
|
3924
|
+
- Updated dependencies [20f3e65]
|
|
3925
|
+
- Updated dependencies [bbba098]
|
|
3926
|
+
- Updated dependencies [87af769]
|
|
3927
|
+
- Updated dependencies [3be720e]
|
|
3928
|
+
- Updated dependencies [43c0d17]
|
|
3929
|
+
- Updated dependencies [c3df43a]
|
|
3930
|
+
- Updated dependencies [d16d0e9]
|
|
3931
|
+
- Updated dependencies [bbe57fd]
|
|
3932
|
+
- Updated dependencies [272a530]
|
|
3933
|
+
- Updated dependencies [1779e8d]
|
|
3934
|
+
- Updated dependencies [9aa2a57]
|
|
3935
|
+
- Updated dependencies [f7fcc2c]
|
|
3936
|
+
- Updated dependencies [f0f4d6c]
|
|
3937
|
+
- Updated dependencies [4128188]
|
|
3938
|
+
- Updated dependencies [b253c4e]
|
|
3939
|
+
- Updated dependencies [78a9c67]
|
|
3940
|
+
- Updated dependencies [2e97c8c]
|
|
3941
|
+
- Updated dependencies [4a7ef0d]
|
|
3942
|
+
- Updated dependencies [4598f6d]
|
|
3943
|
+
- Updated dependencies [dea17b4]
|
|
3944
|
+
- Updated dependencies [89bb77a]
|
|
3945
|
+
- Updated dependencies [06611e4]
|
|
3946
|
+
- Updated dependencies [44152c4]
|
|
3947
|
+
- Updated dependencies [3939545]
|
|
3948
|
+
- Updated dependencies [0ee6e31]
|
|
3949
|
+
- Updated dependencies [66abbde]
|
|
3950
|
+
- Updated dependencies [dc3893d]
|
|
3951
|
+
- Updated dependencies [1bbaa16]
|
|
3952
|
+
- Updated dependencies [6ee259a]
|
|
3953
|
+
- Updated dependencies [7649f43]
|
|
3954
|
+
- Updated dependencies [e708426]
|
|
3955
|
+
- Updated dependencies [3b6d53b]
|
|
3956
|
+
- Updated dependencies [e22fa12]
|
|
3957
|
+
- Updated dependencies [1560d46]
|
|
3958
|
+
- Updated dependencies [276d174]
|
|
3959
|
+
- Updated dependencies [2982ed9]
|
|
3960
|
+
- Updated dependencies [741864f]
|
|
3961
|
+
- Updated dependencies [a8198de]
|
|
3962
|
+
- Updated dependencies [0c789a4]
|
|
3963
|
+
- Updated dependencies [05a49f2]
|
|
3964
|
+
- Updated dependencies [b234a84]
|
|
3965
|
+
- Updated dependencies [a78cd37]
|
|
3966
|
+
- Updated dependencies [5ea623e]
|
|
3967
|
+
- Updated dependencies [4d7d322]
|
|
3968
|
+
- Updated dependencies [5eabe86]
|
|
3969
|
+
- Updated dependencies [ca5d671]
|
|
3970
|
+
- Updated dependencies [32bf2d6]
|
|
3971
|
+
- Updated dependencies [ff0c384]
|
|
3972
|
+
- Updated dependencies [af4fb29]
|
|
3973
|
+
- Updated dependencies [c698a81]
|
|
3974
|
+
- Updated dependencies [ff5ef1c]
|
|
3975
|
+
- Updated dependencies [befd40c]
|
|
3976
|
+
- Updated dependencies [9a97800]
|
|
3977
|
+
- Updated dependencies [6bca0e4]
|
|
3978
|
+
- Updated dependencies [81c0bc4]
|
|
3979
|
+
- Updated dependencies [3c76801]
|
|
3980
|
+
- Updated dependencies [60500cb]
|
|
3981
|
+
- Updated dependencies [2fcefb9]
|
|
3982
|
+
- Updated dependencies [77f846a]
|
|
3983
|
+
- Updated dependencies [bc5870c]
|
|
3984
|
+
- Updated dependencies [b55a346]
|
|
3985
|
+
- Updated dependencies [065bba7]
|
|
3986
|
+
- Updated dependencies [f760064]
|
|
3987
|
+
- Updated dependencies [dd19463]
|
|
3988
|
+
- Updated dependencies [6791717]
|
|
3989
|
+
- Updated dependencies [8ea3bee]
|
|
3990
|
+
- Updated dependencies [100547e]
|
|
3991
|
+
- Updated dependencies [3a58149]
|
|
3992
|
+
- Updated dependencies [6d1c155]
|
|
3993
|
+
- Updated dependencies [d7573b3]
|
|
3994
|
+
- Updated dependencies [bf3edfe]
|
|
3995
|
+
- Updated dependencies [2c8474c]
|
|
3996
|
+
- Updated dependencies [6ce89da]
|
|
3997
|
+
- Updated dependencies [0e05aac]
|
|
3998
|
+
- Updated dependencies [ae61ad4]
|
|
3999
|
+
- Updated dependencies [5aed9e4]
|
|
4000
|
+
- Updated dependencies [83c77dc]
|
|
4001
|
+
- Updated dependencies [3c9fca3]
|
|
4002
|
+
- Updated dependencies [18a8e7d]
|
|
4003
|
+
- Updated dependencies [e7957ab]
|
|
4004
|
+
- Updated dependencies [f7e34ca]
|
|
4005
|
+
- Updated dependencies [e719ebd]
|
|
4006
|
+
- Updated dependencies [516583b]
|
|
4007
|
+
- Updated dependencies [f9e4f91]
|
|
4008
|
+
- Updated dependencies [6ef48b1]
|
|
4009
|
+
- Updated dependencies [58be55e]
|
|
4010
|
+
- Updated dependencies [fa429cf]
|
|
4011
|
+
- Updated dependencies [ed8df3e]
|
|
4012
|
+
- Updated dependencies [fe76ece]
|
|
4013
|
+
- Updated dependencies [8b446f5]
|
|
4014
|
+
- Updated dependencies [8e74b27]
|
|
4015
|
+
- Updated dependencies [7102b20]
|
|
4016
|
+
- Updated dependencies [8ebd57f]
|
|
4017
|
+
- Updated dependencies [968dc1e]
|
|
4018
|
+
- Updated dependencies [9a1fb41]
|
|
4019
|
+
- Updated dependencies [617707a]
|
|
4020
|
+
- Updated dependencies [c40f3b8]
|
|
4021
|
+
- Updated dependencies [58770f3]
|
|
4022
|
+
- Updated dependencies [aefe428]
|
|
4023
|
+
- Updated dependencies [485f096]
|
|
4024
|
+
- Updated dependencies [7357447]
|
|
4025
|
+
- Updated dependencies [199d31b]
|
|
4026
|
+
- Updated dependencies [b655a9d]
|
|
4027
|
+
- Updated dependencies [a865c73]
|
|
4028
|
+
- Updated dependencies [3e01cb5]
|
|
4029
|
+
- Updated dependencies [7138bc1]
|
|
4030
|
+
- Updated dependencies [cef27e2]
|
|
4031
|
+
- Updated dependencies [4e8622b]
|
|
4032
|
+
- Updated dependencies [dffd752]
|
|
4033
|
+
- Updated dependencies [06973aa]
|
|
4034
|
+
- Updated dependencies [50798f3]
|
|
4035
|
+
- Updated dependencies [6a576c9]
|
|
4036
|
+
- Updated dependencies [105f3c5]
|
|
4037
|
+
- Updated dependencies [3ccd9e8]
|
|
4038
|
+
- Updated dependencies [689b979]
|
|
4039
|
+
- Updated dependencies [c70f865]
|
|
4040
|
+
- Updated dependencies [e546222]
|
|
4041
|
+
- Updated dependencies [fd13f52]
|
|
4042
|
+
- Updated dependencies [d7bd274]
|
|
4043
|
+
- Updated dependencies [98c3a74]
|
|
4044
|
+
- Updated dependencies [fffa30d]
|
|
4045
|
+
- Updated dependencies [e4e9557]
|
|
4046
|
+
- Updated dependencies [7a28e1e]
|
|
4047
|
+
- Updated dependencies [ebce5a3]
|
|
4048
|
+
- Updated dependencies [fb336df]
|
|
4049
|
+
- Updated dependencies [4dc80d0]
|
|
4050
|
+
- Updated dependencies [9d9040d]
|
|
4051
|
+
- Updated dependencies [20e317c]
|
|
4052
|
+
- Updated dependencies [0fce2ef]
|
|
4053
|
+
- Updated dependencies [42df928]
|
|
4054
|
+
- Updated dependencies [0e2ddd4]
|
|
4055
|
+
- Updated dependencies [b7479ab]
|
|
4056
|
+
- Updated dependencies [9850c6e]
|
|
4057
|
+
- Updated dependencies [de570cc]
|
|
4058
|
+
- Updated dependencies [b2ea297]
|
|
4059
|
+
- Updated dependencies [5b5a5c3]
|
|
4060
|
+
- Updated dependencies [14582b8]
|
|
4061
|
+
- Updated dependencies [51e144e]
|
|
4062
|
+
- Updated dependencies [19cbf10]
|
|
4063
|
+
- Updated dependencies [b6e83be]
|
|
4064
|
+
- Updated dependencies [ab92940]
|
|
4065
|
+
- Updated dependencies [a691c0b]
|
|
4066
|
+
- Updated dependencies [0b1326d]
|
|
4067
|
+
- Updated dependencies [1e66879]
|
|
4068
|
+
- Updated dependencies [c5200f0]
|
|
4069
|
+
- Updated dependencies [af3861f]
|
|
4070
|
+
- Updated dependencies [2609812]
|
|
4071
|
+
- Updated dependencies [515f171]
|
|
4072
|
+
- Updated dependencies [1f4e029]
|
|
4073
|
+
- Updated dependencies [4f14ad7]
|
|
4074
|
+
- Updated dependencies [258d264]
|
|
4075
|
+
- Updated dependencies [cac64b3]
|
|
4076
|
+
- Updated dependencies [4bb940b]
|
|
4077
|
+
- Updated dependencies [8033ad1]
|
|
4078
|
+
- Updated dependencies [fa140b8]
|
|
4079
|
+
- Updated dependencies [71cba28]
|
|
4080
|
+
- Updated dependencies [190fbd0]
|
|
4081
|
+
- Updated dependencies [c00bf28]
|
|
4082
|
+
- Updated dependencies [93127bd]
|
|
4083
|
+
- Updated dependencies [f2158ec]
|
|
4084
|
+
- Updated dependencies [759606e]
|
|
4085
|
+
- Updated dependencies [fd8dace]
|
|
4086
|
+
- Updated dependencies [72ffc34]
|
|
4087
|
+
- Updated dependencies [a51fa0c]
|
|
4088
|
+
- Updated dependencies [51f3d8d]
|
|
4089
|
+
- Updated dependencies [bf28341]
|
|
4090
|
+
- Updated dependencies [78cbdb5]
|
|
4091
|
+
- Updated dependencies [b7543a9]
|
|
4092
|
+
- Updated dependencies [6c6cee7]
|
|
4093
|
+
- Updated dependencies [42887e0]
|
|
4094
|
+
- Updated dependencies [f1690d4]
|
|
4095
|
+
- Updated dependencies [83fe6e7]
|
|
4096
|
+
- Updated dependencies [d1ab06f]
|
|
4097
|
+
- Updated dependencies [38a9568]
|
|
4098
|
+
- Updated dependencies [f90b8fb]
|
|
4099
|
+
- Updated dependencies [91783c4]
|
|
4100
|
+
- Updated dependencies [982885d]
|
|
4101
|
+
- Updated dependencies [dba7d84]
|
|
4102
|
+
- Updated dependencies [ca39427]
|
|
4103
|
+
- Updated dependencies [bd09957]
|
|
4104
|
+
- Updated dependencies [5a07e67]
|
|
4105
|
+
- Updated dependencies [2d36552]
|
|
4106
|
+
- Updated dependencies [45d8288]
|
|
4107
|
+
- Updated dependencies [b2437a7]
|
|
4108
|
+
- Updated dependencies [f157423]
|
|
4109
|
+
- Updated dependencies [7a90afd]
|
|
4110
|
+
- Updated dependencies [eddc1dd]
|
|
4111
|
+
- Updated dependencies [490f482]
|
|
4112
|
+
- Updated dependencies [27308c5]
|
|
4113
|
+
- Updated dependencies [8689166]
|
|
4114
|
+
- Updated dependencies [c9327c9]
|
|
4115
|
+
- Updated dependencies [920165d]
|
|
4116
|
+
- Updated dependencies [9101be5]
|
|
4117
|
+
- Updated dependencies [f53a8d0]
|
|
4118
|
+
- Updated dependencies [30266cf]
|
|
4119
|
+
- Updated dependencies [968dc1e]
|
|
4120
|
+
- Updated dependencies [57f9b07]
|
|
4121
|
+
- Updated dependencies [3c73d99]
|
|
4122
|
+
- Updated dependencies [d91aed9]
|
|
4123
|
+
- Updated dependencies [ed71d9e]
|
|
4124
|
+
- Updated dependencies [7776fc2]
|
|
4125
|
+
- Updated dependencies [e76634c]
|
|
4126
|
+
- Updated dependencies [c86185e]
|
|
4127
|
+
- Updated dependencies [fb96ecb]
|
|
4128
|
+
- Updated dependencies [1170ed1]
|
|
4129
|
+
- Updated dependencies [92814db]
|
|
4130
|
+
- Updated dependencies [4d73b07]
|
|
4131
|
+
- @object-ui/react@17.7.0
|
|
4132
|
+
- @object-ui/core@17.7.0
|
|
4133
|
+
- @object-ui/types@17.7.0
|
|
4134
|
+
- @object-ui/i18n@17.7.0
|
|
4135
|
+
- @object-ui/components@17.7.0
|
|
4136
|
+
- @object-ui/fields@17.7.0
|
|
4137
|
+
- @object-ui/mobile@17.7.0
|
|
4138
|
+
- @object-ui/permissions@17.7.0
|
|
4139
|
+
|
|
4140
|
+
## 17.6.0
|
|
4141
|
+
|
|
4142
|
+
### Minor Changes
|
|
4143
|
+
|
|
4144
|
+
- 0a73b51: `ObjectView` and `ListView` now flatten a view's `map` block through a
|
|
4145
|
+
whitelist instead of spreading the whole (untyped) block to the top level.
|
|
4146
|
+
|
|
4147
|
+
Both `case 'map'` flatteners used to build the `object-map` schema with
|
|
4148
|
+
`...(options.map || {})` — a raw spread of an untyped bag
|
|
4149
|
+
(`NamedListView.options?: Record<string, any>`), so any key an author wrote in
|
|
4150
|
+
the `map` block reached the top level unfiltered. `ObjectMap`'s own
|
|
4151
|
+
`FlatMapConfigKeys = Omit<ObjectMapConfig, 'style'>` declares `style` OUT of
|
|
4152
|
+
this flat form (`style` is also `BaseSchema.style`, inline CSS legal on every
|
|
4153
|
+
node), so the two disagreed about the same shape. `style` was the live
|
|
4154
|
+
specimen: `map: { style: '<url>' }` reached the top level as a CSS-shaped
|
|
4155
|
+
`style` key it was never supposed to carry.
|
|
4156
|
+
|
|
4157
|
+
Behavior narrowing, stated because it changes what reaches the flattened
|
|
4158
|
+
schema: a `map` block key that is not one of `ObjectMapConfig`'s declared
|
|
4159
|
+
flat keys (`latitudeField` / `longitudeField` / `locationField` / `titleField`
|
|
4160
|
+
/ `descriptionField` / `zoom` / `center`) — including `style` — no longer
|
|
4161
|
+
reaches the top level of the flattened `object-map` schema. This closes a gap
|
|
4162
|
+
rather than removing working behavior: the pinned strict spec view schemas
|
|
4163
|
+
accept no `map` block at all today, so no author-facing surface could reach
|
|
4164
|
+
this path, and `ObjectMap` already stopped reading a top-level `style` as a
|
|
4165
|
+
map style (a dev warning names the correct spelling instead).
|
|
4166
|
+
|
|
4167
|
+
The whitelist is DERIVED from `ObjectMapConfigSchema` (`@object-ui/types/zod`)
|
|
4168
|
+
rather than hand-listed, so the flatteners and the declaration cannot drift
|
|
4169
|
+
apart again — a key added to (or removed from) the schema reaches both
|
|
4170
|
+
flatteners without a second edit. `ObjectMap`'s own `FLAT_MAP_CONFIG_KEYS` is
|
|
4171
|
+
derived from the same schema for the same reason.
|
|
4172
|
+
- f1d4748: Remove the retired `striped` / `bordered` / `virtualScroll` list-view surface
|
|
4173
|
+
|
|
4174
|
+
objectstack#7176 retired `list.striped`, `list.bordered` and `list.virtualScroll`
|
|
4175
|
+
from the spec after measuring every objectui reader as pass-through: each one
|
|
4176
|
+
copied the key onward and no renderer ever applied it. objectui stops declaring,
|
|
4177
|
+
typing and forwarding them.
|
|
4178
|
+
|
|
4179
|
+
Off the chain: the `@objectstack/spec` list-view bridge in `@object-ui/react`,
|
|
4180
|
+
`ListView`'s child-view props in `@object-ui/plugin-list`, both `ObjectView`
|
|
4181
|
+
relays (`@object-ui/plugin-view` and `@object-ui/app-shell`), the `ObjectGridSchema`
|
|
4182
|
+
and `NamedListView` declarations in `@object-ui/types` (interface and zod),
|
|
4183
|
+
`ObjectGrid.component.yml` in `@object-ui/components`, and the page-block
|
|
4184
|
+
inspector's `striped` / `bordered` toggles in the metadata-admin designer.
|
|
4185
|
+
|
|
4186
|
+
Behaviour is unchanged: nothing read these keys, so nothing rendered differently
|
|
4187
|
+
for them. Stored view metadata that still carries one keeps validating — the keys
|
|
4188
|
+
are simply no longer relayed. `ListViewSchema` continues to take the spec's
|
|
4189
|
+
list-view fields by reference, so the protocol's own retirement tombstones
|
|
4190
|
+
arrive with the next `@objectstack/spec` bump and reject the keys at the
|
|
4191
|
+
authoring boundary. Restoring any of the three as live surface requires an
|
|
4192
|
+
implementation card filed first, per the ruling.
|
|
4193
|
+
- b1119ec: Project every declared `recordIdField`, and refuse an action that names no record
|
|
4194
|
+
|
|
4195
|
+
objectstack#8018. An `api` action declaring `recordIdParam` identifies the record
|
|
4196
|
+
it acts on by a row field — `recordIdField`, default `id`. The grid built
|
|
4197
|
+
`$select` from the listView columns, `id` and the predicate refs only, so an
|
|
4198
|
+
action keyed on any other field asked the server for everything except the key
|
|
4199
|
+
naming its own record. The row arrived without it, and the injection was skipped
|
|
4200
|
+
silently: the request went out anyway, minus the parameter. A backend reading a
|
|
4201
|
+
missing selector as "match nothing" then answers success for having changed
|
|
4202
|
+
nothing, so a record-scoped mutation reports success and does nothing.
|
|
4203
|
+
|
|
4204
|
+
Two independent repairs, both in this change:
|
|
4205
|
+
|
|
4206
|
+
- **Projection.** `listViewPredicates` (`@object-ui/core`) now also harvests
|
|
4207
|
+
`recordIdField` from `rowActionDefs`, `bulkActionDefs` and the object's
|
|
4208
|
+
`actions`, spelled as a synthetic `record.<name>` so the one existing harvester
|
|
4209
|
+
handles it. Both projection builders — `ObjectGrid` and `ListView` — read that
|
|
4210
|
+
function, so both gain the key with no call-site change. The existing guards
|
|
4211
|
+
still apply: a name the object does not declare, or one that is not a bare
|
|
4212
|
+
identifier, is dropped rather than put in `$select`, because an unknown key
|
|
4213
|
+
there is not ignored by every backend.
|
|
4214
|
+
- **Loud failure.** New `resolveRecordIdParamSeed` (`@object-ui/core`) is the one
|
|
4215
|
+
definition of "can this row identify the record?". `useConsoleActionRuntime`'s
|
|
4216
|
+
api handler now refuses the dispatch — `{ success: false, error }`, before the
|
|
4217
|
+
request — when the row lacks the key, or holds `null` for it. The two refusals
|
|
4218
|
+
are worded differently because they point at different repairs: an absent key
|
|
4219
|
+
is a projection or read-visibility problem, a null value is a data one. Falsy
|
|
4220
|
+
real values (`0`, `''`, `false`) are values and still dispatch.
|
|
4221
|
+
|
|
4222
|
+
The second half is what closes the class rather than the common case: a row can
|
|
4223
|
+
lack the key for reasons projection cannot fix — a server-side read mask that
|
|
4224
|
+
strips the field regardless of `$select`, a partial payload, a field the
|
|
4225
|
+
principal cannot read.
|
|
4226
|
+
|
|
4227
|
+
Behaviour change worth noting: an action that previously dispatched an
|
|
4228
|
+
under-specified request now fails visibly instead. That is the point — the old
|
|
4229
|
+
path could not report the failure it was causing.
|
|
4230
|
+
|
|
4231
|
+
### Patch Changes
|
|
4232
|
+
|
|
4233
|
+
- 516663d: RecordAttachmentsPanel no longer offers a Retry for an api-disabled `sys_attachment` read.
|
|
4234
|
+
|
|
4235
|
+
`OBJECT_API_DISABLED` (404, `enable.apiEnabled: false`) and its sibling
|
|
4236
|
+
`OBJECT_API_METHOD_NOT_ALLOWED` (405, the operation is absent from
|
|
4237
|
+
`enable.apiMethods`) are pure functions of the object's metadata — no user, no
|
|
4238
|
+
session, no request body — so every retry of every persona re-fetches the
|
|
4239
|
+
identical refusal. Before this change both landed in `RecordAttachmentsPanel`'s
|
|
4240
|
+
`unavailable` state and offered a Retry that was guaranteed to change nothing,
|
|
4241
|
+
the same wrong advice `ListView`'s error panel already stops giving for list
|
|
4242
|
+
reads.
|
|
4243
|
+
|
|
4244
|
+
The panel gains a fifth status, `api-unavailable`: no Retry button, and honest
|
|
4245
|
+
copy ("The attachments list is not available on this object.", new
|
|
4246
|
+
`detail.attachmentsApiUnavailable` key in all ten locale packs) instead of
|
|
4247
|
+
"We couldn't load the attachments for this record." The pre-existing `denied`
|
|
4248
|
+
(authorization) and `unavailable` (network/5xx/expired-session) states and
|
|
4249
|
+
their affordances are unchanged.
|
|
4250
|
+
|
|
4251
|
+
`ListView.classifyLoadError` — the classifier that already separated this case
|
|
4252
|
+
into its own `api-disabled` kind for list views — is lifted out of
|
|
4253
|
+
`packages/plugin-list/src/ListView.tsx`'s module scope into
|
|
4254
|
+
`@object-ui/react` (`classifyLoadError`, `LoadErrorKind`), so both surfaces
|
|
4255
|
+
consume one classification instead of `RecordAttachmentsPanel` re-deriving it.
|
|
4256
|
+
`ListView`'s own behavior is unchanged — it now imports the function it
|
|
4257
|
+
previously defined locally. The classifier delegates its api-disabled check to
|
|
4258
|
+
`isApiAccessDeniedError` (`@object-ui/data-objectstack`), removing a second,
|
|
4259
|
+
independently-maintained copy of the same code list.
|
|
4260
|
+
- a1609a6: Console list filters: a `between` range is submitted only when both bounds are filled, and six operator labels stop rendering as raw i18n keys.
|
|
4261
|
+
|
|
4262
|
+
Two defects in the list-view filter panel (objectstack#8815), both in the Console
|
|
4263
|
+
render layer, with no workaround available downstream.
|
|
4264
|
+
|
|
4265
|
+
**A half-filled range no longer refuses the whole view.** Picking a date column
|
|
4266
|
+
and 「介于」 draws two inputs — that part landed in objectui#3958 — but typing
|
|
4267
|
+
only one bound produced `["2024-01-01", ""]`, and both write paths read "is this
|
|
4268
|
+
row filled in?" with one shape-blind predicate (`null` / `''` / empty array).
|
|
4269
|
+
An array of length 2 passed it, so the empty bound went to the server, which
|
|
4270
|
+
refuses the query outright (`400 INVALID_FILTER`): the list showed
|
|
4271
|
+
「该视图的查询被拒绝」 and the filters the user had already applied stopped
|
|
4272
|
+
applying too. The saved-view fold persisted the same half-range, so the refusal
|
|
4273
|
+
came back on every later read of that view, for every user of it.
|
|
4274
|
+
|
|
4275
|
+
The spec cannot intercept this — `ViewFilterRuleSchema` accepts
|
|
4276
|
+
`["2024-01-01", ""]` because it counts the two slots rather than what is in
|
|
4277
|
+
them, while refusing a scalar or a one-element array. Authoring validation is
|
|
4278
|
+
therefore green on exactly the shape that fails at query time, which makes not
|
|
4279
|
+
emitting it the producer's job. `@object-ui/components` now exports
|
|
4280
|
+
`isFilterValueComplete(operator, value)` — arity-aware, so a `pair` row needs
|
|
4281
|
+
both bounds — and the two consumers that had each kept a copy of the old
|
|
4282
|
+
predicate (`plugin-list`'s `convertFilterGroupToAST`, `app-shell`'s
|
|
4283
|
+
`foldFilterGroupToSpecRules`) read it instead. A half-filled range is now
|
|
4284
|
+
dropped exactly as a half-typed `equals` row already was: no filter, rather than
|
|
4285
|
+
a filter the server will reject. Bounds of `0` and `false` stay real bounds.
|
|
4286
|
+
|
|
4287
|
+
**Six operator labels are translated in all ten locale packs.**
|
|
4288
|
+
`startsWith`, `endsWith`, `isNull`, `isNotNull`, `exists` and `notExists` were
|
|
4289
|
+
missing from every pack, so i18next resolved them to the raw key and the dropdown
|
|
4290
|
+
showed `filterBuilder.operators.isNull` beside translated entries. The
|
|
4291
|
+
component's own defaults table could not cover it: that table serves only the
|
|
4292
|
+
no-provider path, and the Console mounts a provider. The report named four —
|
|
4293
|
+
a `date` column's bucket offers the four nullness operators; a `text` column
|
|
4294
|
+
showed all six.
|
|
4295
|
+
|
|
4296
|
+
Because the label key is built dynamically (`t(\`filterBuilder.operators.${op}\`)`),
|
|
4297
|
+
no existing gate could see the gap: the call-site checker classifies a template
|
|
4298
|
+
key as `missing-prefix` and only asks whether the prefix resolves, and
|
|
4299
|
+
cross-pack parity is satisfied when all ten packs are missing a key together.
|
|
4300
|
+
A new parity test pins the packs against `FILTER_BUILDER_OPERATORS` in both
|
|
4301
|
+
directions, so an operator added to the dropdown now fails loudly until every
|
|
4302
|
+
pack labels it.
|
|
4303
|
+
- c1ef923: Grid and related-list column headers no longer offer a sort on a `formula` column.
|
|
4304
|
+
|
|
4305
|
+
A `formula` value is computed on read: no driver materialises a column for it, so
|
|
4306
|
+
a server `$orderby` naming one has nothing to order by. That sort never worked.
|
|
4307
|
+
Until objectstack#6994 the platform did not say so — the response carried the very
|
|
4308
|
+
values it had been asked to order by, out of order, under a `200`, with ascending
|
|
4309
|
+
and descending byte-identical on a real SQL driver — and it now answers
|
|
4310
|
+
`400 INVALID_SORT`. So the header was wrong before the platform's refusal and is
|
|
4311
|
+
wrong after it, for the same reason: it offers a sort that cannot be performed.
|
|
4312
|
+
|
|
4313
|
+
`ObjectGrid` withheld the affordance only from reference-bearing columns
|
|
4314
|
+
(objectui#3096). Unmaterialized types are a SECOND reason a server sort is
|
|
4315
|
+
impossible, not a different mechanism, so it now reads both — and so do the two
|
|
4316
|
+
sort entry points of a related list (the embedded table's headers and the
|
|
4317
|
+
sort-button row a `data-list` card keeps), which each derived that rule
|
|
4318
|
+
separately.
|
|
4319
|
+
|
|
4320
|
+
Client-side sorting is deliberately unchanged. There the rows are all in the
|
|
4321
|
+
browser and the formula value is the one the server hydrated on read, so ordering
|
|
4322
|
+
by what the cell shows is honest — the same split the relational carve-out makes.
|
|
4323
|
+
A sort DECLARED in view metadata is also unchanged: it still goes out and is still
|
|
4324
|
+
refused by name, because silently dropping an author's declaration would hide the
|
|
4325
|
+
authoring error instead of surfacing it (the toolbar's sort picker keeps such a
|
|
4326
|
+
field listed for exactly that reason — it is the only way to remove it).
|
|
4327
|
+
|
|
4328
|
+
The membership — `formula` alone — moved out of a private set in `ListView` into
|
|
4329
|
+
`@object-ui/core` (`UNMATERIALIZED_FIELD_TYPES` / `isUnmaterializedFieldType`),
|
|
4330
|
+
bound to `@objectstack/spec`'s own storage predicate so the renderer cannot drift
|
|
4331
|
+
from what the drivers actually store. It is deliberately narrower than the spec's
|
|
4332
|
+
write contract `COMPUTED_VALUE_TYPES`: a `summary` and an `autonumber` each get a
|
|
4333
|
+
real maintained column and sort correctly, and withholding their headers would
|
|
4334
|
+
have broken two affordances that work.
|
|
4335
|
+
- 616a2a5: The list filter builder no longer offers `Is set` / `Is not set`, which its query dialects cannot express.
|
|
4336
|
+
|
|
4337
|
+
**User-visible before/after.** The operator dropdown in the list toolbar's
|
|
4338
|
+
filter popover — and in the Studio view/tab/page filter inspectors, the dataset
|
|
4339
|
+
inspector and the generic `filter` config field — loses two rows: **"Is set"**
|
|
4340
|
+
and **"Is not set"**. The sharing-rule criteria builder (`FilterConditionField`)
|
|
4341
|
+
keeps both, unchanged. `Is null` / `Is not null` and `Is empty` / `Is not empty`
|
|
4342
|
+
are untouched everywhere and remain the way to filter on a missing value from
|
|
4343
|
+
the list.
|
|
4344
|
+
|
|
4345
|
+
Nothing that worked stops working. Every save path behind those two rows was
|
|
4346
|
+
already broken, in three different ways depending on the surface:
|
|
4347
|
+
|
|
4348
|
+
- **Live grid** — `ListView.mapOperator` had no row for either id, so its
|
|
4349
|
+
`default:` arm returned the id verbatim and the query went out as
|
|
4350
|
+
`['name', 'exists', 'x']`. `exists` is not a member of the spec's
|
|
4351
|
+
`VALID_AST_OPERATORS`, so `isFilterAST()` rejects the shape: an unfiltered
|
|
4352
|
+
read or a 400, never the filter the user asked for.
|
|
4353
|
+
- **Save as view** — `foldFilterGroupToSpecRules` normalizes through the spec's
|
|
4354
|
+
`normalizeFilterOperator`, which does not know the pair, and
|
|
4355
|
+
`ViewFilterRuleSchema`'s enum then refuses the rule.
|
|
4356
|
+
- **Dataset inspector** — `groupToCondition` has no row either and drops the
|
|
4357
|
+
condition silently, so the filter simply never applied.
|
|
4358
|
+
|
|
4359
|
+
**Why withheld rather than mapped.** Measured on `@objectstack/spec`
|
|
4360
|
+
17.0.0-rc.6: neither `VIEW_FILTER_OPERATORS` nor `VALID_AST_OPERATORS` contains
|
|
4361
|
+
an existence operator, under any spelling — both sets have zero members matching
|
|
4362
|
+
`/exist/`. Only the MongoDB-style `FieldOperatorsSchema` criteria carries
|
|
4363
|
+
`$exists`, and that is precisely the dialect `FilterConditionField` writes, so
|
|
4364
|
+
the pair moves behind the existing `OPT_IN_OPERATORS` gate and that widget opts
|
|
4365
|
+
in. Collapsing them onto `isNotNull` / `isNull` was rejected: the builder
|
|
4366
|
+
already draws those as their own rows, the round trip is lossy (a saved
|
|
4367
|
+
`exists` reads back as `isNotNull`), and the spec's own note records `$exists` =
|
|
4368
|
+
has-value as still unsettled across drivers — `driver-memory`'s live mingo path
|
|
4369
|
+
and `driver-mongodb` read key-presence.
|
|
4370
|
+
|
|
4371
|
+
**The class is now closed by an assertion, not by discipline.** objectui's three
|
|
4372
|
+
existing operator-parity guards all sweep spec vocabulary → objectui; none asked
|
|
4373
|
+
whether an id the dropdown draws is an id the consumer can persist, which is the
|
|
4374
|
+
direction that broke. `plugin-list`'s new
|
|
4375
|
+
`list-offered-operator-expressible-parity.test.ts` forces the set the list
|
|
4376
|
+
toolbar offers to **equal** the set its two dialects can express, in both
|
|
4377
|
+
directions — so an unexpressible operator cannot be offered, and an operator
|
|
4378
|
+
that becomes expressible upstream cannot stay needlessly withheld.
|
|
4379
|
+
- dfc6975: Related-list "+ New" now honours `userActions.create` predicates, and the grid
|
|
4380
|
+
toolbar's inline-edit affordance is gated on `update` permission (objectui#4646,
|
|
4381
|
+
objectui#4647).
|
|
4382
|
+
|
|
4383
|
+
Two declared-but-unenforced gaps on the same toolbar surface.
|
|
4384
|
+
|
|
4385
|
+
**#4646 — `createPredicates` had a producer and no consumer.**
|
|
4386
|
+
`@objectstack/spec@17.0.0` widened `userActions.create` to
|
|
4387
|
+
`z.union([z.boolean(), RowCrudActionOverrideSchema])`, so `resolveCrudAffordances`
|
|
4388
|
+
emits `createPredicates` — and nothing in objectui read them, against roughly
|
|
4389
|
+
fifteen consumption sites apiece for `editPredicates` / `deletePredicates`. The
|
|
4390
|
+
symptom: a parent record entering a frozen state correctly greyed its children's
|
|
4391
|
+
row Edit/Delete while the related list's "+ New" stayed fully live, so the user
|
|
4392
|
+
filled in the whole child form to earn a server 409. The related-list toolbar now
|
|
4393
|
+
evaluates `visibleWhen` / `disabledWhen` **once against the host parent record**,
|
|
4394
|
+
per the spec docblock's binding for this key, on top of the existing
|
|
4395
|
+
`o.create ∧ can(child, 'create')` check. `visibleWhen` hides "+ New" and fails
|
|
4396
|
+
CLOSED; `disabledWhen` greys it and fails SOFT — the same evaluator, fail
|
|
4397
|
+
directions and hidden-vs-disabled split the record header already uses for
|
|
4398
|
+
edit/delete (objectui#4419 / PR #4515). A bare-boolean `userActions.create` is
|
|
4399
|
+
untouched: with no predicates there is nothing to evaluate.
|
|
4400
|
+
|
|
4401
|
+
**#4647 — the inline-edit toggle was the one ungated affordance on its toolbar.**
|
|
4402
|
+
It rendered on "grid view ∧ the host wired `onInlineEditChange` ∧ not the compact
|
|
4403
|
+
toolbar", and every host wires that callback unconditionally. New and Import are
|
|
4404
|
+
hidden for an account without the grant and the bulk-delete entry on the same
|
|
4405
|
+
toolbar ANDs `can(obj, 'delete')`, but a read-only principal could flip inline
|
|
4406
|
+
edit, modify cells and press "Save all" to earn a server 403. It is now gated on
|
|
4407
|
+
the object's resolved edit affordance ∧ `can(object, 'update')`, mirroring that
|
|
4408
|
+
bulk-delete gate. The gate is applied at all three sites that carry this
|
|
4409
|
+
affordance — the wide toolbar's toggle, the compact toolbar's settings-popover
|
|
4410
|
+
entry (which previously had no gate at all, not even the callback), and the
|
|
4411
|
+
`editable` mode handed to the grid, so a stored view carrying `inlineEdit: true`
|
|
4412
|
+
can no longer drop a read-only principal into editable cells with no toggle to
|
|
4413
|
+
press.
|
|
4414
|
+
|
|
4415
|
+
`ListViewSchema.userActions.editInline` is also consumed now: an explicit `false`
|
|
4416
|
+
withholds the affordance wholesale, which authors previously could not do.
|
|
4417
|
+
|
|
4418
|
+
**Behaviour change for read-only users, stated plainly.** Where the UI used to
|
|
4419
|
+
offer inline editing and let the server refuse it, it now declines to offer the
|
|
4420
|
+
entry point at all. No data access changes — the server gate was and remains the
|
|
4421
|
+
enforcement boundary; this only stops the UI walking users into round-trips
|
|
4422
|
+
guaranteed to fail. Accounts *with* the grant see no change, and hosts with no
|
|
4423
|
+
`PermissionProvider` mounted (standalone embeds, the Studio designer) keep
|
|
4424
|
+
today's behaviour, since `can()` answers `true` there by design.
|
|
4425
|
+
|
|
4426
|
+
One deliberate non-change: the absent case of `userActions.editInline` defers to
|
|
4427
|
+
the host's existing `inlineEdit` channel rather than enforcing the spec's
|
|
4428
|
+
`.default(false)`. Enforcing that default would remove the toggle from every
|
|
4429
|
+
stored console list view in one release, since nothing folds a legacy key into
|
|
4430
|
+
`editInline` and no existing view declares it. This follows the rule the
|
|
4431
|
+
surrounding toolbar-flag block already states for itself — defaults chosen to
|
|
4432
|
+
match what the flags have always done. `InterfaceListPage`, the key's other
|
|
4433
|
+
consumer, reads the absent case as OFF, because the ADR-0047 interface page has
|
|
4434
|
+
no such host channel to defer to.
|
|
4435
|
+
- 138ab04: Fix a list filter that silently applied nothing when the first thing you picked was **Is null** or **Is not null**.
|
|
4436
|
+
|
|
4437
|
+
Adding a filter seeds the row with no value, and changing the operator keeps it that way — which is correct for an operator that takes no value, since the panel draws no input for one. The live grid read that row as unfinished and dropped it: the filter appeared in the panel, the query went out without it, and every record came back with no error to explain it. Only `Is empty` / `Is not empty` were exempt; the builder renders six operators value-less.
|
|
4438
|
+
|
|
4439
|
+
Which operators are complete without a value now has a single owner — `VALUELESS_FILTER_BUILDER_OPERATORS`, exported from `@object-ui/components` beside the code that decides it. The live grid and the saved-view fold both read it instead of keeping their own lists, so the two halves of one interaction can no longer disagree about whether a row is finished.
|
|
4440
|
+
- Updated dependencies [88085e3]
|
|
4441
|
+
- Updated dependencies [69251bf]
|
|
4442
|
+
- Updated dependencies [57e668f]
|
|
4443
|
+
- Updated dependencies [516663d]
|
|
4444
|
+
- Updated dependencies [41ac1b7]
|
|
4445
|
+
- Updated dependencies [1eaf0a1]
|
|
4446
|
+
- Updated dependencies [a09bc33]
|
|
4447
|
+
- Updated dependencies [460c4d0]
|
|
4448
|
+
- Updated dependencies [0ae27f7]
|
|
4449
|
+
- Updated dependencies [2533ec5]
|
|
4450
|
+
- Updated dependencies [78c0f9a]
|
|
4451
|
+
- Updated dependencies [bbe8b86]
|
|
4452
|
+
- Updated dependencies [8477be5]
|
|
4453
|
+
- Updated dependencies [279fb13]
|
|
4454
|
+
- Updated dependencies [2e82ab2]
|
|
4455
|
+
- Updated dependencies [ad07b65]
|
|
4456
|
+
- Updated dependencies [41f498b]
|
|
4457
|
+
- Updated dependencies [ef0d150]
|
|
4458
|
+
- Updated dependencies [f34226e]
|
|
4459
|
+
- Updated dependencies [564b605]
|
|
4460
|
+
- Updated dependencies [e1d4251]
|
|
4461
|
+
- Updated dependencies [40d3a33]
|
|
4462
|
+
- Updated dependencies [8b9dc62]
|
|
4463
|
+
- Updated dependencies [1184192]
|
|
4464
|
+
- Updated dependencies [a2a9747]
|
|
4465
|
+
- Updated dependencies [65e88e6]
|
|
4466
|
+
- Updated dependencies [a1609a6]
|
|
4467
|
+
- Updated dependencies [53f23bc]
|
|
4468
|
+
- Updated dependencies [c4533dc]
|
|
4469
|
+
- Updated dependencies [be60815]
|
|
4470
|
+
- Updated dependencies [37f6844]
|
|
4471
|
+
- Updated dependencies [93de4f6]
|
|
4472
|
+
- Updated dependencies [2b50261]
|
|
4473
|
+
- Updated dependencies [384f30d]
|
|
4474
|
+
- Updated dependencies [ac600e5]
|
|
4475
|
+
- Updated dependencies [97fba31]
|
|
4476
|
+
- Updated dependencies [232f61a]
|
|
4477
|
+
- Updated dependencies [d374caf]
|
|
4478
|
+
- Updated dependencies [5673576]
|
|
4479
|
+
- Updated dependencies [c1ef923]
|
|
4480
|
+
- Updated dependencies [911ceaa]
|
|
4481
|
+
- Updated dependencies [98eab36]
|
|
4482
|
+
- Updated dependencies [af5e292]
|
|
4483
|
+
- Updated dependencies [3fbbea1]
|
|
4484
|
+
- Updated dependencies [0bffb18]
|
|
4485
|
+
- Updated dependencies [800f455]
|
|
4486
|
+
- Updated dependencies [5458414]
|
|
4487
|
+
- Updated dependencies [3241559]
|
|
4488
|
+
- Updated dependencies [7f96b10]
|
|
4489
|
+
- Updated dependencies [167ec42]
|
|
4490
|
+
- Updated dependencies [616a2a5]
|
|
4491
|
+
- Updated dependencies [6c68b13]
|
|
4492
|
+
- Updated dependencies [0046d8f]
|
|
4493
|
+
- Updated dependencies [f1d4748]
|
|
4494
|
+
- Updated dependencies [bea374e]
|
|
4495
|
+
- Updated dependencies [b1119ec]
|
|
4496
|
+
- Updated dependencies [5607092]
|
|
4497
|
+
- Updated dependencies [9f23d2b]
|
|
4498
|
+
- Updated dependencies [578e025]
|
|
4499
|
+
- Updated dependencies [af025ee]
|
|
4500
|
+
- Updated dependencies [d109a4d]
|
|
4501
|
+
- Updated dependencies [598c89a]
|
|
4502
|
+
- Updated dependencies [4a0bd17]
|
|
4503
|
+
- Updated dependencies [b8b9af4]
|
|
4504
|
+
- Updated dependencies [d8b9259]
|
|
4505
|
+
- Updated dependencies [31676be]
|
|
4506
|
+
- Updated dependencies [8c0d52e]
|
|
4507
|
+
- Updated dependencies [aff10e2]
|
|
4508
|
+
- Updated dependencies [70a774b]
|
|
4509
|
+
- Updated dependencies [9ce096f]
|
|
4510
|
+
- Updated dependencies [e05db88]
|
|
4511
|
+
- Updated dependencies [7458a41]
|
|
4512
|
+
- Updated dependencies [ad13d63]
|
|
4513
|
+
- Updated dependencies [5ffcc14]
|
|
4514
|
+
- Updated dependencies [d971e51]
|
|
4515
|
+
- Updated dependencies [97abb24]
|
|
4516
|
+
- Updated dependencies [deb157a]
|
|
4517
|
+
- Updated dependencies [9c60144]
|
|
4518
|
+
- Updated dependencies [e7747f1]
|
|
4519
|
+
- Updated dependencies [d2ce342]
|
|
4520
|
+
- Updated dependencies [9695da7]
|
|
4521
|
+
- Updated dependencies [ac2f332]
|
|
4522
|
+
- Updated dependencies [a777058]
|
|
4523
|
+
- Updated dependencies [75444e3]
|
|
4524
|
+
- Updated dependencies [58b8346]
|
|
4525
|
+
- Updated dependencies [2d0bd16]
|
|
4526
|
+
- Updated dependencies [a9e17b4]
|
|
4527
|
+
- Updated dependencies [b8ce7dc]
|
|
4528
|
+
- Updated dependencies [dad51e5]
|
|
4529
|
+
- Updated dependencies [1c9c342]
|
|
4530
|
+
- Updated dependencies [787c738]
|
|
4531
|
+
- Updated dependencies [8396656]
|
|
4532
|
+
- Updated dependencies [dbbd38a]
|
|
4533
|
+
- Updated dependencies [61556dc]
|
|
4534
|
+
- Updated dependencies [8871c14]
|
|
4535
|
+
- Updated dependencies [93fe362]
|
|
4536
|
+
- Updated dependencies [dfc6975]
|
|
4537
|
+
- Updated dependencies [3cf4de0]
|
|
4538
|
+
- Updated dependencies [c9dc811]
|
|
4539
|
+
- Updated dependencies [144ef9b]
|
|
4540
|
+
- Updated dependencies [138ab04]
|
|
4541
|
+
- Updated dependencies [a0b9e91]
|
|
4542
|
+
- Updated dependencies [99bd015]
|
|
4543
|
+
- Updated dependencies [21e4585]
|
|
4544
|
+
- @object-ui/types@17.6.0
|
|
4545
|
+
- @object-ui/fields@17.6.0
|
|
4546
|
+
- @object-ui/i18n@17.6.0
|
|
4547
|
+
- @object-ui/react@17.6.0
|
|
4548
|
+
- @object-ui/components@17.6.0
|
|
4549
|
+
- @object-ui/core@17.6.0
|
|
4550
|
+
- @object-ui/permissions@17.6.0
|
|
4551
|
+
- @object-ui/mobile@17.6.0
|
|
4552
|
+
|
|
3
4553
|
## 17.5.0
|
|
4
4554
|
|
|
5
4555
|
### Minor Changes
|