@object-ui/plugin-form 16.0.0 → 17.1.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 +712 -1
- package/README.md +106 -0
- package/dist/DrawerForm.d.ts +1 -0
- package/dist/DrawerForm.d.ts.map +1 -0
- package/dist/EmbeddableForm.d.ts +1 -0
- package/dist/EmbeddableForm.d.ts.map +1 -0
- package/dist/FormAnalytics.d.ts +1 -0
- package/dist/FormAnalytics.d.ts.map +1 -0
- package/dist/FormSection.d.ts +1 -0
- package/dist/FormSection.d.ts.map +1 -0
- package/dist/LineItemsPanel.d.ts +1 -0
- package/dist/LineItemsPanel.d.ts.map +1 -0
- package/dist/MasterDetailForm.d.ts +9 -1
- package/dist/MasterDetailForm.d.ts.map +1 -0
- package/dist/ModalForm.d.ts +7 -2
- package/dist/ModalForm.d.ts.map +1 -0
- package/dist/ObjectForm.d.ts +1 -0
- package/dist/ObjectForm.d.ts.map +1 -0
- package/dist/SplitForm.d.ts +22 -2
- package/dist/SplitForm.d.ts.map +1 -0
- package/dist/TabbedForm.d.ts +13 -1
- package/dist/TabbedForm.d.ts.map +1 -0
- package/dist/WizardForm.d.ts +14 -1
- package/dist/WizardForm.d.ts.map +1 -0
- package/dist/autoLayout.d.ts +1 -0
- package/dist/autoLayout.d.ts.map +1 -0
- package/dist/deriveMasterDetail.d.ts +1 -0
- package/dist/deriveMasterDetail.d.ts.map +1 -0
- package/dist/fieldGroups.d.ts +1 -0
- package/dist/fieldGroups.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1655 -1457
- package/dist/index.umd.cjs +1 -1
- package/dist/masterDetailTx.d.ts +9 -37
- package/dist/masterDetailTx.d.ts.map +1 -0
- package/dist/occSave.d.ts +78 -0
- package/dist/occSave.d.ts.map +1 -0
- package/dist/sanitize.d.ts +1 -0
- package/dist/sanitize.d.ts.map +1 -0
- package/dist/sectionFields.d.ts +10 -2
- package/dist/sectionFields.d.ts.map +1 -0
- package/dist/successBehavior.d.ts +1 -0
- package/dist/successBehavior.d.ts.map +1 -0
- package/package.json +14 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,716 @@
|
|
|
1
1
|
# @object-ui/plugin-form
|
|
2
2
|
|
|
3
|
+
## 17.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 38ca8be: refactor(fields): `requiredWhen` is the only required-predicate slot — drop the retired `conditionalRequired` alias
|
|
8
|
+
|
|
9
|
+
`@objectstack/spec` 17 (objectstack#3855) **retired** `Field.conditionalRequired`,
|
|
10
|
+
the long-deprecated alias of `requiredWhen`. ObjectUI carried a back-compat read
|
|
11
|
+
for it in seven places; all of them are removed.
|
|
12
|
+
|
|
13
|
+
The removal is safe because the spec did not merely _stop emitting_ the key — it
|
|
14
|
+
made authoring it **fail loudly**. `retiredKey()` declares the key as
|
|
15
|
+
`z.never()`, so:
|
|
16
|
+
|
|
17
|
+
- `z.input` types it as `never` — writing it is a `tsc` error at the authoring site;
|
|
18
|
+
- the parse **rejects** it (verified against `17.0.0-rc.0`), at both `FieldSchema`
|
|
19
|
+
and `ObjectSchema`, with the prescription as the message:
|
|
20
|
+
|
|
21
|
+
> `conditionalRequired` was removed in @objectstack/spec 17 (#3855) — use
|
|
22
|
+
> `requiredWhen`. Rename the key; the value (a CEL predicate) is unchanged.
|
|
23
|
+
> Run `os migrate meta --from 16` to rewrite it automatically.
|
|
24
|
+
|
|
25
|
+
So spec-parsed metadata cannot carry the key — an object declaring it fails to
|
|
26
|
+
load rather than loading with the rule silently dropped. Keeping a renderer-side
|
|
27
|
+
`requiredWhen ?? conditionalRequired` would have re-created exactly the second
|
|
28
|
+
de-facto contract the tombstone exists to prevent: the key would have kept
|
|
29
|
+
working in the UI while being rejected everywhere else, hiding the producer's bug
|
|
30
|
+
(AGENTS.md #0.1). "Backend-agnostic" (#1) does not argue for keeping it either —
|
|
31
|
+
`conditionalRequired` is an ObjectStack-spec-ism, so the only producers that ever
|
|
32
|
+
emit it are ObjectStack producers on ≤16, and the spec ships them a converter.
|
|
33
|
+
|
|
34
|
+
Removed from:
|
|
35
|
+
|
|
36
|
+
| package | site |
|
|
37
|
+
| :----------------------- | :-------------------------------------------------------------------------------------------------------- |
|
|
38
|
+
| `@object-ui/types` | the `conditionalRequired?:` member on `FormField` |
|
|
39
|
+
| `@object-ui/core` | the `??` fallback + rules-param member in `resolveFieldRuleState` |
|
|
40
|
+
| `@object-ui/components` | three pass-throughs in the form renderer |
|
|
41
|
+
| `@object-ui/plugin-form` | `ObjectForm`, `ModalForm`, `sectionFields`, `deriveMasterDetail` (×2) |
|
|
42
|
+
| `@object-ui/app-shell` | the field inspector's legacy read/auto-migrate, and the key's entry in `clientValidation`'s CEL lint list |
|
|
43
|
+
|
|
44
|
+
**Studio authors lose nothing.** The object designer's draft validation parses
|
|
45
|
+
against the spec's own `ObjectSchema`, so a draft carrying the key now surfaces
|
|
46
|
+
the tombstone's rename prescription under the same `fields.<name>.conditionalRequired`
|
|
47
|
+
path the CEL lint used to report — a better message than the inspector's silent
|
|
48
|
+
auto-migration, and one the server agrees with. That behavior is pinned by a test.
|
|
49
|
+
|
|
50
|
+
**Migrating:** rename the key to `requiredWhen` (the CEL value is unchanged), or
|
|
51
|
+
run `os migrate meta --from 16`.
|
|
52
|
+
|
|
53
|
+
- 03bd53b: feat(form): `SplitForm` honours the spec's new `FormSection.pane`
|
|
54
|
+
|
|
55
|
+
A split form's panel assignment was a hardcoded positional rule — first section
|
|
56
|
+
left, everything else right. The rule was invisible in the metadata, so
|
|
57
|
+
reordering sections silently moved them across the divider, and an author could
|
|
58
|
+
not place two sections in the left pane at all.
|
|
59
|
+
|
|
60
|
+
Sections now declare their panel: `pane: 'primary' | 'secondary'`
|
|
61
|
+
(@objectstack/spec `FormSection.pane`, objectstack#4160). Placement follows the
|
|
62
|
+
key, not the array position — reordering paned sections never changes the
|
|
63
|
+
layout. Omitted keys keep the exact legacy rule (first section `primary`, rest
|
|
64
|
+
`secondary`), so existing metadata renders unchanged.
|
|
65
|
+
|
|
66
|
+
`ObjectForm`'s split dispatch copies the key through its per-key section mapping
|
|
67
|
+
(the path that once silently dropped `visibleOn`), and `ObjectFormSection`
|
|
68
|
+
declares it. The spec side rejects `pane` on non-split form types at parse, so
|
|
69
|
+
the key can never be an accepted-but-ignored no-op.
|
|
70
|
+
|
|
71
|
+
### Patch Changes
|
|
72
|
+
|
|
73
|
+
- 7639a61: fix(form): the spec↔runtime form-field chokepoint stops dropping spec 17 vocabulary, and the validator stops contradicting the renderer — #3090
|
|
74
|
+
|
|
75
|
+
`normalizeSectionField` — the one translation point between the spec's authored
|
|
76
|
+
form-field shape (`field` = object-field reference) and the runtime shape
|
|
77
|
+
(`name` = data path) — silently dropped four spec keys, worst of all the
|
|
78
|
+
ADR-0089 **canonical** `visibleWhen` spelling while the deprecated `visibleOn`
|
|
79
|
+
worked. Now:
|
|
80
|
+
|
|
81
|
+
- view-level `visibleWhen` routes into the view-level slot (`visibleOn`) so it
|
|
82
|
+
ANDs with the object-level rule instead of clobbering it, and the wizard's
|
|
83
|
+
final-submit gate folds the same slot into its verdict (before, a required
|
|
84
|
+
field the view itself hides could block submission from off-screen);
|
|
85
|
+
- `dependsOn`, `keyField`, and `disclosure` carry through;
|
|
86
|
+
- a behavioral parity gate walks the spec `FormFieldSchema` key set — a key the
|
|
87
|
+
spec adds fails as unmapped, a key it retires fails as stale.
|
|
88
|
+
|
|
89
|
+
`SelectOptionSchema` is now derived from `@objectstack/spec/data` by reference
|
|
90
|
+
(it used to strip `color` — which `@object-ui/fields` renders — plus `default`
|
|
91
|
+
and the per-option `visibleWhen` gate), with pinned divergences (`value`
|
|
92
|
+
widened for UI forms, `visibleWhen` on the #2212 wire contract) and documented
|
|
93
|
+
UI-only extensions (`disabled`, `icon`). `SelectOption` (TS) gains `color` and
|
|
94
|
+
`default`.
|
|
95
|
+
|
|
96
|
+
`FormFieldSchema` (the runtime vocabulary `objectui validate` enforces) now
|
|
97
|
+
covers every key the `FormField` interface declares — `widget`, `dependsOn`,
|
|
98
|
+
`hidden`, `readonly`, `visibleOn`/`visibleWhen`/`readonlyWhen`/`requiredWhen`,
|
|
99
|
+
`span` — and `type` is optional, matching the interface. A typo'd predicate now
|
|
100
|
+
fails loudly instead of being stripped; spec-shape fields (`{ field: … }`) are
|
|
101
|
+
still rejected, pinning the two-layer boundary.
|
|
102
|
+
|
|
103
|
+
- 94e63ef: fix(form): the runtime `field` metadata slot is declared instead of smuggled, and importing the spec's FormField is a lint error — #3090
|
|
104
|
+
|
|
105
|
+
`FormField.field` — the slot where object-bound form paths stash the resolved
|
|
106
|
+
field-metadata **object** for widgets — rode through the index signature,
|
|
107
|
+
undeclared, readable only via `as any`. Same key, different layer: in the spec
|
|
108
|
+
form-view vocabulary `field` is a _string_ (the referenced object-field name),
|
|
109
|
+
and the undeclared slot kept that pun latent. The slot is now declared
|
|
110
|
+
(`field?: Record<string, any>`) with the invariant in its JSDoc: on a runtime
|
|
111
|
+
FormField it is never a string — the authored string form ends at the
|
|
112
|
+
`normalizeSectionField` chokepoint, and a tripwire test pins that across all
|
|
113
|
+
three input shapes. Assigning a string is now a compile error; the `as any`
|
|
114
|
+
casts at the read sites are gone.
|
|
115
|
+
|
|
116
|
+
A `no-restricted-imports` tripwire bans importing `FormField`/
|
|
117
|
+
`FormFieldSchema` from `@objectstack/spec/ui` inside this repo: the spec's
|
|
118
|
+
FormField TYPE erases to `any` in its dist (objectstack#4171), so the
|
|
119
|
+
misimport silently deletes type safety — tsc says nothing. The lint message
|
|
120
|
+
names the two layers and the correct import. The drift-guard parity test is
|
|
121
|
+
the one legitimate importer, exempted inline with its reason.
|
|
122
|
+
|
|
123
|
+
Ledger: `FormField` and `FormFieldSchema` move from untriaged DEBT to ALLOW
|
|
124
|
+
with the two-layer rationale written down (122 → 120).
|
|
125
|
+
|
|
126
|
+
- aeb0bd2: fix(form): a tabbed/split form honours the form view's own `columns`
|
|
127
|
+
|
|
128
|
+
`FormView.columns` is a spec key, but only `ObjectForm`'s simple path and
|
|
129
|
+
`ModalForm` read it. `TabbedForm` and `SplitForm` derived the grid width from the
|
|
130
|
+
per-section `columns` alone, so a view declaring `columns: 3` rendered 3 columns
|
|
131
|
+
in a modal and **single-column** as a tab or split — the same metadata laying out
|
|
132
|
+
differently depending on which host picked it up.
|
|
133
|
+
|
|
134
|
+
Both now resolve the grid the way the other hosts already did:
|
|
135
|
+
|
|
136
|
+
explicit form `columns` ?? widest section's `columns` ?? 1
|
|
137
|
+
|
|
138
|
+
The two keys answer different questions and the precedence reflects that: the
|
|
139
|
+
view's `columns` is how wide the grid is, a section's `columns` is how densely
|
|
140
|
+
that section fills it (via per-field `colSpan`). `columns` is declared on
|
|
141
|
+
`TabbedFormSchema` / `SplitFormSchema` accordingly — `ObjectForm` already spread
|
|
142
|
+
it through, it was simply being dropped on arrival.
|
|
143
|
+
|
|
144
|
+
- c735bf7: fix(form): a spec-vocabulary field no longer crashes the standalone form, and every surface now says which vocabulary you meant — #3090
|
|
145
|
+
|
|
146
|
+
Writing the regression test against the unfixed renderer proved the failure
|
|
147
|
+
was worse than the assumed silent drop: a `{ field: 'x' }` entry (spec
|
|
148
|
+
form-VIEW vocabulary) slipped past the `f?.name` guards into a
|
|
149
|
+
react-hook-form Controller with `name === undefined` and crashed the whole
|
|
150
|
+
standalone form on `name.split('.')`, with nothing naming the culprit entry.
|
|
151
|
+
The renderer now partitions such entries out — the rest of the form renders —
|
|
152
|
+
and surfaces them with an inline alert plus a console.error whose text is the
|
|
153
|
+
fix instruction (rename to `name`, or use an object-bound form whose sections
|
|
154
|
+
accept the spec shape).
|
|
155
|
+
|
|
156
|
+
`objectui validate` grows the same boundary awareness: on failure, a
|
|
157
|
+
`{ field: … }` entry in a standalone form gets a "likely cause" hint naming
|
|
158
|
+
the real fix instead of the bare `invalid_union` — the previous message read
|
|
159
|
+
as "bolt a `name` on", which converts spec metadata wrongly. On success,
|
|
160
|
+
mixed-vocabulary entries (`name` + string `field`) get a warning: they
|
|
161
|
+
validate, but the spec key is dead weight the renderer ignores.
|
|
162
|
+
|
|
163
|
+
`normalizeSectionField` warns (once per site) when an authored section field
|
|
164
|
+
mixes both identity keys — the spec branch derives the runtime name from
|
|
165
|
+
`field`, so an authored `name` was silently overwritten.
|
|
166
|
+
|
|
167
|
+
- e339d60: fix(plugin-form): swapping `recordId` no longer leaves the previous record on screen
|
|
168
|
+
|
|
169
|
+
`loading` in `ModalForm` / `DrawerForm` / `TabbedForm` / `SplitForm` was only ever
|
|
170
|
+
set `true` once, by `useState(true)`, and thereafter only ever set `false`. A
|
|
171
|
+
`recordId` change therefore re-entered the fetch effect **without** going back
|
|
172
|
+
through the loading branch: the form stayed mounted showing — and accepting edits
|
|
173
|
+
to — record A's values, with nothing indicating a different record had been asked
|
|
174
|
+
for, until B's response landed and replaced them in place. Anything typed in that
|
|
175
|
+
window read as A's on screen and would have been submitted against B.
|
|
176
|
+
|
|
177
|
+
The same effect had no staleness guard either, so two overlapping reads landed in
|
|
178
|
+
**completion** order rather than request order: ask for B then C, and a slow B
|
|
179
|
+
arriving last left the form showing B while the caller had asked for C.
|
|
180
|
+
|
|
181
|
+
Both are the same defect from the user's side — the form displays a record nobody
|
|
182
|
+
asked for — so both are fixed:
|
|
183
|
+
|
|
184
|
+
- a change of record re-enters the loading state before the read, so the previous
|
|
185
|
+
record is off screen while the next one is in flight. Gated on the record
|
|
186
|
+
actually changing: the effect also re-runs on `initialData`/`initialValues`
|
|
187
|
+
identity churn (callers rebuild those objects every render), and flashing the
|
|
188
|
+
loading state for that would thrash;
|
|
189
|
+
- the effect's cleanup marks its read stale, so a response that is no longer the
|
|
190
|
+
one being awaited is dropped instead of overwriting a newer record.
|
|
191
|
+
|
|
192
|
+
`ObjectForm` already re-entered loading before its fetch, which is why this only
|
|
193
|
+
ever reproduced on the four sectioned variants.
|
|
194
|
+
|
|
195
|
+
**Also fixed, a consequence of the above:** hiding the form unmounts the inner
|
|
196
|
+
renderer, and that renderer is the only thing that reports dirtiness via
|
|
197
|
+
`onDirtyChange` — it gets no chance to report `false` on the way out. Without
|
|
198
|
+
clearing the flag, the overlay's unsaved-input guards would stay armed for input
|
|
199
|
+
belonging to a record no longer on screen: a plain refresh would prompt, and
|
|
200
|
+
closing would offer to discard nothing.
|
|
201
|
+
|
|
202
|
+
- aa35561: fix(form): a split create/edit form no longer loses the panel you are not submitting from (#2153)
|
|
203
|
+
|
|
204
|
+
`SplitForm` rendered one `SchemaRenderer` — one react-hook-form instance and one
|
|
205
|
+
`<form>` element — **per section**, and its two groups of sections live in
|
|
206
|
+
separate resizable panels. So each panel owned isolated form state: submitting
|
|
207
|
+
from one panel's action bar sent only that section's fields and silently dropped
|
|
208
|
+
everything the user had typed on the other side of the divider. Filling both
|
|
209
|
+
panels and clicking Create persisted `{ subject }` alone.
|
|
210
|
+
|
|
211
|
+
The same isolation killed cross-panel field rules: a `visibleWhen` in the right
|
|
212
|
+
panel referencing a left-panel field never saw that field in its record, so the
|
|
213
|
+
predicate faulted and failed **open** — the field the author meant to hide was
|
|
214
|
+
always shown.
|
|
215
|
+
|
|
216
|
+
Both panels are now ONE form. The panel group became a layout the form renderer
|
|
217
|
+
owns, via a new `FormSchema.fieldPanes` (+ `fieldPanesOrientation`,
|
|
218
|
+
`fieldPanesResizable`) that mirrors `fieldTabs` (#2959): the `<form>` wraps the
|
|
219
|
+
whole `ResizablePanelGroup` and each pane holds only fields, which is what lets a
|
|
220
|
+
single react-hook-form instance span the divider. Sections inside a pane render
|
|
221
|
+
behind the inline `section-divider` header, each at its own declared column
|
|
222
|
+
density within the form's shared grid.
|
|
223
|
+
|
|
224
|
+
One more fix falls out of moving the panels into the renderer: `splitResizable:
|
|
225
|
+
false` now actually pins the divider. It previously only hid the grip — the
|
|
226
|
+
separator stayed draggable, because nothing passed the panel library's
|
|
227
|
+
`disabled`.
|
|
228
|
+
|
|
229
|
+
Each pane is its own `@container`, so a multi-column section collapses to fewer
|
|
230
|
+
columns as its panel is dragged narrower instead of overflowing.
|
|
231
|
+
|
|
232
|
+
- 3c1f321: fix(form): a tabbed/sectioned create-edit form no longer loses the tabs you are not looking at (#2959, #2153)
|
|
233
|
+
|
|
234
|
+
The explicit-`sections` path rendered one `SchemaRenderer` — one react-hook-form
|
|
235
|
+
instance and one `<form>` element — **per section**, all sharing the same
|
|
236
|
+
`formId`. Two failures compounded:
|
|
237
|
+
|
|
238
|
+
1. the footer submit button (`form={formId}`) can only be associated with the
|
|
239
|
+
**first** of those forms, so section 2+ never reached the payload; and
|
|
240
|
+
2. in the `tabbed` variant Radix unmounted the inactive panel, destroying that
|
|
241
|
+
tab's form state outright.
|
|
242
|
+
|
|
243
|
+
Reported flow (HotCRM, 3 tabs, required `description` on tab 3): fill tab 1 →
|
|
244
|
+
submit → server 400 `description is required` → switch to tab 3, fill it →
|
|
245
|
+
submit → the server now reports `subject; description; status; priority` **all**
|
|
246
|
+
missing, because the second submit's body had lost every earlier value.
|
|
247
|
+
|
|
248
|
+
`ModalForm` (stacked and `contentLayout: 'tabbed'`) and `TabbedForm` now render
|
|
249
|
+
ONE form for all sections, matching `ObjectForm` / `DrawerForm`. Stacked sections
|
|
250
|
+
use the existing inline `section-divider` header (which now also renders the
|
|
251
|
+
section's `description`); tabbed sections go through a new
|
|
252
|
+
`FormSchema.fieldTabs` (+ `defaultFieldTab`, `fieldTabsPosition`) that the form
|
|
253
|
+
renderer distributes into **force-mounted** Radix panels — CSS-hidden rather
|
|
254
|
+
than unmounted, since react-hook-form skips validation for unmounted fields,
|
|
255
|
+
which is how a required field on a tab nobody opened used to sail past the
|
|
256
|
+
client and come back as a server 400.
|
|
257
|
+
|
|
258
|
+
Validation feedback now points at the tab: a rejected field activates its tab and
|
|
259
|
+
every tab holding one is marked on its trigger, for client-side rules and server
|
|
260
|
+
`fields[]` rejections alike.
|
|
261
|
+
|
|
262
|
+
- c0d0bc8: fix(form): a wizard with `allowSkip` no longer submits past the required fields you skipped
|
|
263
|
+
|
|
264
|
+
`allowSkip` let the user jump to any step from the indicator, and
|
|
265
|
+
`handleStepClick` did so without validating anything on the way. Since a wizard
|
|
266
|
+
mounts ONE step at a time and react-hook-form only validates the fields currently
|
|
267
|
+
**mounted**, a required field on a step nobody opened was never registered, never
|
|
268
|
+
validated, and simply absent from the payload.
|
|
269
|
+
|
|
270
|
+
Measured against the unfixed component — 3 steps, required `owner` on step 2,
|
|
271
|
+
`allowSkip: true`, click step 3's indicator, fill it, hit Create:
|
|
272
|
+
|
|
273
|
+
createCalls: 1
|
|
274
|
+
payload: { subject: 'S1', notes: 'S3' } // `owner` missing entirely
|
|
275
|
+
UI mentions "required": false // nothing said so
|
|
276
|
+
|
|
277
|
+
So an invalid create went out and the client said nothing about why — #2959's
|
|
278
|
+
validation half, wearing a wizard's clothes.
|
|
279
|
+
|
|
280
|
+
The final submit now checks the WHOLE declared field set, and when something is
|
|
281
|
+
outstanding it returns the user to the first step that has one, marks that step's
|
|
282
|
+
indicator (`data-error="true"`, destructive circle + icon), names the fields in a
|
|
283
|
+
toast, and sends nothing. Conditional rules are honoured: the check runs on the
|
|
284
|
+
canonical `resolveFieldRuleState`, the same engine the form renderer and the
|
|
285
|
+
server's rule-validator use, so a field hidden by `visibleWhen` or not yet
|
|
286
|
+
required by `requiredWhen` is not demanded. The sequential path is unaffected —
|
|
287
|
+
a forward jump is refused without `allowSkip`, so Next already validated each step.
|
|
288
|
+
|
|
289
|
+
Also in `WizardForm`:
|
|
290
|
+
|
|
291
|
+
- `FormView.columns` is now honoured (spec key, previously dropped): the grid
|
|
292
|
+
width is the view's `columns`, else the step's own. Unlike the tabbed/split
|
|
293
|
+
hosts there is no widest-section fallback — wizard steps never share a viewport,
|
|
294
|
+
so each keeps its authored width.
|
|
295
|
+
- the root gained `@container`. The step grid is sized with container queries, and
|
|
296
|
+
without a container ancestor every `@md:`/`@2xl:` variant was inert — a step
|
|
297
|
+
declaring 2 columns rendered single-column. Found by running it in a browser;
|
|
298
|
+
the class was present all along, which is why asserting the class alone had
|
|
299
|
+
missed it.
|
|
300
|
+
|
|
301
|
+
- Updated dependencies [62311b6]
|
|
302
|
+
- Updated dependencies [fc0272a]
|
|
303
|
+
- Updated dependencies [9e7349e]
|
|
304
|
+
- Updated dependencies [8864971]
|
|
305
|
+
- Updated dependencies [1cf0de7]
|
|
306
|
+
- Updated dependencies [752e18f]
|
|
307
|
+
- Updated dependencies [c785740]
|
|
308
|
+
- Updated dependencies [b41f401]
|
|
309
|
+
- Updated dependencies [19e9fa0]
|
|
310
|
+
- Updated dependencies [d61efd1]
|
|
311
|
+
- Updated dependencies [95b7214]
|
|
312
|
+
- Updated dependencies [7d9734d]
|
|
313
|
+
- Updated dependencies [6ae818e]
|
|
314
|
+
- Updated dependencies [9eb932b]
|
|
315
|
+
- Updated dependencies [746dd00]
|
|
316
|
+
- Updated dependencies [aebfa4f]
|
|
317
|
+
- Updated dependencies [38ca8be]
|
|
318
|
+
- Updated dependencies [3cb9646]
|
|
319
|
+
- Updated dependencies [68ef584]
|
|
320
|
+
- Updated dependencies [4952edf]
|
|
321
|
+
- Updated dependencies [7f0252e]
|
|
322
|
+
- Updated dependencies [c4d7b20]
|
|
323
|
+
- Updated dependencies [c769d3d]
|
|
324
|
+
- Updated dependencies [7639a61]
|
|
325
|
+
- Updated dependencies [94e63ef]
|
|
326
|
+
- Updated dependencies [c735bf7]
|
|
327
|
+
- Updated dependencies [02aef0c]
|
|
328
|
+
- Updated dependencies [6f29aa5]
|
|
329
|
+
- Updated dependencies [d21794c]
|
|
330
|
+
- Updated dependencies [c4db402]
|
|
331
|
+
- Updated dependencies [5319bf1]
|
|
332
|
+
- Updated dependencies [49e5671]
|
|
333
|
+
- Updated dependencies [2307b52]
|
|
334
|
+
- Updated dependencies [9a04d25]
|
|
335
|
+
- Updated dependencies [b5b97e2]
|
|
336
|
+
- Updated dependencies [f59f2c1]
|
|
337
|
+
- Updated dependencies [07de839]
|
|
338
|
+
- Updated dependencies [2a40b5e]
|
|
339
|
+
- Updated dependencies [df613fa]
|
|
340
|
+
- Updated dependencies [4874117]
|
|
341
|
+
- Updated dependencies [ad0183a]
|
|
342
|
+
- Updated dependencies [ce08d55]
|
|
343
|
+
- Updated dependencies [eb4b740]
|
|
344
|
+
- Updated dependencies [aecc934]
|
|
345
|
+
- Updated dependencies [5b084eb]
|
|
346
|
+
- Updated dependencies [aa1240a]
|
|
347
|
+
- Updated dependencies [2374a49]
|
|
348
|
+
- Updated dependencies [390c071]
|
|
349
|
+
- Updated dependencies [d10f526]
|
|
350
|
+
- Updated dependencies [2d5d594]
|
|
351
|
+
- Updated dependencies [ea7f477]
|
|
352
|
+
- Updated dependencies [379728f]
|
|
353
|
+
- Updated dependencies [7f23cd0]
|
|
354
|
+
- Updated dependencies [0ded602]
|
|
355
|
+
- Updated dependencies [24e0e0a]
|
|
356
|
+
- Updated dependencies [f8a95e5]
|
|
357
|
+
- Updated dependencies [3a6cf24]
|
|
358
|
+
- Updated dependencies [aa35561]
|
|
359
|
+
- Updated dependencies [03bd53b]
|
|
360
|
+
- Updated dependencies [3c1f321]
|
|
361
|
+
- Updated dependencies [a045a32]
|
|
362
|
+
- Updated dependencies [912496d]
|
|
363
|
+
- Updated dependencies [80edbd4]
|
|
364
|
+
- Updated dependencies [9867281]
|
|
365
|
+
- @object-ui/core@17.1.0
|
|
366
|
+
- @object-ui/components@17.1.0
|
|
367
|
+
- @object-ui/react@17.1.0
|
|
368
|
+
- @object-ui/types@17.1.0
|
|
369
|
+
- @object-ui/i18n@17.1.0
|
|
370
|
+
- @object-ui/permissions@17.1.0
|
|
371
|
+
- @object-ui/fields@17.1.0
|
|
372
|
+
|
|
373
|
+
## 17.0.0
|
|
374
|
+
|
|
375
|
+
### Minor Changes
|
|
376
|
+
|
|
377
|
+
- f9bbddb: feat: gate detail/form edit & delete on the server's effective operation set (#3546)
|
|
378
|
+
|
|
379
|
+
PR-4 (#3391) wired the **list/toolbar** surface (ObjectView Import, ListView /
|
|
380
|
+
ObjectGrid Export) to the server-resolved effective API operation set
|
|
381
|
+
(`/me/permissions` `apiOperations`, intersected via
|
|
382
|
+
`resolveCrudAffordances(obj, effectiveApiOperations?)`). The **detail / form**
|
|
383
|
+
surfaces still gated edit/delete on the bucket + `userActions` alone. This
|
|
384
|
+
extends the same intersection to them, so the record page and its forms never
|
|
385
|
+
offer an operation the server would 405.
|
|
386
|
+
|
|
387
|
+
- **core** `isObjectInlineEditable(obj, effectiveApiOperations?)` gains the same
|
|
388
|
+
optional second argument as `resolveCrudAffordances` — inline-edit is now
|
|
389
|
+
additionally ANDed with the server allowing `update`.
|
|
390
|
+
- **app-shell** `RecordDetailView` threads the object's effective operations into
|
|
391
|
+
the synthesized Edit/Delete header actions and the record-body inline-edit
|
|
392
|
+
gate (`canEdit`); `RelatedRecordActionsBridge` intersects each **child**
|
|
393
|
+
object's Create/Edit/Delete handlers with that child's own effective set.
|
|
394
|
+
- **plugin-detail** `record:details` ANDs its inline-edit affordance with the
|
|
395
|
+
object's effective `update`.
|
|
396
|
+
- **plugin-form** `ObjectForm`'s blanket managed-object field lock also engages
|
|
397
|
+
when the server denies `update` (edit mode) / `create` (create mode).
|
|
398
|
+
|
|
399
|
+
Backward-compatible: a missing effective set (unrestricted object, older
|
|
400
|
+
backend, or no `PermissionProvider`) leaves the resolved affordance untouched —
|
|
401
|
+
the bucket/`userActions` decision wins, exactly as today. Layers on top of the
|
|
402
|
+
existing per-object `check('edit')` / `check('delete')` permission gates
|
|
403
|
+
(intersection, never union).
|
|
404
|
+
|
|
405
|
+
### Patch Changes
|
|
406
|
+
|
|
407
|
+
- 6dee2cb: feat(form): consume spec-aligned FormView buttons/defaults in ObjectForm
|
|
408
|
+
|
|
409
|
+
The authored `@objectstack/spec` FormViewSchema carries structured
|
|
410
|
+
`buttons.{submit,cancel,reset}.{show,label}` and `defaults`, but the form
|
|
411
|
+
renderer only read the flat renderer-invented `showSubmit`/`submitText`/
|
|
412
|
+
`showCancel`/`cancelText`/`showReset`/`initialValues`. That left the two spec
|
|
413
|
+
keys parsed-but-inert (ADR-0078) and stuck at `experimental` in the spec
|
|
414
|
+
liveness ledger.
|
|
415
|
+
|
|
416
|
+
`ObjectForm` now folds the structured shape down onto those flat props inside
|
|
417
|
+
its existing normalization pass, so every entry path (ObjectView
|
|
418
|
+
drawer/modal/page, RecordFormPage) honors it. An explicitly-set flat key still
|
|
419
|
+
wins, so metadata authored against the deprecated flat keys is unchanged.
|
|
420
|
+
`ObjectView` and `RecordFormPage` forward `buttons`/`defaults` from the spec
|
|
421
|
+
form view. `ObjectFormSchema` gains the optional `buttons`/`defaults` fields.
|
|
422
|
+
|
|
423
|
+
Refs objectstack-ai/objectstack#1894, objectstack-ai/objectstack#2998.
|
|
424
|
+
|
|
425
|
+
- 2f947e4: fix(page,field): consume the spec's `type`/`label`/`maxLength` keys (framework#1878 §3 naming-drift recheck)
|
|
426
|
+
|
|
427
|
+
Three forward-drifts where objectui read a different key than the spec
|
|
428
|
+
declares, so authoring the documented key silently no-oped:
|
|
429
|
+
|
|
430
|
+
- **page `type` → `pageType`** (app-shell + components): `PageSchema` declares
|
|
431
|
+
the page KIND as `type`, but `PageRenderer` reads `schema.pageType` and fell
|
|
432
|
+
back to `'record'` — and nothing mapped between them. Every non-record page
|
|
433
|
+
(`home`/`app`/`list`/`utility`) rendered with the record max-width, a wrong
|
|
434
|
+
`data-page-type` attribute, and a suppressed header. `PageView` now passes
|
|
435
|
+
`pageType` alongside the SchemaNode discriminator `type`.
|
|
436
|
+
- **page `label` → `title`** (components): `PageSchema.label` is required but the
|
|
437
|
+
region renderer read only `title`. Now dual-reads `title ?? label`, mirroring
|
|
438
|
+
the fallback `DashboardRenderer` already uses. Coupled with the above — the
|
|
439
|
+
header is gated on `pageType !== 'record'`, so both were needed for a title to
|
|
440
|
+
appear.
|
|
441
|
+
- **field `maxLength`/`minLength`** (plugin-form + fields): validation already
|
|
442
|
+
dual-read these, but `ObjectForm`'s HTML-attribute pass and `TextAreaField`
|
|
443
|
+
read `max_length` only, so a spec-authored `maxLength` gave no browser cap and
|
|
444
|
+
no character counter. Both now dual-read, matching `buildValidationRules`.
|
|
445
|
+
|
|
446
|
+
Verified in the browser against the showcase: `capability_map` (`type: 'home'`)
|
|
447
|
+
now renders `data-page-type="home"`, the `home` max-width and its page title;
|
|
448
|
+
record pages are unchanged.
|
|
449
|
+
|
|
450
|
+
- 662bdf9: fix(fls): wire the real per-caller FLS channel into import targets and grid
|
|
451
|
+
columns; remove the never-populated `field.permissions` shape (objectstack#3661)
|
|
452
|
+
|
|
453
|
+
The `permissions?: { read?, write?, edit? }` key on `@object-ui/types` field
|
|
454
|
+
definitions (Phase 3.2.6) was declared-but-never-enforced: no producer in the
|
|
455
|
+
stack ever populated it, so every guard reading it short-circuited to "allow".
|
|
456
|
+
Per ADR-0049 enforce-or-remove, the shape is deleted and the three consumers
|
|
457
|
+
now use the server-resolved `/auth/me/permissions` channel
|
|
458
|
+
(`usePermissions().checkField`) — the same channel ObjectForm/ModalForm/ListView
|
|
459
|
+
already enforce:
|
|
460
|
+
|
|
461
|
+
- **ImportWizard target fields (app-shell `ObjectView`)**: the importable
|
|
462
|
+
field set (and thus the downloadable CSV template's columns) now drops
|
|
463
|
+
fields the caller cannot edit, instead of offering columns the server's
|
|
464
|
+
FLS write gate would 403.
|
|
465
|
+
- **ObjectGrid auto-derived columns**: columns the caller cannot read are
|
|
466
|
+
dropped (same gate ListView applies), instead of a dead schema-shape check.
|
|
467
|
+
- **ObjectForm**: the redundant dead guard in field generation is removed;
|
|
468
|
+
the existing `applyFieldPerms` gate remains the real enforcement point.
|
|
469
|
+
|
|
470
|
+
BREAKING CHANGE: `@object-ui/types` field definitions no longer accept a
|
|
471
|
+
`permissions` key. It never carried data at runtime; consumers needing
|
|
472
|
+
per-caller field-level permissions must use `@object-ui/permissions`
|
|
473
|
+
(`MePermissionsProvider` + `useFieldPermissions`/`checkField`).
|
|
474
|
+
|
|
475
|
+
- dc7a798: fix(plugin-grid,plugin-form,plugin-designer,cli,vscode-extension): type-check the last five unchecked packages, and fix the two runtime bugs that hid there (#2919)
|
|
476
|
+
|
|
477
|
+
Closes the remaining `DEBT` entries from the #2911 sweep. Each package gains
|
|
478
|
+
`"type-check": "tsc --noEmit"` and loses its entry in
|
|
479
|
+
`scripts/check-type-check-coverage.mjs`; coverage goes 36 -> 41 of 45 and
|
|
480
|
+
outstanding errors 25 -> 5 (only #2916 `plugin-view` and #2918 `layout` remain).
|
|
481
|
+
|
|
482
|
+
**Two of these were real bugs, not just type noise.**
|
|
483
|
+
|
|
484
|
+
`@object-ui/cli` — `objectui validate` could never report a validation failure.
|
|
485
|
+
`ZodError.errors` was removed in Zod 4 (the repo is on 4.4.3), so `.errors` read
|
|
486
|
+
`undefined` and `.forEach` threw a `TypeError` that the enclosing `catch`
|
|
487
|
+
reported as `✗ Error reading or parsing schema file: Cannot read properties of
|
|
488
|
+
undefined` — swallowing the very errors the command exists to print. Now reads
|
|
489
|
+
`.issues`. Verified against the built CLI: an invalid schema now prints
|
|
490
|
+
`1. Invalid input / Code: invalid_union` and exits 1.
|
|
491
|
+
|
|
492
|
+
`@object-ui/plugin-grid` — grouping a grid by a boolean column showed the raw
|
|
493
|
+
i18n key. `t('grid.booleanTrue', 'Yes')` asked for a key present in neither
|
|
494
|
+
`GRID_DEFAULT_TRANSLATIONS` nor any locale bundle, and passed the English
|
|
495
|
+
fallback as a bare second argument — which `createSafeTranslation`'s no-provider
|
|
496
|
+
translator reads as an _options object_, so the fallback never applied and the
|
|
497
|
+
header rendered the literal `grid.booleanTrue`. Switched to the `grid.yes` /
|
|
498
|
+
`grid.no` keys the boolean cell renderer (`ObjectGrid.tsx`) and
|
|
499
|
+
`BulkActionDialog` already use, with the fallback passed as `defaultValue`.
|
|
500
|
+
Covered by a new regression test, confirmed to fail against the old code.
|
|
501
|
+
|
|
502
|
+
The rest are type-only corrections that preserve runtime behaviour exactly:
|
|
503
|
+
|
|
504
|
+
- **plugin-grid** `importParsers.ts` — `scorePair`'s `score`/`reason` moved into
|
|
505
|
+
one `best` record. They were captured `let`s mutated only inside the `bump`
|
|
506
|
+
closure, which TypeScript's control-flow analysis does not track, so it still
|
|
507
|
+
believed `reason` was `'none'` at the type gate and flagged the comparisons as
|
|
508
|
+
non-overlapping (TS2367). The gate — which stops a text column being mapped
|
|
509
|
+
onto a number field — is unchanged; its two dedicated tests still pass.
|
|
510
|
+
- **plugin-form** — `SectionFieldsContext.fieldLabel` now requires `fallback`,
|
|
511
|
+
matching the `useSafeFieldLabel` producer in `@object-ui/i18n` (an omitted
|
|
512
|
+
fallback could not satisfy the `=> string` return, and all four call sites
|
|
513
|
+
already pass one). This one signature cleared six errors.
|
|
514
|
+
`MasterDetailFormSchema.recordId` widens to `string | number`, matching
|
|
515
|
+
`ObjectFormSchema` and the five envelopes that forward straight into it;
|
|
516
|
+
it is narrowed with `String()` only at the batch-transaction boundary, whose
|
|
517
|
+
`BatchTransactionOperation.id` is a string by protocol (the `isEdit` guard
|
|
518
|
+
already proves it non-null there). `deriveMasterDetail`'s column sort gets an
|
|
519
|
+
explicit `fillPriority` helper — `GridColumn.type` is optional, and a column
|
|
520
|
+
without one keeps sorting at priority 5 exactly as the old
|
|
521
|
+
`TYPE_FILL_PRIORITY[undefined] ?? 5` lookup put it.
|
|
522
|
+
- **plugin-designer** — unused `index` parameter prefixed `_`, matching the
|
|
523
|
+
`_entry` beside it.
|
|
524
|
+
- **cli** — a stale `@ts-expect-error` removed; `viteConfig` is typed `any`, so
|
|
525
|
+
the line it guarded had stopped erroring.
|
|
526
|
+
- **vscode-extension** (`object-ui`) — migrated off `moduleResolution: "node"`,
|
|
527
|
+
which is deprecated and stops working in TypeScript 7, to `node16` paired with
|
|
528
|
+
`module: "node16"` (the package has no `"type": "module"`, so node16 resolves
|
|
529
|
+
it as the CommonJS that tsup emits, and it gains the `exports`-map awareness
|
|
530
|
+
node10 lacks). Its error count was under-reported as 1: that TS5107 config
|
|
531
|
+
error masked four more. The package uses `console`/`Buffer` but sets
|
|
532
|
+
`lib: ["ES2020"]` with no DOM and never declared `@types/node` — added, with an
|
|
533
|
+
explicit `types: ["node", "vscode"]`.
|
|
534
|
+
|
|
535
|
+
Also: `plugin-grid`, `plugin-form` and `plugin-designer` gain the `baseUrl` +
|
|
536
|
+
`paths` override their type-checked plugin peers already carry, and `cli` an
|
|
537
|
+
empty `paths`. Without it the inherited root `paths` point `@object-ui/*` at
|
|
538
|
+
sibling `src/`, which is outside each project's `rootDir` and produces the ~104
|
|
539
|
+
spurious TS6059 errors noted in #2915; workspace deps instead resolve through
|
|
540
|
+
node_modules to built `.d.ts`, which `type-check`'s `dependsOn: ["^build"]`
|
|
541
|
+
guarantees exist.
|
|
542
|
+
|
|
543
|
+
Verified the gate genuinely covers all five rather than trusting the green:
|
|
544
|
+
injecting a type error into each package makes `pnpm type-check --filter <pkg>`
|
|
545
|
+
fail, which was impossible before this change.
|
|
546
|
+
|
|
547
|
+
- Updated dependencies [7b21891]
|
|
548
|
+
- Updated dependencies [0b3be01]
|
|
549
|
+
- Updated dependencies [3c4d935]
|
|
550
|
+
- Updated dependencies [4b1ed7d]
|
|
551
|
+
- Updated dependencies [4b60d2d]
|
|
552
|
+
- Updated dependencies [952b978]
|
|
553
|
+
- Updated dependencies [de5e40c]
|
|
554
|
+
- Updated dependencies [1a03af6]
|
|
555
|
+
- Updated dependencies [3e886eb]
|
|
556
|
+
- Updated dependencies [cfc675e]
|
|
557
|
+
- Updated dependencies [20df08c]
|
|
558
|
+
- Updated dependencies [1767124]
|
|
559
|
+
- Updated dependencies [8ecf5a6]
|
|
560
|
+
- Updated dependencies [af705b9]
|
|
561
|
+
- Updated dependencies [0502a7c]
|
|
562
|
+
- Updated dependencies [7b35e4b]
|
|
563
|
+
- Updated dependencies [8fb1295]
|
|
564
|
+
- Updated dependencies [e16ed2d]
|
|
565
|
+
- Updated dependencies [c6fd752]
|
|
566
|
+
- Updated dependencies [f9bbddb]
|
|
567
|
+
- Updated dependencies [dfd3705]
|
|
568
|
+
- Updated dependencies [c77108c]
|
|
569
|
+
- Updated dependencies [2735de6]
|
|
570
|
+
- Updated dependencies [697cda4]
|
|
571
|
+
- Updated dependencies [c19ac11]
|
|
572
|
+
- Updated dependencies [6dee2cb]
|
|
573
|
+
- Updated dependencies [e05f052]
|
|
574
|
+
- Updated dependencies [0502a7c]
|
|
575
|
+
- Updated dependencies [faad45e]
|
|
576
|
+
- Updated dependencies [09c6a17]
|
|
577
|
+
- Updated dependencies [c7cff19]
|
|
578
|
+
- Updated dependencies [ba73a02]
|
|
579
|
+
- Updated dependencies [cd09a7b]
|
|
580
|
+
- Updated dependencies [f1abf0e]
|
|
581
|
+
- Updated dependencies [f05b84e]
|
|
582
|
+
- Updated dependencies [9b4b952]
|
|
583
|
+
- Updated dependencies [341bfb5]
|
|
584
|
+
- Updated dependencies [2f947e4]
|
|
585
|
+
- Updated dependencies [7d46648]
|
|
586
|
+
- Updated dependencies [9b53d72]
|
|
587
|
+
- Updated dependencies [bb4aa25]
|
|
588
|
+
- Updated dependencies [75f1cdf]
|
|
589
|
+
- Updated dependencies [662bdf9]
|
|
590
|
+
- Updated dependencies [059a052]
|
|
591
|
+
- Updated dependencies [53642d4]
|
|
592
|
+
- Updated dependencies [8aae006]
|
|
593
|
+
- Updated dependencies [c6cfdf1]
|
|
594
|
+
- Updated dependencies [d147a13]
|
|
595
|
+
- Updated dependencies [c6aaed8]
|
|
596
|
+
- Updated dependencies [263f885]
|
|
597
|
+
- Updated dependencies [dc334da]
|
|
598
|
+
- @object-ui/components@17.0.0
|
|
599
|
+
- @object-ui/i18n@17.0.0
|
|
600
|
+
- @object-ui/fields@17.0.0
|
|
601
|
+
- @object-ui/react@17.0.0
|
|
602
|
+
- @object-ui/types@17.0.0
|
|
603
|
+
- @object-ui/core@17.0.0
|
|
604
|
+
- @object-ui/permissions@17.0.0
|
|
605
|
+
|
|
606
|
+
## 16.1.0
|
|
607
|
+
|
|
608
|
+
### Minor Changes
|
|
609
|
+
|
|
610
|
+
- 62b9ab5: feat(data): unify master-detail saves behind `DataSource.batchTransaction`, isolate the non-atomic fallback in the adapter (#2679)
|
|
611
|
+
|
|
612
|
+
Master-detail saves (`MasterDetailForm`, `LineItemsPanel`) now always persist
|
|
613
|
+
through `dataSource.batchTransaction(operations)` — one ordered cross-object
|
|
614
|
+
operation list, with `{ $ref: <op index> }` linking a child to a parent created
|
|
615
|
+
in the same batch. The form no longer contains any client-side orchestration or
|
|
616
|
+
best-effort compensation-delete; that atomicity anti-pattern is gone from the UI
|
|
617
|
+
layer (framework #1604 / framework ADR-0034 item 4).
|
|
618
|
+
|
|
619
|
+
- **`@object-ui/types`** — `batchTransaction?` is now a first-class (optional)
|
|
620
|
+
method on the `DataSource` contract, typed via `BatchTransactionOperation` /
|
|
621
|
+
`BatchRef`. Replaces the previous `(dataSource as any).batchTransaction`
|
|
622
|
+
method-sniffing.
|
|
623
|
+
- **`@object-ui/core`** — new `emulateBatchTransaction(dataSource, operations)`
|
|
624
|
+
(sequential writes, `$ref` resolution, best-effort reverse-order compensation)
|
|
625
|
+
and `runBatchTransaction(dataSource, operations)` (prefers the adapter's method,
|
|
626
|
+
emulates otherwise). `ApiDataSource` / `ValueDataSource` implement
|
|
627
|
+
`batchTransaction` via the emulation.
|
|
628
|
+
- **`@object-ui/data-objectstack`** — `ObjectStackAdapter.batchTransaction` uses
|
|
629
|
+
the server's atomic `POST /api/v1/batch`, prefers the typed
|
|
630
|
+
`client.data.batchTransaction` SDK method when the installed client exposes it,
|
|
631
|
+
and degrades to the client-side emulation ONLY when the endpoint is missing
|
|
632
|
+
(404/405) or the runtime can't do transactions (501). Real errors (400/401/403/
|
|
633
|
+
409/500) still surface. This is the isolated, tested home of the non-atomic
|
|
634
|
+
fallback.
|
|
635
|
+
- **`@object-ui/plugin-form`** — removed `applyDetail` / `createMany` /
|
|
636
|
+
`ApplyDetailResult` from `masterDetailTx.ts`; `MasterDetailForm` and
|
|
637
|
+
`LineItemsPanel` build ops and call `runBatchTransaction`. `LineItemsPanel`
|
|
638
|
+
saves are now atomic on a capable backend, with the rollup folded into the same
|
|
639
|
+
batch.
|
|
640
|
+
|
|
641
|
+
No behavior change on a current ObjectStack backend (it has `/api/v1/batch`);
|
|
642
|
+
older/limited backends keep a working — now clearly non-atomic — save path.
|
|
643
|
+
|
|
644
|
+
### Patch Changes
|
|
645
|
+
|
|
646
|
+
- 7cf4051: chore(deps): align every `@objectstack/*` dependency to `^16.0.0-rc.0`
|
|
647
|
+
|
|
648
|
+
Bumps `@objectstack/spec` / `client` / `formula` / `lint` from `^15.1.1` to the
|
|
649
|
+
`16.0.0-rc.0` pre-release across the workspace (root + `apps/console` +
|
|
650
|
+
`apps/site` + all consuming packages). ObjectUI's own packages are already on
|
|
651
|
+
major 16, so this closes the 15↔16 skew between ObjectUI and the `@objectstack`
|
|
652
|
+
contract libraries (which publish in lockstep with `spec`).
|
|
653
|
+
|
|
654
|
+
This is a dependency alignment, not a behavioral migration: the full workspace
|
|
655
|
+
build (43/43) and the `@objectstack`-consuming package test suites
|
|
656
|
+
(`core` / `app-shell` / `data-objectstack` / `plugin-form` / `types`) are green
|
|
657
|
+
against `16.0.0-rc.0` with no source changes required.
|
|
658
|
+
|
|
659
|
+
Practical effect: `@objectstack/client@16.0.0-rc.0` now ships
|
|
660
|
+
`data.batchTransaction` (framework #3271), so `ObjectStackAdapter`'s feature
|
|
661
|
+
detect (`typeof client.data.batchTransaction === 'function'`) routes
|
|
662
|
+
master-detail cross-object saves through the typed SDK method instead of the
|
|
663
|
+
raw `fetch('/api/v1/batch')` fallback — realizing the "verify SDK path" half of
|
|
664
|
+
#2694. The raw-fetch branch stays as a defensive fallback (removal tracked in
|
|
665
|
+
#2694).
|
|
666
|
+
|
|
667
|
+
- 0a3710b: **Finish the `managedBy` / `userActions` de-dup — one parser for the override shape (completes objectui#2712, framework#3343).** #2712 consolidated the bucket _union_ + affordance _set_ mirrors but left four surfaces still parsing the `userActions.{create,edit,delete}` override shape by hand. They now all route through the shared `@object-ui/core` policy, so no package re-implements the boolean / #2614-object-form parse locally.
|
|
668
|
+
|
|
669
|
+
- **`@object-ui/core`** promotes the internal `normalizeOverride` to the exported **`normalizeUserAction(v, base)`** (the one parser) and adds **`userActionPredicates(v)`** for per-record CEL predicate extraction.
|
|
670
|
+
- **`app-shell/utils/managedByEmptyState.ts`** — the writable-`system` create check and its local `EmptyStateUserActions` interface are replaced by `resolveCrudAffordances({ managedBy, userActions }).create`.
|
|
671
|
+
- **`plugin-grid/rowCrudAffordances.ts`** — the local `isOptedOut` / `predicatesOf` helpers (and duplicated `RowCrudUserAction` / `RowCrudPredicates` types) fold into `normalizeUserAction`; the historical type names stay re-exported for compat.
|
|
672
|
+
- **`plugin-detail/RelatedList.tsx`** — its inline `predicatesOf` fold into `userActionPredicates`.
|
|
673
|
+
- **`plugin-form/ObjectForm.tsx`** — the hand-rolled `managedBy !== 'platform'` blanket lock + `userActions` unlock is replaced by the resolved affordance for the current mode (`edit` / `create`), the **same** `resolveCrudAffordances` contract the detail (`isObjectInlineEditable`) and grid surfaces use.
|
|
674
|
+
|
|
675
|
+
Behavior-preserving for `platform` / `system` / `append-only` / `better-auth`, with one deliberate alignment: an admin-editable **`config`**-bucket object (e.g. `sys_webhook`, `sys_permission_set`) is now editable in `ObjectForm` — it was previously over-locked as "non-`platform`", while detail/grid already treated it as editable (`config` resolves `edit: true`). New unit coverage for the shared parser and the config / create-mode form gate; all existing affordance/edit-gate tests stay green.
|
|
676
|
+
|
|
677
|
+
- Updated dependencies [0318118]
|
|
678
|
+
- Updated dependencies [1c8935a]
|
|
679
|
+
- Updated dependencies [af1b0db]
|
|
680
|
+
- Updated dependencies [8b8b744]
|
|
681
|
+
- Updated dependencies [7cf4051]
|
|
682
|
+
- Updated dependencies [803558e]
|
|
683
|
+
- Updated dependencies [aefcf39]
|
|
684
|
+
- Updated dependencies [2e7d7f0]
|
|
685
|
+
- Updated dependencies [ef14f69]
|
|
686
|
+
- Updated dependencies [94d4876]
|
|
687
|
+
- Updated dependencies [1100a8b]
|
|
688
|
+
- Updated dependencies [7abe4cd]
|
|
689
|
+
- Updated dependencies [69fa5d1]
|
|
690
|
+
- Updated dependencies [549c67d]
|
|
691
|
+
- Updated dependencies [ebe6494]
|
|
692
|
+
- Updated dependencies [2b17339]
|
|
693
|
+
- Updated dependencies [31b77d4]
|
|
694
|
+
- Updated dependencies [6d4fbe6]
|
|
695
|
+
- Updated dependencies [0a3710b]
|
|
696
|
+
- Updated dependencies [f80aaf2]
|
|
697
|
+
- Updated dependencies [62b9ab5]
|
|
698
|
+
- Updated dependencies [14cb729]
|
|
699
|
+
- Updated dependencies [1629313]
|
|
700
|
+
- Updated dependencies [29c6040]
|
|
701
|
+
- Updated dependencies [faebac3]
|
|
702
|
+
- Updated dependencies [2331ac9]
|
|
703
|
+
- Updated dependencies [199fa83]
|
|
704
|
+
- Updated dependencies [eee4ded]
|
|
705
|
+
- Updated dependencies [3b2e4d9]
|
|
706
|
+
- @object-ui/fields@16.1.0
|
|
707
|
+
- @object-ui/i18n@16.1.0
|
|
708
|
+
- @object-ui/core@16.1.0
|
|
709
|
+
- @object-ui/types@16.1.0
|
|
710
|
+
- @object-ui/react@16.1.0
|
|
711
|
+
- @object-ui/components@16.1.0
|
|
712
|
+
- @object-ui/permissions@16.1.0
|
|
713
|
+
|
|
3
714
|
## 16.0.0
|
|
4
715
|
|
|
5
716
|
### Minor Changes
|
|
@@ -793,7 +1504,7 @@ parentData, details)` — emits a parent `update` op (index 0) then diffs each
|
|
|
793
1504
|
`type:'tabbed'` form view now renders tabbed in the modal too — not just on the
|
|
794
1505
|
full-page route (#1762). Non-breaking; `FormView.type` enum unchanged.
|
|
795
1506
|
|
|
796
|
-
Refs objectstack-ai/
|
|
1507
|
+
Refs objectstack-ai/objectstack#1890, ADR-0050
|
|
797
1508
|
|
|
798
1509
|
- 650bd1f: fix(forms/dashboard/related-list): four business-facing rendering fixes found while QA-ing a showcase workspace
|
|
799
1510
|
|