{"version":3,"sources":["../src/components/auth/NextAuthGuard.tsx","../src/components/auth/AuthGuard.tsx","../src/context/CilantroContext.tsx","../src/core/auth.ts","../src/core/storage-adapter.ts","../src/providers/CilantroAuthProvider.tsx","../src/components/auth/LoginForm.tsx","../src/hooks/useReturnUrl.ts","../src/ui/button.tsx","../src/ui/cn.ts","../src/ui/input.tsx","../src/ui/label.tsx","../src/ui/card.tsx","../src/components/auth/SocialLoginButtons.tsx","../src/hooks/useAuth.ts","../src/hooks/useCilantroConfig.ts","../src/ui/skeleton.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport type { AuthGuardProps } from \"./AuthGuard\";\nimport { AuthGuard } from \"./AuthGuard\";\n\nexport type { AuthGuardProps, AuthGuardClassNames, AuthGuardLayout } from \"./AuthGuard\";\n\n/**\n * AuthGuard integrated with Next.js App Router.\n * Uses `useRouter().replace()` for redirects (no full page reload).\n * Requires `next` to be installed.\n *\n * @example\n * // app/dashboard/layout.tsx or page.tsx\n * <NextAuthGuard redirectTo=\"/login\" layout=\"fullscreen\">\n *   <DashboardContent />\n * </NextAuthGuard>\n */\nexport function NextAuthGuard(props: Omit<AuthGuardProps, \"onRedirect\">) {\n  const router = useRouter();\n  return (\n    <AuthGuard\n      {...props}\n      onRedirect={(path) => router.replace(path)}\n    />\n  );\n}\n","\"use client\";\n\nimport { useMemo, type ReactNode } from \"react\";\nimport { useCilantroAuth } from \"../../providers/CilantroAuthProvider\";\nimport { LoginForm } from \"./LoginForm\";\nimport { Skeleton } from \"../../ui/skeleton\";\nimport { cn } from \"../../ui/cn\";\n\nexport interface AuthGuardClassNames {\n  root?: string;\n  fallback?: string;\n  skeleton?: string;\n  loading?: string;\n  /** Centered/fullscreen layout wrapper */\n  layout?: string;\n}\n\nexport type AuthGuardLayout = \"inline\" | \"centered\" | \"fullscreen\";\n\nexport interface AuthGuardProps {\n  children: ReactNode;\n  /** Shown when not authenticated. Default: <LoginForm /> */\n  fallback?: ReactNode;\n  /** Redirect unauthenticated users to this path (e.g. \"/login\"). Uses onRedirect if provided, else window.location. */\n  redirectTo?: string;\n  /** Custom redirect function for SPA routers (Next.js, React Router). e.g. (path) => router.replace(path) */\n  onRedirect?: (path: string) => void;\n  /** Query param for return URL when redirecting. Default: \"returnUrl\" */\n  returnUrlParam?: string;\n  /** Layout for fallback content. \"centered\" and \"fullscreen\" give polished auth UX. */\n  layout?: AuthGuardLayout;\n  /** Root class when showing fallback */\n  className?: string;\n  classNames?: AuthGuardClassNames;\n  /** When true, show fallback (login) instead of redirecting. When false, redirect when redirectTo is set, else render null. */\n  showFallback?: boolean;\n  /** When true, show skeleton while loading. When false, show \"Loading...\" text. */\n  useSkeleton?: boolean;\n}\n\nconst LAYOUT_CLASSES: Record<AuthGuardLayout, string> = {\n  inline: \"\",\n  centered: \"flex min-h-[280px] items-center justify-center p-6\",\n  fullscreen: \"fixed inset-0 z-50 flex min-h-screen items-center justify-center bg-background p-4\",\n};\n\nfunction getCurrentReturnPath(returnUrlParam: string): string {\n  if (typeof window === \"undefined\") return \"/\";\n  const params = new URLSearchParams(window.location.search);\n  const existing = params.get(returnUrlParam);\n  if (existing && existing.startsWith(\"/\")) return existing;\n  const path = window.location.pathname + window.location.search;\n  return path || \"/\";\n}\n\n/**\n * Wraps content and shows fallback (e.g. LoginForm) when the user is not authenticated.\n * Use inside CilantroProvider.\n *\n * For Next.js: pass onRedirect={(path) => router.replace(path)} and redirectTo=\"/login\".\n * For return-after-login flow: set redirectTo=\"/login\"; AuthGuard will redirect with ?returnUrl=currentPath.\n */\nexport function AuthGuard({\n  children,\n  fallback,\n  redirectTo,\n  onRedirect,\n  returnUrlParam = \"returnUrl\",\n  layout = \"inline\",\n  className,\n  classNames,\n  showFallback = true,\n  useSkeleton = false,\n}: AuthGuardProps) {\n  const { isAuthenticated, isLoading } = useCilantroAuth();\n\n  const redirectPath = useMemo(() => {\n    if (!redirectTo) return null;\n    const returnPath = getCurrentReturnPath(returnUrlParam);\n    const sep = redirectTo.includes(\"?\") ? \"&\" : \"?\";\n    return `${redirectTo}${sep}${returnUrlParam}=${encodeURIComponent(returnPath)}`;\n  }, [redirectTo, returnUrlParam]);\n\n  if (isLoading) {\n    const loadingContent = useSkeleton ? (\n      <div className={cn(\"rounded-lg border border-input p-6 space-y-4 w-full max-w-sm\", classNames?.fallback)}>\n        <Skeleton className={cn(\"h-7 w-2/3 rounded\", classNames?.skeleton)} />\n        <Skeleton className={cn(\"h-4 w-full rounded\", classNames?.skeleton)} />\n        <Skeleton className={cn(\"h-10 w-full rounded\", classNames?.skeleton)} />\n        <Skeleton className={cn(\"h-10 w-full rounded\", classNames?.skeleton)} />\n      </div>\n    ) : (\n      <div className=\"flex flex-col items-center gap-3\">\n        <div\n          className=\"h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent\"\n          aria-hidden\n        />\n        <p className=\"text-sm text-muted-foreground\">Loading...</p>\n      </div>\n    );\n\n    return (\n      <div\n        className={cn(\n          LAYOUT_CLASSES[layout],\n          className,\n          classNames?.root,\n          classNames?.loading\n        )}\n        data-cilantro-auth-guard\n        aria-busy=\"true\"\n        aria-live=\"polite\"\n      >\n        <div className={cn(layout !== \"inline\" && \"w-full max-w-sm\", classNames?.layout)}>\n          {loadingContent}\n        </div>\n      </div>\n    );\n  }\n\n  if (!isAuthenticated) {\n    if (!showFallback && redirectPath) {\n      if (onRedirect) {\n        onRedirect(redirectPath);\n      } else if (typeof window !== \"undefined\") {\n        window.location.href = redirectPath;\n      }\n      return null;\n    }\n    if (!showFallback) return null;\n\n    // Redirect mode: send user to login page with returnUrl\n    if (redirectPath) {\n      if (onRedirect) {\n        onRedirect(redirectPath);\n      } else if (typeof window !== \"undefined\") {\n        window.location.href = redirectPath;\n      }\n      return null;\n    }\n\n    // Show fallback (login form) - pass returnPath for post-login redirect\n    const returnPath = getCurrentReturnPath(returnUrlParam);\n    const fallbackContent =\n      fallback ??\n      (\n        <LoginForm\n          className={classNames?.fallback}\n          redirectAfterSuccess={returnPath}\n          returnUrlParam={returnUrlParam}\n          onRedirect={onRedirect}\n        />\n      );\n\n    return (\n      <div\n        className={cn(\n          LAYOUT_CLASSES[layout],\n          className,\n          classNames?.root\n        )}\n        data-cilantro-auth-guard\n      >\n        <div className={cn(layout !== \"inline\" && \"w-full max-w-sm\", classNames?.layout)}>\n          {fallbackContent}\n        </div>\n      </div>\n    );\n  }\n\n  return <>{children}</>;\n}\n","\"use client\";\n\nimport {\n  createContext,\n  type ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useMemo,\n  useState,\n} from \"react\";\nimport { clearAuth } from \"cilantro-sdk\";\nimport { login as cilantroLogin, socialLoginAndSetAuth } from \"cilantro-sdk/auth\";\nimport type { AuthControllerLogin200Data } from \"cilantro-sdk/auth\";\nimport { create as createUser } from \"cilantro-sdk/user\";\nimport { create as createWalletSDK, findAll as findAllWallets } from \"cilantro-sdk/wallet\";\nimport type {\n  WalletControllerFindAllResult,\n  WalletControllerCreateResult,\n} from \"cilantro-sdk/wallet\";\nimport { setAuthConfig, setSdkAuth, getPlatformApiKey, isJwtExpired } from \"../core/auth\";\nimport { setStorageAdapter } from \"../core/storage-adapter\";\nimport type { DeviceKeyStorage } from \"../core/storage-adapter\";\nimport { extractResponseData } from \"../core/types\";\nimport { extractErrorMessage, isAuthError } from \"../core/error-utils\";\nimport { normalizeWallet } from \"../core/normalize\";\n\nconst DEFAULT_BASE_URL = \"https://api.cilantro.gg\";\nconst DEFAULT_JWT_STORAGE_KEY = \"cilantro_jwt\";\nconst DEFAULT_WALLET_STORAGE_KEY = \"cilantro_selected_wallet_id\";\n\n/** Hard-coded Google OAuth client ID for built-in Google Sign-In. */\nconst GOOGLE_CLIENT_ID = \"75083940771-q41831bdbpckupb0idvqf1pgr5ng4lg4.apps.googleusercontent.com\";\n\n/** Minimal user view; populated from SDK login result data.user when available. */\nexport interface User {\n  username?: string;\n  email?: string;\n  userType?: string;\n}\n\n/** SDK wallet list item type. */\nexport type WalletDataDto = WalletControllerFindAllResult[\"data\"] extends readonly (infer E)[]\n  ? E\n  : WalletControllerFindAllResult[\"data\"] extends (infer E)[]\n    ? E\n    : never;\n\n/** Wallet with backward-compat aliases (id, address, active). */\nexport type WalletData = WalletDataDto & {\n  id?: string;\n  address?: string;\n  active?: boolean;\n};\n\nexport interface SocialAuthConfig {\n  /** Google OAuth client ID for built-in Google Sign-In. */\n  googleClientId?: string;\n  /** Discord OAuth client ID (optional; for reference). */\n  discordClientId?: string;\n  /** Client redirect URI for built-in \"Sign in with Discord\" (e.g. https://myapp.com/auth/callback). User lands here after Discord login; use setAuthFromFragment() on that page. */\n  discordRedirectUri?: string;\n}\n\n/** Params for loginWithSocial. Matches cilantro-sdk SocialLoginDto: Google/Apple use id_token, Discord uses access_token. */\nexport type SocialLoginParams =\n  | { provider: \"google\"; id_token: string }\n  | { provider: \"apple\"; id_token: string }\n  | { provider: \"discord\"; access_token: string };\n\nexport interface CilantroConfig {\n  apiKey?: string;\n  baseURL: string;\n  jwt?: string | null;\n  socialAuth?: SocialAuthConfig;\n}\n\nexport interface CilantroContextValue {\n  config: CilantroConfig;\n  storage: DeviceKeyStorage | null;\n  // Auth\n  user: User | null;\n  jwt: string | null;\n  isAuthenticated: boolean;\n  isLoading: boolean;\n  login: (params: { usernameOrEmail: string; password: string }) => Promise<void>;\n  /** Sign in with Google/Apple (id_token) or Discord (access_token). Matches cilantro-sdk SocialLoginDto. */\n  loginWithSocial: (params: SocialLoginParams) => Promise<void>;\n  logout: () => void;\n  register: (\n    username: string,\n    email: string,\n    password: string,\n    isActive?: boolean\n  ) => Promise<void>;\n  clearSessionDueToAuthError: () => void;\n  // Wallet\n  wallet: WalletData | null;\n  wallets: WalletData[];\n  createWallet: (params: { name: string; userId?: string }) => Promise<WalletControllerCreateResult>;\n  selectWallet: (walletId: string) => void;\n  refreshWallets: () => Promise<void>;\n  walletError: string | null;\n  walletsLoading: boolean;\n}\n\nconst CilantroContext = createContext<CilantroContextValue | undefined>(undefined);\n\nexport interface CilantroContextProviderProps {\n  children: ReactNode;\n  apiKey?: string;\n  baseURL?: string;\n  storageAdapter?: DeviceKeyStorage | null;\n  jwtStorageKey?: string;\n  walletStorageKey?: string;\n  /** When true, sync JWT to a cookie (same name as jwtStorageKey) for Next.js/middleware. Default: false. */\n  syncJwtToCookie?: boolean;\n  /** Cookie name for JWT when syncJwtToCookie is true. Default: cilantro_jwt. */\n  jwtCookieName?: string;\n  onLoginSuccess?: () => void;\n  onLogout?: () => void;\n  onRegisterSuccess?: () => void;\n  onSessionExpired?: () => void;\n}\n\nconst DEFAULT_JWT_COOKIE_NAME = \"cilantro_jwt\";\n\nfunction setJwtCookie(name: string, token: string | null): void {\n  if (typeof document === \"undefined\") return;\n  const value = token ?? \"\";\n  if (!value) {\n    document.cookie = `${name}=; path=/; SameSite=Lax; max-age=0`;\n    return;\n  }\n  // Set cookie with long expiry (JWT has its own exp; middleware will validate).\n  const maxAge = 60 * 60 * 24 * 7; // 7 days\n  document.cookie = `${name}=${encodeURIComponent(value)}; path=/; SameSite=Lax; max-age=${maxAge}`;\n}\n\nexport function CilantroContextProvider({\n  children,\n  apiKey,\n  baseURL = DEFAULT_BASE_URL,\n  storageAdapter = null,\n  jwtStorageKey = DEFAULT_JWT_STORAGE_KEY,\n  walletStorageKey = DEFAULT_WALLET_STORAGE_KEY,\n  syncJwtToCookie = false,\n  jwtCookieName = DEFAULT_JWT_COOKIE_NAME,\n  onLoginSuccess,\n  onLogout,\n  onRegisterSuccess,\n  onSessionExpired,\n}: CilantroContextProviderProps) {\n  const [user, setUser] = useState<User | null>(null);\n  const [jwt, setJwt] = useState<string | null>(null);\n  const [authLoading, setAuthLoading] = useState(true);\n  const [wallets, setWallets] = useState<WalletData[]>([]);\n  const [selectedWallet, setSelectedWallet] = useState<WalletData | null>(null);\n  const [walletsLoading, setWalletsLoading] = useState(true);\n  const [walletError, setWalletError] = useState<string | null>(null);\n\n  const config = useMemo<CilantroConfig>(\n    () => ({\n      apiKey,\n      baseURL,\n      jwt: jwt ?? undefined,\n      socialAuth: { googleClientId: GOOGLE_CLIENT_ID },\n    }),\n    [apiKey, baseURL, jwt]\n  );\n\n  // Inject storage adapter into core so signer creation uses it\n  useEffect(() => {\n    setStorageAdapter(storageAdapter ?? null);\n    return () => setStorageAdapter(null);\n  }, [storageAdapter]);\n\n  // Set SDK auth config and restore session from storage\n  useEffect(() => {\n    setAuthConfig({ apiKey, baseURL });\n    const storedToken =\n      typeof window !== \"undefined\" ? localStorage.getItem(jwtStorageKey) : null;\n    if (storedToken) {\n      if (isJwtExpired(storedToken)) {\n        if (typeof window !== \"undefined\") {\n          localStorage.removeItem(jwtStorageKey);\n          const keys = Object.keys(localStorage);\n          keys.forEach((key) => {\n            if (key.startsWith(\"delegated-key-\")) localStorage.removeItem(key);\n          });\n        }\n        setSdkAuth(null);\n      } else {\n        setJwt(storedToken);\n        setSdkAuth(storedToken);\n        try {\n          const payload = JSON.parse(atob(storedToken.split(\".\")[1]));\n          setUser({\n            username: payload.username,\n            email: payload.email,\n            userType: payload.userType,\n          });\n        } catch {\n          // ignore\n        }\n      }\n    } else {\n      setSdkAuth(null);\n    }\n    setAuthLoading(false);\n  }, [apiKey, baseURL, jwtStorageKey]);\n\n  // Sync JWT to cookie for Next.js/middleware when enabled\n  useEffect(() => {\n    if (syncJwtToCookie && typeof document !== \"undefined\") {\n      setJwtCookie(jwtCookieName, jwt);\n    }\n  }, [jwt, syncJwtToCookie, jwtCookieName]);\n\n  const login = useCallback(\n    async (params: { usernameOrEmail: string; password: string }) => {\n      const result = await cilantroLogin(params);\n      const data: AuthControllerLogin200Data | undefined =\n        result && typeof result === \"object\" && \"data\" in result\n          ? (result as { data: AuthControllerLogin200Data }).data\n          : undefined;\n      const token =\n        (data && \"jwt\" in data ? (data as { jwt?: string }).jwt : undefined) ??\n        (data && \"accessToken\" in data ? (data as { accessToken?: string }).accessToken : undefined);\n      if (!token) throw new Error(\"No JWT token received from server\");\n      const userType = data?.userType;\n      setJwt(token);\n      setSdkAuth(token);\n      if (typeof window !== \"undefined\") {\n        localStorage.setItem(jwtStorageKey, token);\n        if (syncJwtToCookie) setJwtCookie(jwtCookieName, token);\n      }\n      if (data?.user && typeof data.user === \"object\") {\n        const u = data.user as Record<string, unknown>;\n        setUser({\n          username: u.username as string | undefined,\n          email: u.email as string | undefined,\n          userType: (userType ?? u.userType) as string | undefined,\n        });\n      } else {\n        try {\n          const payload = JSON.parse(atob(token.split(\".\")[1])) as Record<string, unknown>;\n          setUser({\n            username: payload.username as string | undefined,\n            email: payload.email as string | undefined,\n            userType: (userType ?? payload.userType) as string | undefined,\n          });\n        } catch {\n          setUser({ userType: userType as string | undefined });\n        }\n      }\n      onLoginSuccess?.();\n    },\n    [jwtStorageKey, jwtCookieName, syncJwtToCookie, onLoginSuccess]\n  );\n\n  const loginWithSocial = useCallback(\n    async (params: SocialLoginParams) => {\n      const credentials =\n        params.provider === \"discord\"\n          ? { provider: \"discord\" as const, access_token: params.access_token }\n          : { provider: params.provider, id_token: params.id_token };\n      const result = await socialLoginAndSetAuth(credentials);\n      const data: AuthControllerLogin200Data | undefined =\n        result && typeof result === \"object\" && \"data\" in result\n          ? (result as { data: AuthControllerLogin200Data }).data\n          : undefined;\n      const token =\n        (data && \"jwt\" in data ? (data as { jwt?: string }).jwt : undefined) ??\n        (data && \"accessToken\" in data ? (data as { accessToken?: string }).accessToken : undefined);\n      if (!token) throw new Error(\"No JWT token received from server\");\n      const userType = data?.userType;\n      setJwt(token);\n      setSdkAuth(token);\n      if (typeof window !== \"undefined\") {\n        localStorage.setItem(jwtStorageKey, token);\n        if (syncJwtToCookie) setJwtCookie(jwtCookieName, token);\n      }\n      if (data?.user && typeof data.user === \"object\") {\n        const u = data.user as Record<string, unknown>;\n        setUser({\n          username: u.username as string | undefined,\n          email: u.email as string | undefined,\n          userType: (userType ?? u.userType) as string | undefined,\n        });\n      } else {\n        try {\n          const payload = JSON.parse(atob(token.split(\".\")[1])) as Record<string, unknown>;\n          setUser({\n            username: payload.username as string | undefined,\n            email: payload.email as string | undefined,\n            userType: (userType ?? payload.userType) as string | undefined,\n          });\n        } catch {\n          setUser({ userType: userType as string | undefined });\n        }\n      }\n      onLoginSuccess?.();\n    },\n    [jwtStorageKey, jwtCookieName, syncJwtToCookie, onLoginSuccess]\n  );\n\n  const logout = useCallback(() => {\n    setUser(null);\n    setJwt(null);\n    clearAuth();\n    if (typeof window !== \"undefined\") {\n      localStorage.removeItem(jwtStorageKey);\n      if (syncJwtToCookie) setJwtCookie(jwtCookieName, null);\n      const keys = Object.keys(localStorage);\n      keys.forEach((key) => {\n        if (key.startsWith(\"delegated-key-\")) localStorage.removeItem(key);\n      });\n    }\n    setSdkAuth(null);\n    onLogout?.();\n  }, [jwtStorageKey, jwtCookieName, syncJwtToCookie, onLogout]);\n\n  const register = useCallback(\n    async (username: string, email: string, password: string, isActive = true) => {\n      setSdkAuth(null);\n      const platformKey = getPlatformApiKey();\n      const userData: {\n        username: string;\n        email: string;\n        password: string;\n        isActive: boolean;\n        platformApiKey?: string;\n      } = { username, email, password, isActive };\n      if (platformKey) userData.platformApiKey = platformKey;\n      await createUser(userData);\n      await login({ usernameOrEmail: username, password });\n      onRegisterSuccess?.();\n    },\n    [login, onRegisterSuccess]\n  );\n\n  const clearSessionDueToAuthError = useCallback(() => {\n    setUser(null);\n    setJwt(null);\n    clearAuth();\n    if (typeof window !== \"undefined\") {\n      localStorage.removeItem(jwtStorageKey);\n      if (syncJwtToCookie) setJwtCookie(jwtCookieName, null);\n      const keys = Object.keys(localStorage);\n      keys.forEach((key) => {\n        if (key.startsWith(\"delegated-key-\")) localStorage.removeItem(key);\n      });\n    }\n    setSdkAuth(null);\n    onSessionExpired?.();\n  }, [jwtStorageKey, jwtCookieName, syncJwtToCookie, onSessionExpired]);\n\n  const loadWallets = useCallback(async () => {\n    if (!jwt) return;\n    setWalletsLoading(true);\n    setWalletError(null);\n    try {\n      setSdkAuth(jwt);\n      const result = await findAllWallets();\n      const walletsList = extractResponseData<WalletDataDto[]>(result) ?? [];\n      const list = Array.isArray(walletsList) ? walletsList : [];\n      const formatted: WalletData[] = list.map((w) =>\n        normalizeWallet(w as unknown as Record<string, unknown>) as unknown as WalletData\n      );\n      setWallets(formatted);\n    } catch (err) {\n      if (isAuthError(err)) {\n        clearSessionDueToAuthError();\n        setWalletError(\"Session expired. Please sign in again.\");\n      } else {\n        setWalletError(extractErrorMessage(err));\n      }\n      setWallets([]);\n    } finally {\n      setWalletsLoading(false);\n    }\n  }, [jwt, clearSessionDueToAuthError]);\n\n  useEffect(() => {\n    if (jwt) {\n      loadWallets();\n    } else {\n      setWallets([]);\n      setSelectedWallet(null);\n      setWalletError(null);\n      setWalletsLoading(false);\n    }\n  }, [jwt, loadWallets]);\n\n  useEffect(() => {\n    if (wallets.length > 0 && !selectedWallet) {\n      const storedId =\n        typeof window !== \"undefined\" ? localStorage.getItem(walletStorageKey) : null;\n      if (storedId) {\n        const w = wallets.find((x) => x.id === storedId || x.walletId === storedId);\n        if (w) setSelectedWallet(w);\n        else {\n          setSelectedWallet(wallets[0]);\n          if (typeof window !== \"undefined\")\n            localStorage.setItem(walletStorageKey, wallets[0].id ?? wallets[0].walletId);\n        }\n      } else {\n        setSelectedWallet(wallets[0]);\n        if (typeof window !== \"undefined\")\n          localStorage.setItem(walletStorageKey, wallets[0].id ?? wallets[0].walletId);\n      }\n    } else if (wallets.length === 0) {\n      setSelectedWallet(null);\n      if (typeof window !== \"undefined\") localStorage.removeItem(walletStorageKey);\n    }\n  }, [wallets, walletStorageKey]);\n\n  const selectWallet = useCallback(\n    (walletId: string) => {\n      const w = wallets.find((x) => x.id === walletId || x.walletId === walletId);\n      if (w) {\n        setSelectedWallet(w);\n        if (typeof window !== \"undefined\")\n          localStorage.setItem(walletStorageKey, w.id ?? w.walletId);\n      }\n    },\n    [wallets, walletStorageKey]\n  );\n\n  const createWallet = useCallback(\n    async (params: { name: string; userId?: string }) => {\n      setSdkAuth(jwt ?? undefined);\n      const dto = { walletName: params.name, chain: \"solana\" as const };\n      const result = await createWalletSDK(dto);\n      await loadWallets();\n      return result as WalletControllerCreateResult;\n    },\n    [jwt, loadWallets]\n  );\n\n  const value: CilantroContextValue = useMemo(\n    () => ({\n      config,\n      storage: storageAdapter ?? null,\n      user,\n      jwt,\n      isAuthenticated: !!jwt,\n      isLoading: authLoading,\n      login,\n      loginWithSocial,\n      logout,\n      register,\n      clearSessionDueToAuthError,\n      wallet: selectedWallet,\n      wallets,\n      createWallet,\n      selectWallet,\n      refreshWallets: loadWallets,\n      walletError,\n      walletsLoading,\n    }),\n    [\n      config,\n      storageAdapter,\n      user,\n      jwt,\n      authLoading,\n      login,\n      loginWithSocial,\n      logout,\n      register,\n      clearSessionDueToAuthError,\n      selectedWallet,\n      wallets,\n      createWallet,\n      selectWallet,\n      loadWallets,\n      walletError,\n      walletsLoading,\n    ]\n  );\n\n  return (\n    <CilantroContext.Provider value={value}>{children}</CilantroContext.Provider>\n  );\n}\n\nexport function useCilantroContext(): CilantroContextValue {\n  const ctx = useContext(CilantroContext);\n  if (ctx === undefined) {\n    throw new Error(\"useCilantroContext must be used within CilantroContextProvider\");\n  }\n  return ctx;\n}\n","import { setAuth } from \"cilantro-sdk\";\n\nlet platformApiKey = \"\";\nlet apiUrl: string | undefined;\n\nexport interface AuthConfig {\n  platformApiKey?: string;\n  apiUrl?: string;\n  /** Guide-aligned alias for platformApiKey */\n  apiKey?: string;\n  /** Guide-aligned alias for apiUrl */\n  baseURL?: string;\n  jwt?: string | null;\n}\n\n/**\n * Set SDK authentication config. Call with apiKey/baseURL (or platformApiKey/apiUrl) from provider/config.\n * Do not read process.env in library code.\n */\nexport function setAuthConfig(config: AuthConfig): void {\n  platformApiKey = config.apiKey ?? config.platformApiKey ?? \"\";\n  apiUrl = config.baseURL ?? config.apiUrl;\n  const authPayload: { jwt?: string; apiKey?: string; platformApiKey?: string; apiUrl?: string } = {};\n  if (config.jwt) {\n    authPayload.jwt = config.jwt;\n  }\n  if (platformApiKey) {\n    authPayload.apiKey = platformApiKey; // SDK uses auth.apiKey for X-API-Key\n    authPayload.platformApiKey = platformApiKey; // backward compat with SDK\n  }\n  if (apiUrl) {\n    authPayload.apiUrl = apiUrl;\n  }\n  setAuth(authPayload);\n}\n\n/**\n * Set SDK auth with JWT (assumes platformApiKey already set via setAuthConfig).\n */\nexport function setSdkAuth(jwt?: string | null): void {\n  const authConfig: { jwt?: string; apiKey?: string; platformApiKey?: string; apiUrl?: string } = {};\n  if (jwt) {\n    authConfig.jwt = jwt;\n  }\n  if (platformApiKey) {\n    authConfig.apiKey = platformApiKey; // SDK uses auth.apiKey for X-API-Key\n    authConfig.platformApiKey = platformApiKey; // backward compat with SDK\n  }\n  if (apiUrl) {\n    authConfig.apiUrl = apiUrl;\n  }\n  setAuth(authConfig);\n}\n\n/**\n * Get the platform API key (set via setAuthConfig).\n */\nexport function getPlatformApiKey(): string {\n  return platformApiKey;\n}\n\n/**\n * Get the optional API URL (set via setAuthConfig).\n */\nexport function getApiUrl(): string | undefined {\n  return apiUrl;\n}\n\n/**\n * Check if a JWT is expired using the payload `exp` claim.\n * @param token - JWT string (assumes 3 segments separated by dots).\n * @param bufferSeconds - Optional buffer in seconds; if exp is within this many seconds of now, treat as expired (default 60).\n * @returns true if token is expired or invalid/not a JWT.\n */\nexport function isJwtExpired(token: string, bufferSeconds = 60): boolean {\n  if (!token || typeof token !== \"string\") return true;\n  const parts = token.split(\".\");\n  if (parts.length !== 3) return true;\n  try {\n    const payload = JSON.parse(atob(parts[1])) as { exp?: number };\n    const exp = payload.exp;\n    if (exp == null || typeof exp !== \"number\") return false;\n    const now = Math.floor(Date.now() / 1000);\n    return exp - bufferSeconds <= now;\n  } catch {\n    return true;\n  }\n}\n","import { createIndexedDBAdapter } from \"cilantro-sdk/helpers\";\n\nexport type DeviceKeyStorage = ReturnType<typeof createIndexedDBAdapter>;\n\nlet storageAdapterInstance: DeviceKeyStorage | null = null;\nlet contextProvidedAdapter: DeviceKeyStorage | null = null;\nlet initializationPromise: Promise<DeviceKeyStorage> | null = null;\n\n/**\n * Set the storage adapter from CilantroProvider (context). When set, getStorageAdapter() returns this.\n */\nexport function setStorageAdapter(adapter: DeviceKeyStorage | null): void {\n  contextProvidedAdapter = adapter;\n}\n\nexport async function initializeStorageAdapter(): Promise<DeviceKeyStorage> {\n  if (contextProvidedAdapter) return contextProvidedAdapter;\n  if (storageAdapterInstance) return storageAdapterInstance;\n  if (initializationPromise) return initializationPromise;\n  if (typeof window === \"undefined\") {\n    throw new Error(\"Storage adapter can only be initialized in browser environment.\");\n  }\n  initializationPromise = (async () => {\n    try {\n      storageAdapterInstance = createIndexedDBAdapter();\n      return storageAdapterInstance;\n    } catch (error) {\n      initializationPromise = null;\n      throw error;\n    }\n  })();\n  return initializationPromise;\n}\n\nexport function getStorageAdapter(): DeviceKeyStorage {\n  if (contextProvidedAdapter) return contextProvidedAdapter;\n  if (storageAdapterInstance) return storageAdapterInstance;\n  if (typeof window === \"undefined\") {\n    throw new Error(\"Storage adapter can only be initialized in browser environment.\");\n  }\n  storageAdapterInstance = createIndexedDBAdapter();\n  return storageAdapterInstance;\n}\n\nexport function getStorageAdapterType(): \"IndexedDB\" | \"LocalStorage\" | \"Memory\" | \"Unknown\" {\n  if (typeof window === \"undefined\") return \"Memory\";\n  if (\"indexedDB\" in window) return \"IndexedDB\";\n  return \"Unknown\";\n}\n\nexport function clearStorageAdapter(): void {\n  storageAdapterInstance = null;\n}\n","\"use client\";\n\nimport { useCilantroContext } from \"../context/CilantroContext\";\nimport type { User, SocialLoginParams } from \"../context/CilantroContext\";\n\nexport interface CilantroAuthContextType {\n  user: User | null;\n  /** JWT token. Alias for token. */\n  jwt: string | null;\n  token: string | null;\n  isAuthenticated: boolean;\n  login: (usernameOrEmail: string, password: string) => Promise<void>;\n  loginWithSocial: (params: SocialLoginParams) => Promise<void>;\n  register: (username: string, email: string, password: string, isActive?: boolean) => Promise<void>;\n  logout: () => void;\n  clearSessionDueToAuthError: () => void;\n  isLoading: boolean;\n}\n\nexport function useCilantroAuth(): CilantroAuthContextType {\n  const ctx = useCilantroContext();\n  return {\n    user: ctx.user,\n    jwt: ctx.jwt,\n    token: ctx.jwt,\n    isAuthenticated: ctx.isAuthenticated,\n    login: (usernameOrEmail: string, password: string) =>\n      ctx.login({ usernameOrEmail, password }),\n    loginWithSocial: ctx.loginWithSocial,\n    register: ctx.register,\n    logout: ctx.logout,\n    clearSessionDueToAuthError: ctx.clearSessionDueToAuthError,\n    isLoading: ctx.isLoading,\n  };\n}\n\nexport type { User };\n","\"use client\";\n\nimport { useState } from \"react\";\nimport { useCilantroAuth } from \"../../providers/CilantroAuthProvider\";\nimport { useReturnUrl } from \"../../hooks/useReturnUrl\";\nimport { Button } from \"../../ui/button\";\nimport { Input } from \"../../ui/input\";\nimport { Label } from \"../../ui/label\";\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"../../ui/card\";\nimport { cn } from \"../../ui/cn\";\nimport { SocialLoginButtons } from \"./SocialLoginButtons\";\n\nexport interface LoginFormClassNames {\n  root?: string;\n  header?: string;\n  title?: string;\n  description?: string;\n  form?: string;\n  label?: string;\n  input?: string;\n  submitButton?: string;\n  error?: string;\n}\n\nexport interface LoginFormProps {\n  className?: string;\n  classNames?: LoginFormClassNames;\n  style?: React.CSSProperties;\n  onSuccess?: () => void;\n  onError?: (error: Error) => void;\n  /** Redirect to this path after successful login. If not set, reads from URL ?returnUrl= (when returnUrlParam provided). */\n  redirectAfterSuccess?: string;\n  /** Custom redirect function for SPA routers. e.g. (path) => router.replace(path) */\n  onRedirect?: (path: string) => void;\n  /** Query param to read returnUrl from URL. Default: \"returnUrl\". Used when redirectAfterSuccess not set. */\n  returnUrlParam?: string;\n  rememberMe?: boolean;\n  /** Override submit button label */\n  submitLabel?: string;\n  /** Override title */\n  title?: string;\n  /** Override description */\n  description?: string;\n  /** Render \"Create account\" link (e.g. to switch to register). Shown below submit. */\n  renderSwitchToRegister?: () => React.ReactNode;\n  /** Enable Google sign-in. Built-in flow is always available, or provide callback. */\n  onGoogleSignIn?: () => Promise<string>;\n  /** Show Google sign-in using built-in flow (always available). */\n  enableGoogleSignIn?: boolean;\n}\n\nexport function LoginForm({\n  className,\n  classNames,\n  style,\n  onSuccess,\n  onError,\n  redirectAfterSuccess,\n  onRedirect,\n  returnUrlParam = \"returnUrl\",\n  submitLabel = \"Sign in\",\n  title = \"Sign in\",\n  description,\n  renderSwitchToRegister,\n  onGoogleSignIn,\n  enableGoogleSignIn,\n}: LoginFormProps) {\n  const { login, isLoading } = useCilantroAuth();\n  const { returnPath } = useReturnUrl({ param: returnUrlParam, defaultPath: \"/\" });\n  const [usernameOrEmail, setUsernameOrEmail] = useState(\"\");\n  const [password, setPassword] = useState(\"\");\n  const [error, setError] = useState<string | null>(null);\n\n  const pathToRedirect = redirectAfterSuccess ?? returnPath;\n\n  const handleSubmit = async (e: React.FormEvent) => {\n    e.preventDefault();\n    setError(null);\n    try {\n      await login(usernameOrEmail.trim(), password);\n      onSuccess?.();\n      if (pathToRedirect) {\n        if (onRedirect) {\n          onRedirect(pathToRedirect);\n        } else if (typeof window !== \"undefined\") {\n          window.location.href = pathToRedirect;\n        }\n      }\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      setError(message);\n      onError?.(err instanceof Error ? err : new Error(message));\n    }\n  };\n\n  return (\n    <Card className={cn(className, classNames?.root)} style={style} data-cilantro-login-form>\n      <CardHeader className={classNames?.header}>\n        <CardTitle className={classNames?.title}>{title}</CardTitle>\n        {description != null && (\n          <CardDescription className={classNames?.description}>{description}</CardDescription>\n        )}\n      </CardHeader>\n      <CardContent>\n        <form onSubmit={handleSubmit} className={cn(\"space-y-4\", classNames?.form)}>\n          {(onGoogleSignIn || enableGoogleSignIn) && (\n            <SocialLoginButtons\n              onGoogleSignIn={onGoogleSignIn}\n              onSuccess={() => {\n                if (pathToRedirect) {\n                  if (onRedirect) onRedirect(pathToRedirect);\n                  else if (typeof window !== \"undefined\") window.location.href = pathToRedirect;\n                }\n              }}\n              showDivider={true}\n            />\n          )}\n          <div className=\"space-y-2\">\n            <Label htmlFor=\"cilantro-login-username\" className={classNames?.label}>\n              Username or email\n            </Label>\n            <Input\n              id=\"cilantro-login-username\"\n              type=\"text\"\n              autoComplete=\"username\"\n              className={classNames?.input}\n              value={usernameOrEmail}\n              onChange={(e) => setUsernameOrEmail(e.target.value)}\n              placeholder=\"Username or email\"\n              required\n              disabled={isLoading}\n            />\n          </div>\n          <div className=\"space-y-2\">\n            <Label htmlFor=\"cilantro-login-password\" className={classNames?.label}>\n              Password\n            </Label>\n            <Input\n              id=\"cilantro-login-password\"\n              type=\"password\"\n              autoComplete=\"current-password\"\n              className={classNames?.input}\n              value={password}\n              onChange={(e) => setPassword(e.target.value)}\n              placeholder=\"Password\"\n              required\n              disabled={isLoading}\n            />\n          </div>\n          {error && (\n            <div\n              className={cn(\n                \"rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive\",\n                classNames?.error\n              )}\n              role=\"alert\"\n            >\n              {error}\n            </div>\n          )}\n          <Button\n            type=\"submit\"\n            size=\"touch\"\n            className={cn(\"w-full\", classNames?.submitButton)}\n            disabled={isLoading || !usernameOrEmail.trim() || !password}\n          >\n            {isLoading ? \"Signing in...\" : submitLabel}\n          </Button>\n          {renderSwitchToRegister && (\n            <div className=\"text-center text-sm text-muted-foreground\">{renderSwitchToRegister()}</div>\n          )}\n        </form>\n      </CardContent>\n    </Card>\n  );\n}\n","\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\n\nexport interface UseReturnUrlOptions {\n  /** Query param name for return URL. Default: \"returnUrl\" */\n  param?: string;\n  /** Default path when no returnUrl in URL. Default: \"/\" */\n  defaultPath?: string;\n  /** Only allow relative paths (no protocol). Default: true */\n  allowRelativeOnly?: boolean;\n}\n\n/**\n * Read returnUrl from URL search params and provide a safe redirect path.\n * Use with LoginForm/RegisterForm to redirect users back after auth.\n *\n * @example\n * // In LoginForm wrapper - redirect to returnUrl after login\n * const { returnPath, redirect } = useReturnUrl();\n * <LoginForm onSuccess={() => redirect()} redirectAfterSuccess={returnPath} onRedirect={router.replace} />\n */\nexport function useReturnUrl(options: UseReturnUrlOptions = {}): {\n  /** Safe path to redirect to (from URL or defaultPath) */\n  returnPath: string;\n  /** Redirect to returnPath using window.location (or pass custom fn) */\n  redirect: (customRedirect?: (path: string) => void) => void;\n  /** Raw value from URL (may be empty) */\n  rawReturnUrl: string | null;\n} {\n  const {\n    param = \"returnUrl\",\n    defaultPath = \"/\",\n    allowRelativeOnly = true,\n  } = options;\n\n  const rawReturnUrl = useMemo(() => {\n    if (typeof window === \"undefined\") return null;\n    const params = new URLSearchParams(window.location.search);\n    return params.get(param);\n  }, [param]);\n\n  const returnPath = useMemo(() => {\n    if (!rawReturnUrl) return defaultPath;\n    if (allowRelativeOnly) {\n      // Reject absolute URLs (protocol-relative or http(s))\n      const trimmed = rawReturnUrl.trim();\n      if (trimmed.startsWith(\"//\") || /^https?:\\/\\//i.test(trimmed)) {\n        return defaultPath;\n      }\n      if (trimmed.startsWith(\"/\")) return trimmed;\n      // Relative path without leading slash - make absolute\n      return `/${trimmed}`;\n    }\n    return rawReturnUrl;\n  }, [rawReturnUrl, defaultPath, allowRelativeOnly]);\n\n  const redirect = useCallback(\n    (customRedirect?: (path: string) => void) => {\n      const path = returnPath;\n      if (customRedirect) {\n        customRedirect(path);\n      } else if (typeof window !== \"undefined\") {\n        window.location.href = path;\n      }\n    },\n    [returnPath]\n  );\n\n  return { returnPath, redirect, rawReturnUrl };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./cn\";\n\nconst buttonVariants = cva(\n  \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n        destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n        outline: \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n        secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n        ghost: \"hover:bg-accent hover:text-accent-foreground\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n      size: {\n        default: \"h-10 px-4 py-2\",\n        sm: \"h-9 rounded-md px-3\",\n        lg: \"h-11 rounded-md px-8\",\n        icon: \"h-10 w-10\",\n        touch: \"h-12 min-h-[44px] min-w-[44px] rounded-md px-4\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n);\n\nexport interface ButtonProps\n  extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n    VariantProps<typeof buttonVariants> {\n  asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, variant, size, asChild = false, ...props }, ref) => {\n    const Comp = asChild ? Slot : \"button\";\n    return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;\n  }\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]): string {\n  return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"./cn\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}\n\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n  ({ className, type, ...props }, ref) => {\n    return (\n      <input\n        type={type}\n        className={cn(\n          \"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n          className\n        )}\n        ref={ref}\n        {...props}\n      />\n    );\n  }\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n","\"use client\";\n\nimport * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"./cn\";\n\nconst labelVariants = cva(\n  \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n);\n\nconst Label = React.forwardRef<\n  React.ElementRef<typeof LabelPrimitive.Root>,\n  React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n  <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"./cn\";\n\nconst Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\"rounded-xl border bg-card text-card-foreground shadow\", className)}\n      {...props}\n    />\n  )\n);\nCard.displayName = \"Card\";\n\nconst CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div ref={ref} className={cn(\"flex flex-col space-y-1.5 p-6\", className)} {...props} />\n  )\n);\nCardHeader.displayName = \"CardHeader\";\n\nconst CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(\n  ({ className, ...props }, ref) => (\n    <h3\n      ref={ref}\n      className={cn(\"font-semibold leading-none tracking-tight\", className)}\n      {...props}\n    />\n  )\n);\nCardTitle.displayName = \"CardTitle\";\n\nconst CardDescription = React.forwardRef<\n  HTMLParagraphElement,\n  React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n  <p ref={ref} className={cn(\"text-sm text-muted-foreground\", className)} {...props} />\n));\nCardDescription.displayName = \"CardDescription\";\n\nconst CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n  )\n);\nCardContent.displayName = \"CardContent\";\n\nconst CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n  ({ className, ...props }, ref) => (\n    <div ref={ref} className={cn(\"flex items-center p-6 pt-0\", className)} {...props} />\n  )\n);\nCardFooter.displayName = \"CardFooter\";\n\nexport { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };\n","\"use client\";\n\nimport { useState, useCallback } from \"react\";\nimport { GoogleLogin } from \"@react-oauth/google\";\nimport { getDiscordAuthorizeUrl } from \"cilantro-sdk/auth\";\nimport { useAuth } from \"../../hooks/useAuth\";\nimport { useCilantroConfig } from \"../../hooks/useCilantroConfig\";\nimport { Button } from \"../../ui/button\";\nimport { cn } from \"../../ui/cn\";\n\nexport type SocialProvider = \"google\" | \"apple\" | \"discord\";\n\nexport interface SocialLoginButtonsClassNames {\n  root?: string;\n  divider?: string;\n  button?: string;\n  googleButton?: string;\n  appleButton?: string;\n  discordButton?: string;\n  error?: string;\n}\n\nexport interface SocialLoginButtonsProps {\n  /** Called when user clicks Google. Return the id_token from Google Sign-In. */\n  onGoogleSignIn?: () => Promise<string>;\n  /** Called when user clicks Apple. Return the id_token from Apple Sign-In. */\n  onAppleSignIn?: () => Promise<string>;\n  /** Called when user clicks Discord. Return the OAuth access_token from Discord (required by SDK). */\n  onDiscordSignIn?: () => Promise<string>;\n  /** Called after successful social login (e.g. for redirect). */\n  onSuccess?: () => void;\n  /** Show Google sign-in. Default: true */\n  showGoogle?: boolean;\n  /** Show Apple sign-in. Default: false */\n  showApple?: boolean;\n  /** Show Discord sign-in. Use built-in redirect (set discordRedirectUri in CilantroProvider socialAuth) or provide onDiscordSignIn. Default: false */\n  showDiscord?: boolean;\n  className?: string;\n  classNames?: SocialLoginButtonsClassNames;\n  /** Optional divider between social and email/password form */\n  showDivider?: boolean;\n}\n\n/**\n * Social sign-in buttons (Google, Apple, Discord). Integrates with loginWithSocial.\n * Matches cilantro-sdk SocialLoginDto: Google/Apple use id_token, Discord uses access_token.\n */\nexport function SocialLoginButtons({\n  onGoogleSignIn,\n  onAppleSignIn,\n  onDiscordSignIn,\n  onSuccess,\n  showGoogle = true,\n  showApple = false,\n  showDiscord = false,\n  className,\n  classNames,\n  showDivider = true,\n}: SocialLoginButtonsProps) {\n  const { loginWithSocial } = useAuth();\n  const { config } = useCilantroConfig();\n  const [loading, setLoading] = useState<SocialProvider | null>(null);\n  const [error, setError] = useState<string | null>(null);\n\n  const sa = config.socialAuth;\n  const hasBuiltInGoogle = showGoogle && sa?.googleClientId && !onGoogleSignIn;\n  const hasCallbackGoogle = showGoogle && onGoogleSignIn;\n  const hasApple = showApple && onAppleSignIn;\n  const hasBuiltInDiscord = showDiscord && sa?.discordRedirectUri && !onDiscordSignIn;\n  const hasCallbackDiscord = showDiscord && onDiscordSignIn;\n  const hasDiscord = hasBuiltInDiscord || hasCallbackDiscord;\n  const hasProviders = hasBuiltInGoogle || hasCallbackGoogle || hasApple || hasDiscord;\n\n  const handleIdTokenLogin = async (\n    getToken: (() => Promise<string>) | undefined,\n    provider: \"google\" | \"apple\"\n  ) => {\n    if (!getToken) {\n      setError(`Sign in with ${provider} is not configured.`);\n      return;\n    }\n    setError(null);\n    setLoading(provider);\n    try {\n      const id_token = await getToken();\n      if (id_token) {\n        await loginWithSocial({ provider, id_token });\n        onSuccess?.();\n      }\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      setError(message);\n    } finally {\n      setLoading(null);\n    }\n  };\n\n  const handleDiscordClick = useCallback(async () => {\n    if (onDiscordSignIn) {\n      setError(null);\n      setLoading(\"discord\");\n      try {\n        const access_token = await onDiscordSignIn();\n        if (access_token) {\n          await loginWithSocial({ provider: \"discord\", access_token });\n          onSuccess?.();\n        }\n      } catch (err) {\n        const message = err instanceof Error ? err.message : String(err);\n        setError(message);\n      } finally {\n        setLoading(null);\n      }\n      return;\n    }\n    setError(\"Sign in with Discord is not configured.\");\n  }, [onDiscordSignIn, loginWithSocial, onSuccess]);\n\n  const handleDiscordRedirect = useCallback(async () => {\n    const clientRedirectUri = sa?.discordRedirectUri;\n    if (!clientRedirectUri) {\n      setError(\"Sign in with Discord is not configured (missing discordRedirectUri in socialAuth).\");\n      return;\n    }\n    setError(null);\n    setLoading(\"discord\");\n    try {\n      const result = await getDiscordAuthorizeUrl({ clientRedirectUri });\n      const authorizeUrl = typeof result === \"object\" && result !== null && \"authorizeUrl\" in result\n        ? String((result as { authorizeUrl: string }).authorizeUrl)\n        : \"\";\n      if (authorizeUrl) {\n        window.location.href = authorizeUrl;\n      } else {\n        setError(\"Discord sign-in: no authorize URL returned.\");\n        setLoading(null);\n      }\n    } catch (err) {\n      const message = err instanceof Error ? err.message : String(err);\n      setError(message);\n      setLoading(null);\n    }\n  }, [sa?.discordRedirectUri]);\n\n  const handleGoogleSuccess = useCallback(\n    async (credentialResponse: { credential?: string }) => {\n      const id_token = credentialResponse.credential;\n      if (!id_token) return;\n      setError(null);\n      setLoading(\"google\");\n      try {\n        await loginWithSocial({ provider: \"google\", id_token });\n        onSuccess?.();\n      } catch (err) {\n        const message = err instanceof Error ? err.message : String(err);\n        setError(message);\n      } finally {\n        setLoading(null);\n      }\n    },\n    [loginWithSocial, onSuccess]\n  );\n\n  if (!hasProviders) return null;\n\n  return (\n    <div className={cn(\"space-y-3\", className, classNames?.root)} data-cilantro-social-login>\n      {showDivider && (\n        <div className={cn(\"relative\", classNames?.divider)}>\n          <div className=\"absolute inset-0 flex items-center\">\n            <span className=\"w-full border-t\" />\n          </div>\n          <div className=\"relative flex justify-center text-xs uppercase\">\n            <span className=\"bg-background px-2 text-muted-foreground\">Or continue with</span>\n          </div>\n        </div>\n      )}\n      <div className=\"flex flex-col gap-2\">\n        {hasBuiltInGoogle && (\n          <div className={cn(\"w-full [&>div]:!w-full [&>div]:!justify-center\", classNames?.googleButton)} data-cilantro-social-google>\n            <GoogleLogin\n              onSuccess={handleGoogleSuccess}\n              onError={() => setError(\"Google sign-in failed\")}\n              theme=\"outline\"\n              size=\"large\"\n              width=\"100%\"\n            />\n          </div>\n        )}\n        {hasCallbackGoogle && (\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            className={cn(\"w-full\", classNames?.button, classNames?.googleButton)}\n            disabled={!!loading}\n            onClick={() => handleIdTokenLogin(onGoogleSignIn, \"google\")}\n            data-cilantro-social-google\n          >\n            {loading === \"google\" ? (\n              <span className=\"flex items-center gap-2\">\n                <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n                Signing in...\n              </span>\n            ) : (\n              \"Sign in with Google\"\n            )}\n          </Button>\n        )}\n        {hasApple && (\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            className={cn(\"w-full\", classNames?.button, classNames?.appleButton)}\n            disabled={!!loading}\n            onClick={() => handleIdTokenLogin(onAppleSignIn, \"apple\")}\n            data-cilantro-social-apple\n          >\n            {loading === \"apple\" ? (\n              <span className=\"flex items-center gap-2\">\n                <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n                Signing in...\n              </span>\n            ) : (\n              \"Sign in with Apple\"\n            )}\n          </Button>\n        )}\n        {hasDiscord && (\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            className={cn(\"w-full\", classNames?.button, classNames?.discordButton)}\n            disabled={!!loading}\n            onClick={hasCallbackDiscord ? handleDiscordClick : handleDiscordRedirect}\n            data-cilantro-social-discord\n          >\n            {loading === \"discord\" ? (\n              <span className=\"flex items-center gap-2\">\n                <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n                Signing in...\n              </span>\n            ) : (\n              \"Sign in with Discord\"\n            )}\n          </Button>\n        )}\n      </div>\n      {error && (\n        <div\n          className={cn(\n            \"rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-sm text-destructive\",\n            classNames?.error\n          )}\n          role=\"alert\"\n        >\n          {error}\n        </div>\n      )}\n    </div>\n  );\n}\n","\"use client\";\n\nimport { useCilantroContext } from \"../context/CilantroContext\";\nimport type { User, SocialLoginParams } from \"../context/CilantroContext\";\n\nexport interface UseAuthResult {\n  /** Current user (from JWT payload or login response). */\n  user: User | null;\n  /** JWT token. Prefer this name. */\n  jwt: string | null;\n  /** Alias for jwt. Same value. */\n  token: string | null;\n  isLoading: boolean;\n  isAuthenticated: boolean;\n  login: (params: { usernameOrEmail: string; password: string }) => Promise<void>;\n  /** Sign in with Google/Apple (id_token) or Discord (access_token). Matches cilantro-sdk SocialLoginDto. */\n  loginWithSocial: (params: SocialLoginParams) => Promise<void>;\n  /** Register a new user and auto-login. */\n  register: (\n    username: string,\n    email: string,\n    password: string,\n    isActive?: boolean\n  ) => Promise<void>;\n  logout: () => void;\n  /** Clear session when API returns 401/403. Call onSessionExpired. */\n  clearSessionDueToAuthError: () => void;\n}\n\nexport function useAuth(): UseAuthResult {\n  const ctx = useCilantroContext();\n  return {\n    user: ctx.user,\n    jwt: ctx.jwt,\n    token: ctx.jwt,\n    isLoading: ctx.isLoading,\n    isAuthenticated: ctx.isAuthenticated,\n    login: ctx.login,\n    loginWithSocial: ctx.loginWithSocial,\n    register: ctx.register,\n    logout: ctx.logout,\n    clearSessionDueToAuthError: ctx.clearSessionDueToAuthError,\n  };\n}\n","\"use client\";\n\nimport { useCilantroContext } from \"../context/CilantroContext\";\nimport type { CilantroConfig } from \"../context/CilantroContext\";\nimport type { DeviceKeyStorage } from \"../core/storage-adapter\";\n\nexport interface UseCilantroConfigResult {\n  config: CilantroConfig;\n  storage: DeviceKeyStorage | null;\n}\n\nexport function useCilantroConfig(): UseCilantroConfigResult {\n  const ctx = useCilantroContext();\n  return {\n    config: ctx.config,\n    storage: ctx.storage,\n  };\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"./cn\";\n\nexport interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {}\n\nconst Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(\n  ({ className, ...props }, ref) => (\n    <div\n      ref={ref}\n      className={cn(\"rounded-md bg-muted animate-pulse\", className)}\n      {...props}\n    />\n  )\n);\nSkeleton.displayName = \"Skeleton\";\n\nexport { Skeleton };\n"],"mappings":"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","names":["useMemo","useState","useCallback","useMemo","jsx","React","jsx","React","cva","jsx","cva","React","jsx","useState","useCallback","jsx","useState","useCallback","jsx","jsxs","useState","React","jsx","jsx","jsxs","useMemo","jsx"]}