@object-ui/data-objectstack 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/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/objectstack/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/objectstack/issues/1604)
330
+ and its discovery capability in
331
+ [framework #3298](https://github.com/objectstack-ai/objectstack/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