|
| 1 | +--- |
| 2 | +"@objectstack/rest": major |
| 3 | +"@objectstack/runtime": major |
| 4 | +"@objectstack/client-react": major |
| 5 | +--- |
| 6 | + |
| 7 | +**BREAKING** — `GET /meta/:type/:name` now answers exactly one body shape: the |
| 8 | +`GetMetaItemResponseSchema` envelope `{ type, name, item, … }` that |
| 9 | +`packages/spec` has always declared for it. On the default configuration this |
| 10 | +endpoint used to answer the **bare metadata document** instead (#5563). |
| 11 | + |
| 12 | +### What changed, and why it is breaking |
| 13 | + |
| 14 | +The route had two mutually exclusive branches with different response |
| 15 | +structures. The cached branch — reached whenever `metadata.enableCache` is on, |
| 16 | +which is the **default** (`enableCache: z.boolean().default(true)`) — served |
| 17 | +`getMetaItemCached`'s `result.data`, and that value has the envelope already |
| 18 | +stripped. The uncached branch served `getMetaItem`'s envelope. So the one shape |
| 19 | +the spec declared was the one a default deployment could not obtain, and the |
| 20 | +envelope surfaced only when the cache was off or when the read structurally |
| 21 | +bypassed it (`app`, `doc`, `book`, `?state=draft`, `?preview=draft`, |
| 22 | +`?package=`). Consumers had no correct static type — they sniffed at runtime or |
| 23 | +reached for `as any` (#5545 was blocked on exactly this). |
| 24 | + |
| 25 | +The dispatcher's `/meta` domain had the same split one layer down: the protocol |
| 26 | +resolver answered the envelope while the ObjectQL-registry and MetadataService |
| 27 | +fallbacks answered bare documents. Both fallbacks now wrap what they found, |
| 28 | +taking `type`/`name` from the request. |
| 29 | + |
| 30 | +### Migration |
| 31 | + |
| 32 | +`GET /api/v1/meta/object/customer`, default configuration: |
| 33 | + |
| 34 | +```jsonc |
| 35 | +// before — the bare document |
| 36 | +{ "name": "customer", "label": "Customer", "fields": { /* … */ } } |
| 37 | + |
| 38 | +// after — the declared envelope; the document is verbatim under `item` |
| 39 | +{ |
| 40 | + "type": "object", |
| 41 | + "name": "customer", |
| 42 | + "item": { "name": "customer", "label": "Customer", "fields": { /* … */ } } |
| 43 | +} |
| 44 | +``` |
| 45 | + |
| 46 | +- **Reading the body directly** (`fetch`, `client.meta.getItem`, |
| 47 | + `client.meta.getCached().data`): read the document at `.item`. Nothing inside |
| 48 | + it changed. `type` is the canonical singular metadata type name, so |
| 49 | + `/meta/objects/customer` and `/meta/object/customer` answer the same `type`. |
| 50 | +- **`useObject` / `useFields` (`@objectstack/client-react`)**: `useObject().data` |
| 51 | + is now the envelope — `data.item.label`, `data.item.fields`, where it used to |
| 52 | + be `data.label` / `data.fields`. `useFields()` is unchanged (it already |
| 53 | + returns the flattened field list) and is the shorter path when fields are all |
| 54 | + you need. |
| 55 | +- **`isMetaEnvelope`, exported from `@objectstack/rest`, is REMOVED.** It |
| 56 | + existed only to tell the two shapes apart. There is one shape now, so the |
| 57 | + replacement for `isMetaEnvelope(r) ? r.item : r` is `r.item`. |
| 58 | +- **Not converged, deliberately**: `?layers=true` still answers the layered |
| 59 | + diagnostic projection `{ type, name, code, overlay, overlayScope, effective, |
| 60 | + validation }`. Collapsing three layers into one `item` would delete the |
| 61 | + diagnostic. Unaffected unless you pass that flag. |
0 commit comments