{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-better-auth",
  "title": "UseBetterAuth",
  "description": "Flexible authentication hook.",
  "files": [
    {
      "path": "registry/hooks/use-better-auth.tsx",
      "content": "'use client';\n\nimport { useCallback, useEffect, useState } from 'react';\n\nexport interface StorageLike {\n  getItem(key: string): string | null;\n  setItem(key: string, value: string): void;\n  removeItem(key: string): void;\n}\n\nexport interface BetterAuthResponse<TUser> {\n  token: string;\n  user: TUser;\n}\n\nexport interface DefaultCredentials\n  extends Record<string, string | number | boolean> {\n  email: string;\n  password: string;\n}\n\nexport interface UseBetterAuthReturn<\n  TUser,\n  TCredentials extends Record<string, string | number | boolean>,\n> {\n  token: string | null;\n  user: TUser | null;\n  loading: boolean;\n  error: Error | null;\n  isAuthenticated: boolean;\n  login: (credentials: TCredentials) => Promise<void>;\n  logout: () => void;\n  refresh: () => Promise<void>;\n  updateUser: (user: TUser, token?: string) => void;\n  clearError: () => void;\n}\n\nexport interface UseBetterAuthOptions<\n  TUser,\n  TCredentials extends Record<string, string | number | boolean>,\n> {\n  /** Storage key prefix for storing authentication data. Defaults to 'better-auth' */\n  storageKey?: string;\n  /** Custom storage implementation. Defaults to localStorage */\n  storage?: StorageLike;\n  /** Login endpoint URL. Defaults to '/api/login' */\n  loginUrl?: string;\n  /** Token refresh endpoint URL */\n  refreshUrl?: string;\n  /** Logout endpoint URL */\n  logoutUrl?: string;\n  /** Auto-refresh interval in milliseconds */\n  refreshInterval?: number;\n  /** Callback fired when authentication state changes */\n  onAuthChange?: (token: string | null, user: TUser | null) => void;\n  /** Custom fetcher function for authentication requests */\n  fetcher?: (\n    credentials: TCredentials,\n    url: string,\n  ) => Promise<BetterAuthResponse<TUser>>;\n}\n\n/**\n * A flexible authentication hook that provides login, logout, token refresh, and user management.\n *\n * Features:\n * - Customizable storage (localStorage, sessionStorage, or custom implementation)\n * - Automatic token refresh with configurable intervals\n * - SSR-safe with proper hydration handling\n * - Type-safe with generic user and credentials types\n * - Custom fetcher support for different API implementations\n * - Cross-tab synchronization through storage events\n * - Error handling with clear error states\n *\n * @example\n * ```tsx\n * // Basic usage\n * const auth = useBetterAuth<User>({\n *   loginUrl: '/api/auth/login',\n *   refreshUrl: '/api/auth/refresh',\n * });\n *\n * // Login\n * await auth.login({ email: 'user@example.com', password: 'password' });\n *\n * // Check authentication\n * if (auth.isAuthenticated) {\n *   console.log('User:', auth.user);\n * }\n *\n * // Logout\n * auth.logout();\n * ```\n *\n * @example\n * ```tsx\n * // With custom storage and fetcher\n * const auth = useBetterAuth<User, CustomCredentials>({\n *   storage: sessionStorage,\n *   storageKey: 'my-app-auth',\n *   refreshInterval: 300000, // 5 minutes\n *   fetcher: async (credentials, url) => {\n *     const response = await customApiCall(url, credentials);\n *     return { token: response.accessToken, user: response.userData };\n *   },\n *   onAuthChange: (token, user) => {\n *     console.log('Auth state changed:', { token, user });\n *   },\n * });\n * ```\n *\n * @param options Configuration options for the authentication hook\n * @returns Authentication state and methods\n */\nexport function useBetterAuth<\n  TUser,\n  TCredentials extends Record<\n    string,\n    string | number | boolean\n  > = DefaultCredentials,\n>(\n  options: UseBetterAuthOptions<TUser, TCredentials> = {},\n): UseBetterAuthReturn<TUser, TCredentials> {\n  const {\n    storageKey = 'better-auth',\n    storage = typeof window !== 'undefined' ? localStorage : undefined,\n    loginUrl = '/api/login',\n    refreshUrl,\n    logoutUrl,\n    refreshInterval,\n    onAuthChange,\n    fetcher,\n  } = options;\n\n  // Debug logging in development\n  const debugLog = useCallback((message: string, data?: any) => {\n    if (process.env.NODE_ENV === 'development') {\n      console.log(`[useBetterAuth] ${message}`, data || '');\n    }\n  }, []);\n\n  const [token, setToken] = useState<string | null>(() => {\n    if (typeof window === 'undefined' || !storage) return null;\n    try {\n      const storedToken = storage.getItem(`${storageKey}:token`);\n      debugLog('Initial token loaded from storage', {\n        hasToken: !!storedToken,\n      });\n      return storedToken;\n    } catch (error) {\n      debugLog('Error loading initial token', error);\n      return null;\n    }\n  });\n\n  const [user, setUser] = useState<TUser | null>(() => {\n    if (typeof window === 'undefined' || !storage) return null;\n    try {\n      const saved = storage.getItem(`${storageKey}:user`);\n      const userData = saved ? (JSON.parse(saved) as TUser) : null;\n      debugLog('Initial user loaded from storage', { hasUser: !!userData });\n      return userData;\n    } catch (error) {\n      debugLog('Error loading initial user', error);\n      return null;\n    }\n  });\n\n  const [loading, setLoading] = useState(false);\n  const [error, setError] = useState<Error | null>(null);\n\n  const login = useCallback(\n    async (credentials: TCredentials) => {\n      debugLog('Login attempt started', { loginUrl });\n      setLoading(true);\n      setError(null);\n\n      try {\n        const fetchFn =\n          fetcher ??\n          (async (\n            creds: TCredentials,\n            url: string,\n          ): Promise<BetterAuthResponse<TUser>> => {\n            debugLog('Using default fetcher for login');\n            const res = await fetch(url, {\n              method: 'POST',\n              headers: { 'Content-Type': 'application/json' },\n              body: JSON.stringify(creds),\n            });\n\n            if (!res.ok) {\n              const errorText = await res.text().catch(() => 'Unknown error');\n              throw new Error(`Login failed: ${res.status} ${errorText}`);\n            }\n\n            return (await res.json()) as BetterAuthResponse<TUser>;\n          });\n\n        const json = await fetchFn(credentials, loginUrl);\n        debugLog('Login successful', {\n          hasToken: !!json.token,\n          hasUser: !!json.user,\n        });\n\n        setToken(json.token);\n        setUser(json.user);\n\n        if (storage) {\n          storage.setItem(`${storageKey}:token`, json.token);\n          storage.setItem(`${storageKey}:user`, JSON.stringify(json.user));\n          debugLog('Auth data saved to storage');\n        }\n\n        onAuthChange?.(json.token, json.user);\n      } catch (err) {\n        const error = err instanceof Error ? err : new Error('Login failed');\n        debugLog('Login failed', error);\n        setError(error);\n        throw error;\n      } finally {\n        setLoading(false);\n      }\n    },\n    [storageKey, loginUrl, fetcher, storage, onAuthChange, debugLog],\n  );\n\n  const logout = useCallback(() => {\n    debugLog('Logout initiated', { hasLogoutUrl: !!logoutUrl });\n\n    if (logoutUrl && token) {\n      fetch(logoutUrl, {\n        method: 'POST',\n        headers: { Authorization: `Bearer ${token}` },\n      }).catch((error) => {\n        debugLog('Logout API call failed (ignored)', error);\n      });\n    }\n\n    setToken(null);\n    setUser(null);\n\n    if (storage) {\n      storage.removeItem(`${storageKey}:token`);\n      storage.removeItem(`${storageKey}:user`);\n      debugLog('Auth data cleared from storage');\n    }\n\n    onAuthChange?.(null, null);\n  }, [logoutUrl, token, storageKey, storage, onAuthChange, debugLog]);\n\n  const refresh = useCallback(async () => {\n    if (!token || !refreshUrl) {\n      debugLog('Refresh skipped', {\n        hasToken: !!token,\n        hasRefreshUrl: !!refreshUrl,\n      });\n      return;\n    }\n\n    debugLog('Token refresh started');\n    setLoading(true);\n    setError(null);\n\n    try {\n      const res = await fetch(refreshUrl, {\n        headers: { Authorization: `Bearer ${token}` },\n      });\n\n      if (!res.ok) {\n        const errorText = await res.text().catch(() => 'Unknown error');\n        throw new Error(`Token refresh failed: ${res.status} ${errorText}`);\n      }\n\n      const json = (await res.json()) as BetterAuthResponse<TUser>;\n      debugLog('Token refresh successful');\n\n      setToken(json.token);\n      setUser(json.user);\n\n      if (storage) {\n        storage.setItem(`${storageKey}:token`, json.token);\n        storage.setItem(`${storageKey}:user`, JSON.stringify(json.user));\n      }\n\n      onAuthChange?.(json.token, json.user);\n    } catch (err) {\n      const error =\n        err instanceof Error ? err : new Error('Token refresh failed');\n      debugLog('Token refresh failed', error);\n      setError(error);\n      throw error;\n    } finally {\n      setLoading(false);\n    }\n  }, [token, refreshUrl, storageKey, storage, onAuthChange, debugLog]);\n\n  const updateUser = useCallback(\n    (nextUser: TUser, nextToken?: string) => {\n      debugLog('User update', { hasNewToken: !!nextToken });\n\n      setUser(nextUser);\n      if (nextToken !== undefined) {\n        setToken(nextToken);\n        if (storage) {\n          storage.setItem(`${storageKey}:token`, nextToken);\n        }\n      }\n      if (storage) {\n        storage.setItem(`${storageKey}:user`, JSON.stringify(nextUser));\n      }\n      onAuthChange?.(nextToken ?? token, nextUser);\n    },\n    [storageKey, storage, onAuthChange, token, debugLog],\n  );\n\n  const clearError = useCallback(() => {\n    debugLog('Error cleared');\n    setError(null);\n  }, [debugLog]);\n\n  // Sync token to storage and trigger auth change\n  useEffect(() => {\n    if (!token || !storage) return;\n    storage.setItem(`${storageKey}:token`, token);\n    onAuthChange?.(token, user);\n  }, [token, storageKey, storage, onAuthChange, user]);\n\n  // Sync user to storage and trigger auth change\n  useEffect(() => {\n    if (user === null || !storage) return;\n    storage.setItem(`${storageKey}:user`, JSON.stringify(user));\n    onAuthChange?.(token, user);\n  }, [user, storageKey, storage, onAuthChange, token]);\n\n  // Auto-refresh setup\n  useEffect(() => {\n    if (!refreshInterval || !refreshUrl) return;\n\n    debugLog('Auto-refresh enabled', { interval: refreshInterval });\n    const id = setInterval(refresh, refreshInterval);\n    return () => {\n      debugLog('Auto-refresh disabled');\n      clearInterval(id);\n    };\n  }, [refreshInterval, refreshUrl, refresh, debugLog]);\n\n  return {\n    token,\n    user,\n    loading,\n    error,\n    isAuthenticated: token !== null,\n    login,\n    logout,\n    refresh,\n    updateUser,\n    clearError,\n  };\n}\n",
      "type": "registry:hook",
      "target": "hooks/guarahooks/use-better-auth.tsx"
    }
  ],
  "categories": [
    "auth"
  ],
  "type": "registry:hook"
}