Skip to content

feat: typed error responses with $error<T> and status-keyed response maps#2

Draft
Copilot wants to merge 2 commits into
mainfrom
copilot/add-typed-error-responses
Draft

feat: typed error responses with $error<T> and status-keyed response maps#2
Copilot wants to merge 2 commits into
mainfrom
copilot/add-typed-error-responses

Conversation

Copy link
Copy Markdown

Copilot AI commented May 23, 2026

Adds declared error responses as part of the route contract. Declared HTTP errors resolve as typed tuples on the client instead of rejecting, while unexpected/undeclared failures still reject.

Changes

  • $error<T>() marker — new compile-time marker analogous to $type<T>(), distinguishes error branches in a response map
  • Status-keyed response mapsresponse field now accepts { [status]: $type<T>() | $error<T>() } in addition to the existing single-marker form
  • ctx.error(status, body) — server handler helper typed to only accept statuses declared with $error<T>(); returns a Response with the given status
  • Client tuple resolution — when a route declares $error<T> entries, the client resolves all declared statuses as [error, result, status] discriminated tuples
  • Type inferenceInferRouteResponse produces the tuple union; InferRouteHandlerResult extracts success types; InferResponseMapErrors types the error helper

Example

const getUser = http.get('users/:id', {
  response: {
    200: $type<User>(),
    401: $error<AuthError>(),
    404: $error<NotFoundError>(),
  },
})

// Server
createRouter().use({ getUser }, {
  async getUser(ctx) {
    if (!authorized) return ctx.error(401, { code: 'UNAUTHORIZED', message: '...' })
    return { id: ctx.path.id, name: 'Ada' }
  },
})

// Client — resolved type is [null, User, 200] | [AuthError, null, 401] | [NotFoundError, null, 404]
const [error, result, status] = await client.getUser({ path: { id: '123' } })

Backward compatibility

Existing response: $type<T>() and response: ndjson.$type<T>() routes are unchanged — no tuple wrapping, no breaking type changes. The response map form is purely additive.

Copilot AI and others added 2 commits May 23, 2026 05:14
Agent-Logs-Url: https://github.com/alloc/rouzer/sessions/ad2874c8-97ca-4ce2-8013-e7bf328459c6

Co-authored-by: aleclarson <1925840+aleclarson@users.noreply.github.com>
- Add $error<T>() marker function for declaring error response types
- Support status-keyed response maps in route schemas
- Add ctx.error(status, body) to server handler context
- Client resolves declared errors as [error, null, status] tuples
- Undeclared statuses still reject the promise
- Add runtime and type-level tests

Agent-Logs-Url: https://github.com/alloc/rouzer/sessions/ad2874c8-97ca-4ce2-8013-e7bf328459c6

Co-authored-by: aleclarson <1925840+aleclarson@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants