add dual-world xrpc error helpers and toLex migration escape hatch

Phase 3 foundations (task 1): src/lib/xrpc-error.ts matches both the old
@atproto/api XRPCError and lex-client XrpcError/XrpcResponseError during
the migration; errors.ts token-invalid matching goes through it. toLex<T>()
added to #/types/bsky as a marked interim cast for mixed-world boundaries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-07-16 16:50:41 +03:00
parent bf8b6bc0c7
commit e6f73fc110
4 changed files with 199 additions and 3 deletions
+44
View File
@@ -0,0 +1,44 @@
import {XRPCError} from '@atproto/api'
import {XrpcError, XrpcResponseError} from '@atproto/lex-client'
/**
* True for an XRPC error from either the old bridge agent (`@atproto/api`
* `XRPCError`) or a lex `Client` (`@atproto/lex-client` `XrpcError`, the
* abstract base of `XrpcResponseError`/`XrpcInvalidResponseError`/
* `XrpcInternalError`). During the migration both worlds can throw, so matchers
* must accept both.
*/
export function isXrpcError(e: unknown): e is XRPCError | XrpcError {
return e instanceof XRPCError || e instanceof XrpcError
}
/**
* HTTP status, or undefined if not an XRPC error / no response. Only lex
* `XrpcResponseError` (a server response) carries a status; the internal/fetch
* lex errors do not.
*/
export function getErrorStatus(e: unknown): number | undefined {
if (e instanceof XRPCError) return e.status
if (e instanceof XrpcResponseError) return e.status
return undefined
}
/** The lexicon error code (`err.error`), from either world. */
export function getErrorName(e: unknown): string | undefined {
if (isXrpcError(e)) return (e as {error?: string}).error
return undefined
}
/**
* Read a response header off an XRPC error, normalizing the shape change:
* old XRPCError.headers is a plain record; lex XrpcResponseError.headers is a
* WHATWG Headers object.
*/
export function getErrorHeader(e: unknown, name: string): string | undefined {
if (e instanceof XrpcResponseError) return e.headers.get(name) ?? undefined
if (e instanceof XRPCError) {
const h = (e as {headers?: Record<string, string>}).headers
return h?.[name.toLowerCase()]
}
return undefined
}