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
refactor(types,rest,services,plugin-sharing): one shared writer for the response envelope, and error.code is enforced at compile time (#3973) (#4229)
`BaseResponseSchema` declares one envelope for every REST body the platform
emits. It declared it once; the code that *wrote* it was copied per route
module. After #3843 and #3983 converted the last drifting one, seven modules
each carried their own two-line `sendOk` / `sendError` pair — the envelope's
shape in fourteen places rather than one.
`check:route-envelope` proved those copies agreed, which is why this is a
cleanup rather than a bug fix. But a guard proves agreement; it does not create
it. An eighth module starts by copying the pair again — not hypothetically:
`share-link-routes.ts` was found already drifting by the repo-wide scan, and
its drift had broken `client.shareLinks.create()` / `.list()` (#3983).
Placement was #3973's open question, not design. `packages/spec` is
schemas-only (Prime Directive #2), and the callers span `rest`, four
`services/*` and one `plugins/*`. `@objectstack/types` depends on nothing but
`@objectstack/spec`, so every caller can reach it, and it is already where the
repo puts a helper the HTTP boundaries share — `looksLikeInternalErrorLeak`
(#3867) sits one file over and made the same argument first. The builders take
a structural `{ status(n), json(body) }`, so the package imports no HTTP
contract at all.
`error.code` is now checked by the compiler. All seven copies typed it
`string`, so an invented code was caught only at runtime, by a conformance
suite parsing a driven body — i.e. only on routes some test happened to drive.
The shared `sendError` types it as ADR-0112's closed `ErrorCode` union, so an
unregistered code fails to compile at every call site at once. This cost no
call-site churn: every code the seven modules emit was already registered.
Nothing changes on the wire. The seven pairs were identical modulo the optional
`status` and `extra` parameters this one unions, and each module's driven
conformance suite still parses its real bodies against the real spec schemas.
One internal call site was rewritten: `package-routes` passed `details`
positionally and now passes `{ details }`, producing the same `error.details`.
The guard got stronger. A module routing everything through the shared pair
builds none itself, so the seven now declare `0 / 0 / 0` where they declared
`2 / 1 / 1`, and the pair is pinned separately at `2 / 1 / 1` so the invariant
is total for the surface rather than per-module. What the count asserts is no
longer "your two builders are the enveloped ones" but "you have no builders".
Two out-of-scope findings filed rather than folded in (Prime Directive #10):
#4224 (settings passes four keys `ApiErrorSchema` does not declare — the reason
`extra` stays `Record<string, unknown>`) and #4225 (three admin routes answer
503 naming the wrong service — carried out of #3973 so closing it does not bury
the note).
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
0 commit comments