@object-ui/data-objectstack 16.0.0 → 16.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 +199 -0
- package/README.md +63 -0
- package/dist/index.cjs +1022 -872
- package/dist/index.d.cts +79 -14
- package/dist/index.d.ts +79 -14
- package/dist/index.js +1021 -872
- package/package.json +4 -4
- package/src/exportDownload.test.ts +9 -3
- package/src/index.ts +259 -105
- package/src/integration.ts +8 -0
- package/src/listViews.test.ts +97 -0
- package/src/metadata-client.test.ts +8 -0
- package/src/metadata-client.ts +9 -0
- package/src/onMutation.test.ts +267 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,204 @@
|
|
|
1
1
|
# @object-ui/data-objectstack
|
|
2
2
|
|
|
3
|
+
## 16.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8c1e415: feat(data-objectstack): gate the non-atomic batch fallback on the discovery `transactionalBatch` capability (#2693)
|
|
8
|
+
|
|
9
|
+
`ObjectStackAdapter.batchTransaction` now negotiates atomic cross-object batch
|
|
10
|
+
**declaratively** instead of only probing at runtime. At `connect()` the adapter
|
|
11
|
+
reads `capabilities.transactionalBatch` from `GET /api/v1/discovery`
|
|
12
|
+
(framework #3298 — `declared === enforced`; the server advertises `true` only
|
|
13
|
+
when the `/batch` route is mounted _and_ the runtime engine can honour a
|
|
14
|
+
transaction):
|
|
15
|
+
|
|
16
|
+
- **Declared `true`** — the adapter TRUSTS server atomicity: it calls `/batch`
|
|
17
|
+
and surfaces any failure (including `404`/`405`/`501`) as a real error. No
|
|
18
|
+
runtime probe, no non-atomic client-side compensation.
|
|
19
|
+
- **Declared `false`, or absent** (backend predates #3298) — the legacy path is
|
|
20
|
+
unchanged: probe `/batch` and, on `404`/`405`/`501`, fall back to the
|
|
21
|
+
non-atomic `emulateBatchTransaction`. Keeping this avoids regressing older
|
|
22
|
+
backends from "saves, less safe" to "no save path" (#2679 compat constraint).
|
|
23
|
+
|
|
24
|
+
Both the hierarchical wire shape (`{ transactionalBatch: { enabled: true } }`)
|
|
25
|
+
and the flat form the client SDK normalizes to (`{ transactionalBatch: true }`)
|
|
26
|
+
are accepted. `@object-ui/core`'s generic `emulateBatchTransaction` /
|
|
27
|
+
`runBatchTransaction` are untouched and remain the fallback for adapters with no
|
|
28
|
+
server-side transaction (`ValueDataSource`, `MockDataSource`, …).
|
|
29
|
+
|
|
30
|
+
Docs: the adapter README and the data-source guide now document the capability
|
|
31
|
+
table and the minimum-backend note — atomic cross-object saves are guaranteed
|
|
32
|
+
only against backends advertising the capability (framework #3298 / #1604).
|
|
33
|
+
|
|
34
|
+
Picks up #2679 acceptance item 4; unblocked by framework#3298 (merged).
|
|
35
|
+
|
|
36
|
+
- 62b9ab5: feat(data): unify master-detail saves behind `DataSource.batchTransaction`, isolate the non-atomic fallback in the adapter (#2679)
|
|
37
|
+
|
|
38
|
+
Master-detail saves (`MasterDetailForm`, `LineItemsPanel`) now always persist
|
|
39
|
+
through `dataSource.batchTransaction(operations)` — one ordered cross-object
|
|
40
|
+
operation list, with `{ $ref: <op index> }` linking a child to a parent created
|
|
41
|
+
in the same batch. The form no longer contains any client-side orchestration or
|
|
42
|
+
best-effort compensation-delete; that atomicity anti-pattern is gone from the UI
|
|
43
|
+
layer (framework #1604 / framework ADR-0034 item 4).
|
|
44
|
+
|
|
45
|
+
- **`@object-ui/types`** — `batchTransaction?` is now a first-class (optional)
|
|
46
|
+
method on the `DataSource` contract, typed via `BatchTransactionOperation` /
|
|
47
|
+
`BatchRef`. Replaces the previous `(dataSource as any).batchTransaction`
|
|
48
|
+
method-sniffing.
|
|
49
|
+
- **`@object-ui/core`** — new `emulateBatchTransaction(dataSource, operations)`
|
|
50
|
+
(sequential writes, `$ref` resolution, best-effort reverse-order compensation)
|
|
51
|
+
and `runBatchTransaction(dataSource, operations)` (prefers the adapter's method,
|
|
52
|
+
emulates otherwise). `ApiDataSource` / `ValueDataSource` implement
|
|
53
|
+
`batchTransaction` via the emulation.
|
|
54
|
+
- **`@object-ui/data-objectstack`** — `ObjectStackAdapter.batchTransaction` uses
|
|
55
|
+
the server's atomic `POST /api/v1/batch`, prefers the typed
|
|
56
|
+
`client.data.batchTransaction` SDK method when the installed client exposes it,
|
|
57
|
+
and degrades to the client-side emulation ONLY when the endpoint is missing
|
|
58
|
+
(404/405) or the runtime can't do transactions (501). Real errors (400/401/403/
|
|
59
|
+
409/500) still surface. This is the isolated, tested home of the non-atomic
|
|
60
|
+
fallback.
|
|
61
|
+
- **`@object-ui/plugin-form`** — removed `applyDetail` / `createMany` /
|
|
62
|
+
`ApplyDetailResult` from `masterDetailTx.ts`; `MasterDetailForm` and
|
|
63
|
+
`LineItemsPanel` build ops and call `runBatchTransaction`. `LineItemsPanel`
|
|
64
|
+
saves are now atomic on a capable backend, with the rollup folded into the same
|
|
65
|
+
batch.
|
|
66
|
+
|
|
67
|
+
No behavior change on a current ObjectStack backend (it has `/api/v1/batch`);
|
|
68
|
+
older/limited backends keep a working — now clearly non-atomic — save path.
|
|
69
|
+
|
|
70
|
+
### Patch Changes
|
|
71
|
+
|
|
72
|
+
- 8b8b744: chore(deps): align `@objectstack/formula` / `lint` / `client` to `^15.1.1`
|
|
73
|
+
|
|
74
|
+
These three were still pinned to `^14.6.0` while `@objectstack/spec` was already
|
|
75
|
+
`^15.1.1` — a version skew from the v15 upgrade (formula/lint/client publish in
|
|
76
|
+
lockstep with spec, and their own 15.0.0 entries are pure dependency bumps, so
|
|
77
|
+
this is alignment, not a behavioral migration).
|
|
78
|
+
|
|
79
|
+
Practical effect: the client-side field-rule evaluation
|
|
80
|
+
(`visibleWhen`/`readonlyWhen`/`requiredWhen` via `fieldRules.ts`, which delegates
|
|
81
|
+
to `@objectstack/formula`'s `ExpressionEngine`) now tracks the 15.x engine — and
|
|
82
|
+
will pick up the framework's `dateField == today()` equality fix
|
|
83
|
+
(objectstack-ai/framework#3205) automatically at the next 15.x release via the
|
|
84
|
+
caret range. Renderer/action `visible`/`disabled` predicates are unaffected (they
|
|
85
|
+
use the home-grown JS evaluator — tracked separately in #2661).
|
|
86
|
+
|
|
87
|
+
- 7cf4051: chore(deps): align every `@objectstack/*` dependency to `^16.0.0-rc.0`
|
|
88
|
+
|
|
89
|
+
Bumps `@objectstack/spec` / `client` / `formula` / `lint` from `^15.1.1` to the
|
|
90
|
+
`16.0.0-rc.0` pre-release across the workspace (root + `apps/console` +
|
|
91
|
+
`apps/site` + all consuming packages). ObjectUI's own packages are already on
|
|
92
|
+
major 16, so this closes the 15↔16 skew between ObjectUI and the `@objectstack`
|
|
93
|
+
contract libraries (which publish in lockstep with `spec`).
|
|
94
|
+
|
|
95
|
+
This is a dependency alignment, not a behavioral migration: the full workspace
|
|
96
|
+
build (43/43) and the `@objectstack`-consuming package test suites
|
|
97
|
+
(`core` / `app-shell` / `data-objectstack` / `plugin-form` / `types`) are green
|
|
98
|
+
against `16.0.0-rc.0` with no source changes required.
|
|
99
|
+
|
|
100
|
+
Practical effect: `@objectstack/client@16.0.0-rc.0` now ships
|
|
101
|
+
`data.batchTransaction` (framework #3271), so `ObjectStackAdapter`'s feature
|
|
102
|
+
detect (`typeof client.data.batchTransaction === 'function'`) routes
|
|
103
|
+
master-detail cross-object saves through the typed SDK method instead of the
|
|
104
|
+
raw `fetch('/api/v1/batch')` fallback — realizing the "verify SDK path" half of
|
|
105
|
+
#2694. The raw-fetch branch stays as a defensive fallback (removal tracked in
|
|
106
|
+
#2694).
|
|
107
|
+
|
|
108
|
+
- 0ea5036: refactor(data-objectstack): route `batchTransaction` through the client SDK only, drop the raw-fetch branch
|
|
109
|
+
|
|
110
|
+
`@objectstack/client@^16` (framework #3271, the current ObjectUI dependency
|
|
111
|
+
floor) ships `data.batchTransaction`, so `ObjectStackAdapter.batchTransaction`
|
|
112
|
+
now calls the typed SDK method directly. The transitional hand-rolled
|
|
113
|
+
`fetch('/api/v1/batch')` branch — a feature-detect shim kept while the SDK
|
|
114
|
+
method was unreleased — is removed (#2694). Per AGENTS.md §7, adapter data
|
|
115
|
+
always flows through `@objectstack/client`, never a raw `fetch`.
|
|
116
|
+
|
|
117
|
+
No behavior change: the SDK still drives the server's atomic `POST /api/v1/batch`,
|
|
118
|
+
one `MutationEvent` is emitted per committed op (no double-fire), and the adapter
|
|
119
|
+
still degrades to the non-atomic `emulateBatchTransaction` when this backend lacks
|
|
120
|
+
the endpoint (404/405) or its runtime can't do transactions (501). Every other
|
|
121
|
+
status still surfaces to the caller.
|
|
122
|
+
|
|
123
|
+
- 549c67d: chore(lint): clear the mechanical baseline lint errors so these packages' lint gates protect them again
|
|
124
|
+
|
|
125
|
+
Extends the fields/core cleanup from #2709 (objectui#2713). These eight package
|
|
126
|
+
lints were red at baseline on `main`, so their per-package `lint` gate could not
|
|
127
|
+
catch new violations of the same class. Cleared every **error** (no behavior
|
|
128
|
+
change; warnings are out of scope):
|
|
129
|
+
|
|
130
|
+
- **`no-useless-catch`** (`data-objectstack`) — unwrapped five try/catch blocks
|
|
131
|
+
whose `catch` only re-threw; errors still propagate identically.
|
|
132
|
+
- **`preserve-caught-error`** (`cli`, `data-objectstack`, `react`) — the caught
|
|
133
|
+
error's message is inlined into the thrown `Error`; a scoped disable with a
|
|
134
|
+
justifying comment carries each one, because these packages target ES2020
|
|
135
|
+
whose lib types the 1-arg `Error` constructor only (so `{ cause }` won't
|
|
136
|
+
compile) — same reasoning as the core case in #2709.
|
|
137
|
+
- **`prefer-const`** (`plugin-calendar`, `plugin-map`) — `let`→`const` for
|
|
138
|
+
never-reassigned bindings.
|
|
139
|
+
- **`no-empty-object-type`** (`plugin-designer`) — empty extend-only interfaces
|
|
140
|
+
→ equivalent `type` aliases.
|
|
141
|
+
- **`no-useless-assignment`** (`react`) — dropped a dead initializer that both
|
|
142
|
+
branches overwrite before it is read.
|
|
143
|
+
- **`no-require-imports`** (`plugin-calendar`, `plugin-timeline` tests) —
|
|
144
|
+
hoisted `vi.mock` factories now use an `async` factory with
|
|
145
|
+
`await import('react')` instead of `require('react')`.
|
|
146
|
+
- **stale `eslint-disable` directive** (`plugin-markdown`) — removed a
|
|
147
|
+
`react/no-danger` disable whose plugin is not loaded in the flat config (an
|
|
148
|
+
unknown-rule reference that ESLint v10 reports as an error); the rationale is
|
|
149
|
+
kept as a plain comment.
|
|
150
|
+
|
|
151
|
+
- 29c6040: fix(app-shell): redo the record-list "Add View" create flow — empty-name 405, invisible drafts, canonical naming
|
|
152
|
+
|
|
153
|
+
Rebuilds the record-list "Add View" / "Save as view" create path so a
|
|
154
|
+
runtime-created view has one canonical identity and is actually verifiable
|
|
155
|
+
before publish (supersedes #2754; fixes #2767).
|
|
156
|
+
|
|
157
|
+
- **Unified identity (P1).** New `viewEnvelope(objectName, spec, { name, label })`
|
|
158
|
+
seam in `runtime-metadata-persistence.ts` emits the canonical ViewItem
|
|
159
|
+
(`{ name: '<object>.<key>', object, viewKind: 'list', label, config }` with
|
|
160
|
+
`config.data = { provider: 'object', object }`), mirroring the Studio
|
|
161
|
+
`anchors.ts:createBuildBody`. The **qualified** name is passed as BOTH the
|
|
162
|
+
`PUT /meta/view/:name` URL segment and `body.name`, so the `sys_metadata`
|
|
163
|
+
row key, the ViewTabBar tab id, and the body identity all agree and the
|
|
164
|
+
draft → read → publish loop resolves. `ObjectView` and `ObjectDataPage` both
|
|
165
|
+
call the single helper — the duplicated envelope block is gone (P6).
|
|
166
|
+
- **Empty-name guards (405).** `MetadataClient.save()` and
|
|
167
|
+
`createRuntimeMetadata()` throw a clear contextual error instead of emitting
|
|
168
|
+
`PUT /meta/view/` (empty `:name`, server 405).
|
|
169
|
+
- **Draft visibility (P2/P3/P4).** `DataSource.listViews(objectName, { previewDrafts })`:
|
|
170
|
+
in draft-preview mode the `ObjectStackAdapter` makes a **single**
|
|
171
|
+
`MetadataClient.withPreviewDrafts(true).list('view')` request and uses the
|
|
172
|
+
server's already-overlaid list (draft wins by name, `_draft` tagged) —
|
|
173
|
+
replacing, not appending, so a draft that edits a published view can't
|
|
174
|
+
double-tab. No hand-rolled `fetch` of metadata routes at the adapter layer.
|
|
175
|
+
After a create in normal mode the console navigates to the new view with
|
|
176
|
+
`?preview=draft`, so the DraftPreviewBar is visible and Publish is one click.
|
|
177
|
+
- **CJK-aware naming (P5).** `CreateViewDialog` gains an editable machine-name
|
|
178
|
+
field, prefilled via `slugify(label)` for Latin labels and required (submit
|
|
179
|
+
disabled) when slugify yields empty for non-Latin labels — no more silent
|
|
180
|
+
random `task_grid_mrsyt56j` names. New `console.objectView.viewName*` keys
|
|
181
|
+
(en/zh).
|
|
182
|
+
|
|
183
|
+
- Updated dependencies [1c8935a]
|
|
184
|
+
- Updated dependencies [8b8b744]
|
|
185
|
+
- Updated dependencies [7cf4051]
|
|
186
|
+
- Updated dependencies [2e7d7f0]
|
|
187
|
+
- Updated dependencies [94d4876]
|
|
188
|
+
- Updated dependencies [2b17339]
|
|
189
|
+
- Updated dependencies [31b77d4]
|
|
190
|
+
- Updated dependencies [6d4fbe6]
|
|
191
|
+
- Updated dependencies [0a3710b]
|
|
192
|
+
- Updated dependencies [62b9ab5]
|
|
193
|
+
- Updated dependencies [1629313]
|
|
194
|
+
- Updated dependencies [29c6040]
|
|
195
|
+
- Updated dependencies [faebac3]
|
|
196
|
+
- Updated dependencies [2331ac9]
|
|
197
|
+
- Updated dependencies [199fa83]
|
|
198
|
+
- Updated dependencies [eee4ded]
|
|
199
|
+
- @object-ui/core@16.1.0
|
|
200
|
+
- @object-ui/types@16.1.0
|
|
201
|
+
|
|
3
202
|
## 16.0.0
|
|
4
203
|
|
|
5
204
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -272,6 +272,68 @@ await dataSource.bulk('users', 'delete', [
|
|
|
272
272
|
- Provides partial success reporting for resilient error handling
|
|
273
273
|
- Atomic operations where supported by the backend
|
|
274
274
|
|
|
275
|
+
## Cross-Object Atomic Batch (`batchTransaction`)
|
|
276
|
+
|
|
277
|
+
`bulk()` above operates on **one** object. To persist a set of **cross-object**
|
|
278
|
+
writes as a single all-or-nothing unit — the master-detail case, where a parent
|
|
279
|
+
and its children must commit or roll back together — use `batchTransaction`:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// Create a parent and a child that references it, atomically.
|
|
283
|
+
// `{ $ref: 0 }` resolves to the id minted by operation 0 (the parent).
|
|
284
|
+
await dataSource.batchTransaction([
|
|
285
|
+
{ object: 'invoice', action: 'create', data: { no: 'INV-1' } },
|
|
286
|
+
{ object: 'invoice_line', action: 'create', data: { invoice: { $ref: 0 }, amount: 10 } },
|
|
287
|
+
]);
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
On a supporting backend this is one `POST /api/v1/batch` that commits or rolls
|
|
291
|
+
back the whole set in a single server transaction — no orphaned parent if a
|
|
292
|
+
child write fails.
|
|
293
|
+
|
|
294
|
+
### Declarative capability negotiation (`transactionalBatch`)
|
|
295
|
+
|
|
296
|
+
Whether the adapter can rely on server atomicity is decided **at connect time**,
|
|
297
|
+
not by firing a batch and reading the failure. `connect()` reads the
|
|
298
|
+
`capabilities.transactionalBatch` flag from `GET /api/v1/discovery`
|
|
299
|
+
([framework #3298](https://github.com/objectstack-ai/framework/issues/3298)),
|
|
300
|
+
which the server sets to `true` only when the `/batch` route is mounted **and**
|
|
301
|
+
the runtime engine can honour a transaction (`declared === enforced`):
|
|
302
|
+
|
|
303
|
+
| Discovery `capabilities.transactionalBatch` | `batchTransaction` behaviour |
|
|
304
|
+
| --- | --- |
|
|
305
|
+
| `true` | **Trusts server atomicity.** Calls `/batch`; any failure — including `404`/`405`/`501` — surfaces as a real error. No non-atomic client-side fallback. |
|
|
306
|
+
| `false` | Backend can't do an atomic batch (route absent, or a runtime without transactions) → falls back to the non-atomic client-side emulation below. |
|
|
307
|
+
| *absent* | Backend predates #3298 and advertises nothing → the legacy runtime probe stays: try `/batch`, and on `404`/`405`/`501` fall back to emulation. |
|
|
308
|
+
|
|
309
|
+
The hierarchical wire shape (`{ transactionalBatch: { enabled: true } }`) and the
|
|
310
|
+
flat form the client SDK normalizes to (`{ transactionalBatch: true }`) are both
|
|
311
|
+
accepted.
|
|
312
|
+
|
|
313
|
+
### Non-atomic fallback
|
|
314
|
+
|
|
315
|
+
When the capability is `false` or absent, the adapter degrades to a client-side
|
|
316
|
+
emulation (`@object-ui/core`'s `emulateBatchTransaction`): the operations run in
|
|
317
|
+
order and, on failure, it best-effort deletes the records it created (children
|
|
318
|
+
before parent) before rethrowing. This is **not** a transaction — a create's
|
|
319
|
+
side effects (hooks, rollups, webhooks) are not undone by a later delete, and a
|
|
320
|
+
mid-batch network drop leaves no chance to compensate. It exists only so a save
|
|
321
|
+
is still possible against a backend that lacks server atomicity; removing it
|
|
322
|
+
would turn "saves, less safe" into "no save path" on older backends
|
|
323
|
+
([objectui #2679](https://github.com/objectstack-ai/objectui/issues/2679)).
|
|
324
|
+
|
|
325
|
+
### Minimum supported backend
|
|
326
|
+
|
|
327
|
+
Atomic cross-object saves are **guaranteed only against ObjectStack backends on
|
|
328
|
+
the 16.x line that advertise `capabilities.transactionalBatch: true`** — the
|
|
329
|
+
endpoint landed in [framework #1604](https://github.com/objectstack-ai/framework/issues/1604)
|
|
330
|
+
and its discovery capability in
|
|
331
|
+
[framework #3298](https://github.com/objectstack-ai/framework/issues/3298).
|
|
332
|
+
ObjectUI does not hard-require it: against an older backend a master-detail save
|
|
333
|
+
still succeeds, but non-atomically via the fallback above. Treat the advertised
|
|
334
|
+
capability as the floor for the atomicity guarantee, not as a connection
|
|
335
|
+
prerequisite.
|
|
336
|
+
|
|
275
337
|
## User-Scoped State Adapter
|
|
276
338
|
|
|
277
339
|
In addition to the main `DataSource` adapter, this package ships
|
|
@@ -348,6 +410,7 @@ new ObjectStackAdapter(config: {
|
|
|
348
410
|
- `update(resource, id, data)` - Update an existing record
|
|
349
411
|
- `delete(resource, id)` - Delete a record
|
|
350
412
|
- `bulk(resource, operation, data)` - Batch operations (create/update/delete)
|
|
413
|
+
- `batchTransaction(operations)` - Cross-object atomic batch (master-detail); atomic when the backend advertises `transactionalBatch`, else non-atomic client-side fallback
|
|
351
414
|
- `getObjectSchema(objectName)` - Get schema metadata (cached)
|
|
352
415
|
- `getCacheStats()` - Get cache statistics
|
|
353
416
|
- `invalidateCache(key?)` - Invalidate cache entries
|