Components
Branch on domain errors only — exhaustively, or it fails to compile.
Every way an operation can fail is a typed, wire-safe value in its own closed union — and responsibility for each failure is assigned to exactly one place in the component tree.
// the honest base: the complete union
const query = useResultQuery(client.doc.byId, { id })
// DocNotFound | Unauthorized | ServerInternal | Offline |
// NetworkFailure | Timeout | HttpFailure |
// ProtocolViolation | DecodeFailure | Stale
// under the onion: each shell subtracted what it owns
const query = AuthShell.useQuery(client.doc.byId, { id })
// DocNotFound — and a missing case fails to compileFailure ownership, end to end
Branch on domain errors only — exhaustively, or it fails to compile.
Claim transport blips, auth expiry, and defects at the tree position that owns them.
Mutations return who changed; every cached view patches in place. Zero refetches.
Contract digests detect stale clients; the reload is the fix, not a mystery incident.
Why result-rpc
Closed unions
Every failure a call can produce is a typed, wire-safe tag in that operation’s own closed union — domain, transport, protocol, and staleness in one place.
Error boundaries for values
Providers claim failure classes ambiently — pause and resume, or escalate to the real error boundary — and remove those tags from the unions components see.
The identity graph
defineModel gives values identity. Change an avatar and the header, bylines, and member rows patch in place — automatic invalidation and updates, no normalized store.
Coming from tRPC?