You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The atomic cross-object batch endpoint (POST {basePath}/batch, #1604 / ADR-0034
item 4) and its typed SDK surface (client.data.batchTransaction, #3271) shipped,
but discovery never told a client whether a backend supports it. Consumers had to
probe — fire a /batch, read 404/405 (no route) or 501 (no runtime transaction),
then fall back to non-atomic client-side simulation. That is "find out by
calling", not capability negotiation, and it blocks hard-deleting ObjectUI's
non-atomic fallback (objectui#2679).
Add a required `transactionalBatch: boolean` to WellKnownCapabilitiesSchema and
fill it honestly in every discovery producer (declared === enforced), so it is
never a declared-but-unpopulated bit:
- metadata-protocol (getDiscovery): true iff the runtime engine can honour a
transaction (typeof engine.transaction === 'function'). engine.transaction()
degrades to a non-atomic passthrough / 501 without one.
- rest-server (/discovery): ANDs that with api.enableBatch — the gate that mounts
the /batch route — so batch-disabled servers report false even on a tx-capable
engine (never advertise a route that 404s).
- plugin-hono-server (standalone discovery): false — this minimal surface mounts
CRUD only, not /batch. Under-reporting is the safe direction (client keeps its
correct-but-slower fallback).
- client: already normalizes hierarchical capabilities → flat booleans, so
client.capabilities.transactionalBatch is exposed and now typed.
Tests assert the bit across all producers (spec schema required-field, protocol
engine-tx true/false, rest enableBatch AND-ing + always-populated, hono false,
client hierarchical→flat). Regenerated content/docs/references/api/discovery.mdx.
Additive and behavior-preserving; only the discovery payload gains a field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
**Broadcast a `transactionalBatch` capability bit in discovery so clients negotiate the atomic cross-object batch declaratively, instead of runtime-probing 404/405/501 (#3298).**
10
+
11
+
The atomic cross-object batch endpoint (`POST {basePath}/batch`, #1604 / ADR-0034 item 4) and its typed SDK surface (`client.data.batchTransaction`, #3271) already shipped, but discovery never told a client whether a backend actually supports it. Consumers (notably ObjectUI's `ObjectStackAdapter`) had to *probe*: fire a `/batch`, read `404`/`405` (no route) or `501` (no runtime transaction), and only then fall back to non-atomic client-side simulation. That is "find out by calling", not capability negotiation — it cannot be decided at connect time and cannot serve as the "minimum backend supports `/batch`" gate that blocks hard-deleting the non-atomic fallback downstream.
12
+
13
+
`WellKnownCapabilitiesSchema` gains a required `transactionalBatch: boolean`, and **every** discovery producer fills it honestly (`declared === enforced`), so it never becomes a declared-but-unpopulated bit:
14
+
15
+
-**`@objectstack/metadata-protocol`** (`getDiscovery`) — reports whether the runtime engine can honour a transaction (`typeof engine.transaction === 'function'`). The `/batch` handler runs its ops inside `engine.transaction()`, which degrades to a non-atomic passthrough (or 501) without one.
16
+
-**`@objectstack/rest`** (`/discovery`) — ANDs the engine signal with whether it actually mounts the route (`api.enableBatch`), so a server with batch disabled reports `false` even on a transaction-capable engine (never advertise an endpoint that would 404).
17
+
-**`@objectstack/plugin-hono-server`** (standalone discovery) — reports `false`: this minimal surface registers CRUD only and does not mount `/batch` (that ships with `@objectstack/rest`). Under-reporting is the safe direction — a client keeps its correct-but-slower fallback rather than losing atomicity.
18
+
-**`@objectstack/client`** — already normalizes hierarchical `capabilities` to flat booleans, so `client.capabilities.transactionalBatch` is exposed (and now typed) for declarative consumers.
19
+
20
+
The bit follows the existing capability semantics: `true` ⟺ the `/batch` route is mounted **and** the runtime can honour a transaction — the exact condition under which the endpoint returns `200` rather than `404`/`405`/`501`. Additive and behavior-preserving; only the discovery payload gains a field.
|**transactionalBatch**|`boolean`| ✅ | Whether the backend exposes the atomic cross-object batch endpoint (POST `{basePath}`/batch, #1604/ADR-0034): all ops commit or roll back together in one transaction. Lets clients skip non-atomic client-side simulation instead of runtime-probing 404/405/501. True ⟺ the /batch route is mounted AND the runtime can honour a transaction. |
0 commit comments