/** * Error codes the Ed25519 admin auth flow can surface to the UI. Each maps to * a dedicated screen rather than a generic toast, because the recovery * action differs per code (re-login vs. retry vs. wait). */ export type AuthErrorCode = | 'session-expired' | 'invalid-signature' | 'unauthorized' | 'forbidden' | 'backend-unavailable'; export interface AuthError { code: AuthErrorCode; message: string; /** HTTP status that produced this error, when known (absent for client-side errors, e.g. no Ed25519 support). */ status?: number; } /** Maps a backend error envelope's `error.code` to the client's AuthErrorCode screens. Only codes with a dedicated screen are mapped; anything else falls back to the HTTP-status-derived code via authErrorCodeFromStatus. */ const BACKEND_ERROR_CODE_MAP: Record = { TOKEN_EXPIRED: 'session-expired', INVALID_SIGNATURE: 'invalid-signature', UNAUTHENTICATED: 'unauthorized', FORBIDDEN: 'forbidden', SERVICE_UNAVAILABLE: 'backend-unavailable', }; export function authErrorCodeFromBackendCode(code: unknown): AuthErrorCode | undefined { return typeof code === 'string' ? BACKEND_ERROR_CODE_MAP[code] : undefined; } /** Maps a backend HTTP status to the AuthErrorCode screen it should route to. */ export function authErrorCodeFromStatus(status: number): AuthErrorCode { switch (status) { case 401: return 'unauthorized'; case 403: return 'forbidden'; case 0: return 'backend-unavailable'; default: return status >= 500 ? 'backend-unavailable' : 'unauthorized'; } }