import { createContext, createSignal, useContext, onMount, Accessor, JSX } from 'solid-js'; import { getClerk, loadClerk, getClerkUrls } from './clerk-client'; import { User, UserRole, AuthState } from './types'; type ClerkUser = any; interface ClerkSession { getId: () => string; getUser: () => ClerkUser; } interface ClerkClient { user: () => ClerkUser | null; session: () => ClerkSession | null; isLoading: boolean; signOut: () => Promise; } const AuthContext = createContext | undefined>(undefined); const AuthActionsContext = createContext<{ signIn: () => void; signOut: () => Promise; updateUser: (data: Partial) => Promise; clerkClient: Accessor; } | undefined>(undefined); export { AuthContext, AuthActionsContext }; function clerkUserToUser(clerkUser: ClerkUser): User { const primaryEmail = clerkUser.primaryEmailAddress?.emailAddress || ''; const firstName = clerkUser.firstName || ''; const lastName = clerkUser.lastName || ''; const name = [firstName, lastName].filter(Boolean).join(' ') || primaryEmail.split('@')[0] || 'User'; return { id: clerkUser.id, email: primaryEmail, name, avatarUrl: clerkUser.imageUrl, role: 'owner' as UserRole, }; } export function ClerkProvider(props: { children: JSX.Element }) { const [state, setState] = createSignal({ user: null, isLoading: true, isAuthenticated: false, error: null, }); const [clerkClient, setClerkClient] = createSignal(null); onMount(async () => { try { const client = await loadClerk(); if (!client) { setState({ user: null, isLoading: false, isAuthenticated: false, error: 'Authentication service unavailable', }); return; } const wrappedClient: ClerkClient = { user: () => client.user, session: () => (client.session as any) || null, isLoading: false, signOut: async () => { await client.signOut(); setState({ user: null, isLoading: false, isAuthenticated: false, error: null, }); }, }; setClerkClient(wrappedClient); if (client.user) { setState({ user: clerkUserToUser(client.user), isLoading: false, isAuthenticated: true, error: null, }); } else { setState((prev) => ({ ...prev, isLoading: false })); } } catch (err) { setState({ user: null, isLoading: false, isAuthenticated: false, error: err instanceof Error ? err.message : 'Failed to initialize auth', }); } }); const signIn = () => { const urls = getClerkUrls(); window.location.href = urls.signInUrl; }; const signOut = async () => { const client = getClerk(); if (client) { await client.signOut(); } setState({ user: null, isLoading: false, isAuthenticated: false, error: null, }); }; const updateUser = async (data: Partial) => { setState((prev) => ({ ...prev, user: prev.user ? { ...prev.user, ...data } : null, })); }; return ( {props.children} ); } export function useAuth(): Accessor { const context = useContext(AuthContext); if (!context) { throw new Error('useAuth must be used within a ClerkProvider'); } return context; } export function useAuthActions() { const context = useContext(AuthActionsContext); if (!context) { throw new Error('useAuthActions must be used within a ClerkProvider'); } return context; } export function requireAuth() { const auth = useAuth(); const authState = auth(); if (!authState.isAuthenticated) { throw new Error('Authentication required'); } return authState.user!; }