@insforge/react 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.d.mts +2 -2
- package/dist/components.d.ts +2 -2
- package/dist/components.js +2 -24
- package/dist/components.js.map +1 -1
- package/dist/components.mjs +2 -24
- package/dist/components.mjs.map +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.js +4 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -33
- package/dist/index.mjs.map +1 -1
- package/dist/router.d.mts +25 -41
- package/dist/router.d.ts +25 -41
- package/dist/router.js +2 -107
- package/dist/router.js.map +1 -1
- package/dist/router.mjs +4 -109
- package/dist/router.mjs.map +1 -1
- package/dist/types.d.mts +0 -4
- package/dist/types.d.ts +0 -4
- package/package.json +1 -1
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;;AAyEA,IAAM,eAAA,GAAkBA,mBAAA;AAAA,EACtB;AACF,CAAA;AA6fO,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAUC,iBAAW,eAAe,CAAA;AAC1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,OAAA;AACT;;;AC1iBO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIC,eAA6C,IAAI,CAAA;AACvF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE9C,EAAAC,gBAAU,MAAM;AACd,IAAA,eAAe,WAAA,GAAc;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,EAAoB;AACzC,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,cAAA,CAAe,MAAM,CAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,MACrB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,aAAa,QAAA,EAAS;AACjC","file":"hooks.js","sourcesContent":["\"use client\";\r\n\r\nimport {\r\n createContext,\r\n useContext,\r\n useEffect,\r\n useState,\r\n useCallback,\r\n useRef,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { createClient } from \"@insforge/sdk\";\r\nimport type { InsforgeUser } from \"../types\";\r\nimport { GetPublicAuthConfigResponse } from \"@insforge/shared-schemas\";\r\n\r\ninterface InsforgeContextValue {\r\n // Auth state\r\n user: InsforgeUser | null;\r\n isLoaded: boolean;\r\n isSignedIn: boolean;\r\n\r\n // Auth methods\r\n setUser: (user: InsforgeUser | null) => void;\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signOut: () => Promise<void>;\r\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\r\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\r\n \r\n // Callback handling for OAuth and email/password redirects\r\n handleAuthCallback: (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }) => Promise<{ success: boolean; error?: string }>;\r\n\r\n // Email verification methods\r\n sendPasswordResetCode: (\r\n email: string\r\n ) => Promise<{ success: boolean; message: string } | null>;\r\n resetPassword: (\r\n token: string,\r\n newPassword: string\r\n ) => Promise<{ message: string; redirectTo?: string } | null>;\r\n verifyEmail: (\r\n token: string\r\n ) => Promise<{ accessToken: string; user?: any } | null>;\r\n\r\n // Public auth config\r\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\r\n // Base config\r\n baseUrl: string;\r\n}\r\n\r\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\r\n undefined\r\n);\r\n\r\nexport interface InsforgeProviderProps {\r\n children: ReactNode;\r\n baseUrl: string;\r\n onAuthChange?: (user: InsforgeUser | null) => void;\r\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\r\n syncTokenToCookie?: (token: string) => Promise<boolean>;\r\n clearCookie?: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Unified Insforge Provider - manages authentication state and configuration\r\n *\r\n * Manages user authentication state and provides all necessary context to child components.\r\n * Works with any React framework (Next.js, Vite, Remix, etc.).\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { InsforgeProvider } from '@insforge/react';\r\n *\r\n * export default function App() {\r\n * return (\r\n * <InsforgeProvider baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\r\n * {children}\r\n * </InsforgeProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <InsforgeProvider\r\n * baseUrl={baseUrl}\r\n * syncTokenToCookie={async (token) => {\r\n * await fetch('/api/auth', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ token })\r\n * });\r\n * return true;\r\n * }}\r\n * clearCookie={async () => {\r\n * await fetch('/api/auth', { method: 'DELETE' });\r\n * }}\r\n * >\r\n * {children}\r\n * </InsforgeProvider>\r\n * ```\r\n */\r\nexport function InsforgeProvider({\r\n children,\r\n baseUrl,\r\n onAuthChange,\r\n syncTokenToCookie,\r\n clearCookie,\r\n}: InsforgeProviderProps) {\r\n // Auth state\r\n const [user, setUser] = useState<InsforgeUser | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n // Initialize SDK client with lazy initialization - only runs once\r\n const [insforge] = useState(() => createClient({ baseUrl }));\r\n\r\n // Load auth state - returns explicit success/error status\r\n const loadAuthState = useCallback(async (): Promise<{\r\n success: boolean;\r\n error?: string;\r\n }> => {\r\n try {\r\n // Use SDK's getCurrentSession() to check for existing session\r\n const sessionResult = insforge.auth.getCurrentSession();\r\n const session = sessionResult.data?.session;\r\n const token = session?.accessToken || null;\r\n\r\n if (!token) {\r\n // No token, user is not authenticated\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"no_session\" };\r\n }\r\n\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n // Token is valid, update user state with fresh data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n setIsLoaded(true);\r\n return { success: true };\r\n } else {\r\n // Token invalid or expired\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"invalid_token\" };\r\n }\r\n } catch (error) {\r\n // Token validation failed\r\n console.error(\"[InsforgeProvider] Token validation failed:\", error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : \"authentication_failed\",\r\n };\r\n }\r\n }, [insforge, onAuthChange, syncTokenToCookie, clearCookie]);\r\n\r\n useEffect(() => {\r\n // Run loadAuthState only once on mount\r\n loadAuthState();\r\n\r\n return () => {\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n };\r\n }, []); // Empty deps - run only on mount\r\n\r\n const getPublicAuthConfig = useCallback(async () => {\r\n try {\r\n const result = await insforge.auth.getPublicAuthConfig();\r\n if (result.data) {\r\n return result.data;\r\n } else {\r\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error(\r\n \"[InsforgeProvider] Failed to get public auth config:\",\r\n error\r\n );\r\n return null;\r\n }\r\n }, [insforge]);\r\n\r\n /**\r\n * Helper function to handle successful authentication\r\n */\r\n const handleAuthSuccess = useCallback(\r\n async (\r\n authToken: string,\r\n fallbackUser?: { id?: string; email?: string; name?: string }\r\n ) => {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Try to sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(authToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay\r\n }\r\n }\r\n } else if (fallbackUser) {\r\n // Fallback to basic user data if getCurrentUser fails\r\n const userData: InsforgeUser = {\r\n id: fallbackUser.id || \"\",\r\n email: fallbackUser.email || \"\",\r\n name: fallbackUser.name || \"\",\r\n avatarUrl: \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie]\r\n );\r\n\r\n /**\r\n * Handle authentication callback from OAuth or email/password redirects\r\n * This is specifically for callback pages - saves token and verifies authentication\r\n */\r\n const handleAuthCallback = useCallback(\r\n async (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }): Promise<{ success: boolean; error?: string }> => {\r\n try {\r\n // Save the token to localStorage using SDK's setSession\r\n await insforge.auth.setSession({\r\n accessToken: params.accessToken,\r\n user: {\r\n id: params.userId || '',\r\n email: params.email || '',\r\n name: params.name || '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n },\r\n });\r\n\r\n // Now verify the token and get full user data\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (!userResult.data) {\r\n // Token is invalid\r\n await insforge.auth.signOut();\r\n \r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n return { success: false, error: 'invalid_token' };\r\n }\r\n\r\n // Update user state with full profile data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || params.name || '',\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(params.accessToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay, continue\r\n }\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n // Authentication failed\r\n console.error('[InsforgeProvider] Auth callback failed:', error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'authentication_failed',\r\n };\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie, clearCookie]\r\n );\r\n\r\n const signIn = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signInWithPassword({\r\n email,\r\n password,\r\n });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage =\r\n sdkResult.error?.message || \"Invalid email or password\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signUp = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signUp({ email, password });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage = sdkResult.error?.message || \"Sign up failed\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signOut = useCallback(async () => {\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n // Clear refresh interval if exists\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n }, [insforge, onAuthChange, clearCookie]);\r\n\r\n const updateUser = useCallback(\r\n async (data: Partial<InsforgeUser>) => {\r\n if (!user) throw new Error(\"No user signed in\");\r\n\r\n const profileUpdate: Record<string, any> = {\r\n nickname: data.name,\r\n avatarUrl: data.avatarUrl,\r\n };\r\n\r\n const result = await insforge.auth.setProfile(profileUpdate);\r\n\r\n if (result.data) {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const updatedUser: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n setUser(updatedUser);\r\n if (onAuthChange) {\r\n onAuthChange(updatedUser);\r\n }\r\n }\r\n }\r\n },\r\n [user, onAuthChange, insforge]\r\n );\r\n\r\n const sendPasswordResetCode = useCallback(\r\n async (email: string) => {\r\n const sdkResult = await insforge.auth.sendPasswordResetCode({ email });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const resetPassword = useCallback(\r\n async (token: string, newPassword: string) => {\r\n const sdkResult = await insforge.auth.resetPassword({\r\n newPassword,\r\n otp: token,\r\n });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const verifyEmail = useCallback(\r\n async (token: string) => {\r\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n return (\r\n <InsforgeContext.Provider\r\n value={{\r\n user,\r\n isLoaded,\r\n isSignedIn: !!user,\r\n setUser,\r\n signIn,\r\n signUp,\r\n signOut,\r\n updateUser,\r\n reloadAuth: loadAuthState,\r\n handleAuthCallback,\r\n baseUrl,\r\n sendPasswordResetCode,\r\n resetPassword,\r\n verifyEmail,\r\n getPublicAuthConfig,\r\n }}\r\n >\r\n {children}\r\n </InsforgeContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Insforge context\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useInsforge();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useInsforge(): InsforgeContextValue {\r\n const context = useContext(InsforgeContext);\r\n if (!context) {\r\n throw new Error(\"useInsforge must be used within InsforgeProvider\");\r\n }\r\n return context;\r\n}\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n * \r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n * \r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n * \r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth() {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access current user data\r\n * \r\n * @returns Object containing:\r\n * - `user`: Current user object (InsforgeUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data\r\n * - `setUser`: Internal function to manually set user state\r\n * \r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n * \r\n * async function handleUpdate(name: string) {\r\n * await updateUser({ name });\r\n * }\r\n * \r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user, isLoaded, updateUser, setUser } = useInsforge();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n\r\n","import { useState, useEffect } from 'react';\r\nimport type {\r\n GetPublicAuthConfigResponse\r\n} from '@insforge/shared-schemas';\r\nimport { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\r\n * - `emailConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { oauthProviders, emailConfig, isLoaded } = usePublicAuthConfig();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {oauthProviders.length}</p>\r\n * <p>Password min length: {emailConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within InsforgeProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n emailConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useInsforge();\r\n const [emailConfig, setEmailConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setEmailConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setEmailConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { emailConfig, isLoaded };\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;;AAyEA,IAAM,eAAA,GAAkBA,mBAAA;AAAA,EACtB;AACF,CAAA;AA4fO,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAUC,iBAAW,eAAe,CAAA;AAC1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,OAAA;AACT;;;ACziBO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIC,eAA6C,IAAI,CAAA;AACvF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAAS,KAAK,CAAA;AAE9C,EAAAC,gBAAU,MAAM;AACd,IAAA,eAAe,WAAA,GAAc;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,EAAoB;AACzC,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,cAAA,CAAe,MAAM,CAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,MACrB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,aAAa,QAAA,EAAS;AACjC","file":"hooks.js","sourcesContent":["\"use client\";\r\n\r\nimport {\r\n createContext,\r\n useContext,\r\n useEffect,\r\n useState,\r\n useCallback,\r\n useRef,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { createClient } from \"@insforge/sdk\";\r\nimport type { InsforgeUser } from \"../types\";\r\nimport { GetPublicAuthConfigResponse } from \"@insforge/shared-schemas\";\r\n\r\ninterface InsforgeContextValue {\r\n // Auth state\r\n user: InsforgeUser | null;\r\n isLoaded: boolean;\r\n isSignedIn: boolean;\r\n\r\n // Auth methods\r\n setUser: (user: InsforgeUser | null) => void;\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signOut: () => Promise<void>;\r\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\r\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\r\n \r\n // Callback handling for OAuth and email/password redirects\r\n handleAuthCallback: (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }) => Promise<{ success: boolean; error?: string }>;\r\n\r\n // Email verification methods\r\n sendPasswordResetCode: (\r\n email: string\r\n ) => Promise<{ success: boolean; message: string } | null>;\r\n resetPassword: (\r\n token: string,\r\n newPassword: string\r\n ) => Promise<{ message: string; redirectTo?: string } | null>;\r\n verifyEmail: (\r\n token: string\r\n ) => Promise<{ accessToken: string; user?: any } | null>;\r\n\r\n // Public auth config\r\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\r\n // Base config\r\n baseUrl: string;\r\n}\r\n\r\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\r\n undefined\r\n);\r\n\r\nexport interface InsforgeProviderProps {\r\n children: ReactNode;\r\n baseUrl: string;\r\n onAuthChange?: (user: InsforgeUser | null) => void;\r\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\r\n syncTokenToCookie?: (token: string) => Promise<boolean>;\r\n clearCookie?: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Unified Insforge Provider - manages authentication state and configuration\r\n *\r\n * Manages user authentication state and provides all necessary context to child components.\r\n * Works with any React framework (Next.js, Vite, Remix, etc.).\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { InsforgeProvider } from '@insforge/react';\r\n *\r\n * export default function App() {\r\n * return (\r\n * <InsforgeProvider baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\r\n * {children}\r\n * </InsforgeProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <InsforgeProvider\r\n * baseUrl={baseUrl}\r\n * syncTokenToCookie={async (token) => {\r\n * await fetch('/api/auth', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ token })\r\n * });\r\n * return true;\r\n * }}\r\n * clearCookie={async () => {\r\n * await fetch('/api/auth', { method: 'DELETE' });\r\n * }}\r\n * >\r\n * {children}\r\n * </InsforgeProvider>\r\n * ```\r\n */\r\nexport function InsforgeProvider({\r\n children,\r\n baseUrl,\r\n onAuthChange,\r\n syncTokenToCookie,\r\n clearCookie,\r\n}: InsforgeProviderProps) {\r\n // Auth state\r\n const [user, setUser] = useState<InsforgeUser | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n // Initialize SDK client with lazy initialization - only runs once\r\n const [insforge] = useState(() => createClient({ baseUrl }));\r\n\r\n // Load auth state - returns explicit success/error status\r\n const loadAuthState = useCallback(async (): Promise<{\r\n success: boolean;\r\n error?: string;\r\n }> => {\r\n try {\r\n // Use SDK's getCurrentSession() to check for existing session\r\n const sessionResult = insforge.auth.getCurrentSession();\r\n const session = sessionResult.data?.session;\r\n const token = session?.accessToken || null;\r\n\r\n if (!token) {\r\n // No token, user is not authenticated\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"no_session\" };\r\n }\r\n\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n // Token is valid, update user state with fresh data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n setIsLoaded(true);\r\n return { success: true };\r\n } else {\r\n // Token invalid or expired\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"invalid_token\" };\r\n }\r\n } catch (error) {\r\n // Token validation failed\r\n console.error(\"[InsforgeProvider] Token validation failed:\", error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : \"authentication_failed\",\r\n };\r\n }\r\n }, [insforge, onAuthChange, syncTokenToCookie, clearCookie]);\r\n\r\n useEffect(() => {\r\n // Run loadAuthState only once on mount\r\n loadAuthState();\r\n\r\n return () => {\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n };\r\n }, []); // Empty deps - run only on mount\r\n\r\n const getPublicAuthConfig = useCallback(async () => {\r\n try {\r\n const result = await insforge.auth.getPublicAuthConfig();\r\n if (result.data) {\r\n return result.data;\r\n } else {\r\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error(\r\n \"[InsforgeProvider] Failed to get public auth config:\",\r\n error\r\n );\r\n return null;\r\n }\r\n }, [insforge]);\r\n\r\n /**\r\n * Helper function to handle successful authentication\r\n */\r\n const handleAuthSuccess = useCallback(\r\n async (\r\n authToken: string,\r\n fallbackUser?: { id?: string; email?: string; name?: string }\r\n ) => {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Try to sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(authToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay\r\n }\r\n }\r\n } else if (fallbackUser) {\r\n // Fallback to basic user data if getCurrentUser fails\r\n const userData: InsforgeUser = {\r\n id: fallbackUser.id || \"\",\r\n email: fallbackUser.email || \"\",\r\n name: fallbackUser.name || \"\",\r\n avatarUrl: \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie]\r\n );\r\n\r\n /**\r\n * Handle authentication callback from OAuth or email/password redirects\r\n * This is specifically for callback pages - saves token and verifies authentication\r\n */\r\n const handleAuthCallback = useCallback(\r\n async (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }): Promise<{ success: boolean; error?: string }> => {\r\n try {\r\n await insforge.auth.setSession({\r\n accessToken: params.accessToken,\r\n user: {\r\n id: params.userId || '',\r\n email: params.email || '',\r\n name: params.name || '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n },\r\n });\r\n\r\n // Now verify the token and get full user data\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (!userResult.data) {\r\n // Token is invalid\r\n await insforge.auth.signOut();\r\n \r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n return { success: false, error: 'invalid_token' };\r\n }\r\n\r\n // Update user state with full profile data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || params.name || '',\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(params.accessToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay, continue\r\n }\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n // Authentication failed\r\n console.error('[InsforgeProvider] Auth callback failed:', error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'authentication_failed',\r\n };\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie, clearCookie]\r\n );\r\n\r\n const signIn = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signInWithPassword({\r\n email,\r\n password,\r\n });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage =\r\n sdkResult.error?.message || \"Invalid email or password\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signUp = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signUp({ email, password });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage = sdkResult.error?.message || \"Sign up failed\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signOut = useCallback(async () => {\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n // Clear refresh interval if exists\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n }, [insforge, onAuthChange, clearCookie]);\r\n\r\n const updateUser = useCallback(\r\n async (data: Partial<InsforgeUser>) => {\r\n if (!user) throw new Error(\"No user signed in\");\r\n\r\n const profileUpdate: Record<string, any> = {\r\n nickname: data.name,\r\n avatarUrl: data.avatarUrl,\r\n };\r\n\r\n const result = await insforge.auth.setProfile(profileUpdate);\r\n\r\n if (result.data) {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const updatedUser: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n setUser(updatedUser);\r\n if (onAuthChange) {\r\n onAuthChange(updatedUser);\r\n }\r\n }\r\n }\r\n },\r\n [user, onAuthChange, insforge]\r\n );\r\n\r\n const sendPasswordResetCode = useCallback(\r\n async (email: string) => {\r\n const sdkResult = await insforge.auth.sendPasswordResetCode({ email });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const resetPassword = useCallback(\r\n async (token: string, newPassword: string) => {\r\n const sdkResult = await insforge.auth.resetPassword({\r\n newPassword,\r\n otp: token,\r\n });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const verifyEmail = useCallback(\r\n async (token: string) => {\r\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n return (\r\n <InsforgeContext.Provider\r\n value={{\r\n user,\r\n isLoaded,\r\n isSignedIn: !!user,\r\n setUser,\r\n signIn,\r\n signUp,\r\n signOut,\r\n updateUser,\r\n reloadAuth: loadAuthState,\r\n handleAuthCallback,\r\n baseUrl,\r\n sendPasswordResetCode,\r\n resetPassword,\r\n verifyEmail,\r\n getPublicAuthConfig,\r\n }}\r\n >\r\n {children}\r\n </InsforgeContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Insforge context\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useInsforge();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useInsforge(): InsforgeContextValue {\r\n const context = useContext(InsforgeContext);\r\n if (!context) {\r\n throw new Error(\"useInsforge must be used within InsforgeProvider\");\r\n }\r\n return context;\r\n}\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n * \r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n * \r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n * \r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth() {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access current user data\r\n * \r\n * @returns Object containing:\r\n * - `user`: Current user object (InsforgeUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data\r\n * - `setUser`: Internal function to manually set user state\r\n * \r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n * \r\n * async function handleUpdate(name: string) {\r\n * await updateUser({ name });\r\n * }\r\n * \r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user, isLoaded, updateUser, setUser } = useInsforge();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n\r\n","import { useState, useEffect } from 'react';\r\nimport type {\r\n GetPublicAuthConfigResponse\r\n} from '@insforge/shared-schemas';\r\nimport { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\r\n * - `emailConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { oauthProviders, emailConfig, isLoaded } = usePublicAuthConfig();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {oauthProviders.length}</p>\r\n * <p>Password min length: {emailConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within InsforgeProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n emailConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useInsforge();\r\n const [emailConfig, setEmailConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setEmailConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setEmailConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { emailConfig, isLoaded };\r\n}\r\n\r\n"]}
|
package/dist/hooks.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["useState","useEffect"],"mappings":";;;;AAyEA,IAAM,eAAA,GAAkB,aAAA;AAAA,EACtB;AACF,CAAA;AA6fO,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAU,WAAW,eAAe,CAAA;AAC1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,OAAA;AACT;;;AC1iBO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,SAA6C,IAAI,CAAA;AACvF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,SAAS,KAAK,CAAA;AAE9C,EAAAC,UAAU,MAAM;AACd,IAAA,eAAe,WAAA,GAAc;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,EAAoB;AACzC,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,cAAA,CAAe,MAAM,CAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,MACrB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,aAAa,QAAA,EAAS;AACjC","file":"hooks.mjs","sourcesContent":["\"use client\";\r\n\r\nimport {\r\n createContext,\r\n useContext,\r\n useEffect,\r\n useState,\r\n useCallback,\r\n useRef,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { createClient } from \"@insforge/sdk\";\r\nimport type { InsforgeUser } from \"../types\";\r\nimport { GetPublicAuthConfigResponse } from \"@insforge/shared-schemas\";\r\n\r\ninterface InsforgeContextValue {\r\n // Auth state\r\n user: InsforgeUser | null;\r\n isLoaded: boolean;\r\n isSignedIn: boolean;\r\n\r\n // Auth methods\r\n setUser: (user: InsforgeUser | null) => void;\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signOut: () => Promise<void>;\r\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\r\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\r\n \r\n // Callback handling for OAuth and email/password redirects\r\n handleAuthCallback: (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }) => Promise<{ success: boolean; error?: string }>;\r\n\r\n // Email verification methods\r\n sendPasswordResetCode: (\r\n email: string\r\n ) => Promise<{ success: boolean; message: string } | null>;\r\n resetPassword: (\r\n token: string,\r\n newPassword: string\r\n ) => Promise<{ message: string; redirectTo?: string } | null>;\r\n verifyEmail: (\r\n token: string\r\n ) => Promise<{ accessToken: string; user?: any } | null>;\r\n\r\n // Public auth config\r\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\r\n // Base config\r\n baseUrl: string;\r\n}\r\n\r\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\r\n undefined\r\n);\r\n\r\nexport interface InsforgeProviderProps {\r\n children: ReactNode;\r\n baseUrl: string;\r\n onAuthChange?: (user: InsforgeUser | null) => void;\r\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\r\n syncTokenToCookie?: (token: string) => Promise<boolean>;\r\n clearCookie?: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Unified Insforge Provider - manages authentication state and configuration\r\n *\r\n * Manages user authentication state and provides all necessary context to child components.\r\n * Works with any React framework (Next.js, Vite, Remix, etc.).\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { InsforgeProvider } from '@insforge/react';\r\n *\r\n * export default function App() {\r\n * return (\r\n * <InsforgeProvider baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\r\n * {children}\r\n * </InsforgeProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <InsforgeProvider\r\n * baseUrl={baseUrl}\r\n * syncTokenToCookie={async (token) => {\r\n * await fetch('/api/auth', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ token })\r\n * });\r\n * return true;\r\n * }}\r\n * clearCookie={async () => {\r\n * await fetch('/api/auth', { method: 'DELETE' });\r\n * }}\r\n * >\r\n * {children}\r\n * </InsforgeProvider>\r\n * ```\r\n */\r\nexport function InsforgeProvider({\r\n children,\r\n baseUrl,\r\n onAuthChange,\r\n syncTokenToCookie,\r\n clearCookie,\r\n}: InsforgeProviderProps) {\r\n // Auth state\r\n const [user, setUser] = useState<InsforgeUser | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n // Initialize SDK client with lazy initialization - only runs once\r\n const [insforge] = useState(() => createClient({ baseUrl }));\r\n\r\n // Load auth state - returns explicit success/error status\r\n const loadAuthState = useCallback(async (): Promise<{\r\n success: boolean;\r\n error?: string;\r\n }> => {\r\n try {\r\n // Use SDK's getCurrentSession() to check for existing session\r\n const sessionResult = insforge.auth.getCurrentSession();\r\n const session = sessionResult.data?.session;\r\n const token = session?.accessToken || null;\r\n\r\n if (!token) {\r\n // No token, user is not authenticated\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"no_session\" };\r\n }\r\n\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n // Token is valid, update user state with fresh data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n setIsLoaded(true);\r\n return { success: true };\r\n } else {\r\n // Token invalid or expired\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"invalid_token\" };\r\n }\r\n } catch (error) {\r\n // Token validation failed\r\n console.error(\"[InsforgeProvider] Token validation failed:\", error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : \"authentication_failed\",\r\n };\r\n }\r\n }, [insforge, onAuthChange, syncTokenToCookie, clearCookie]);\r\n\r\n useEffect(() => {\r\n // Run loadAuthState only once on mount\r\n loadAuthState();\r\n\r\n return () => {\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n };\r\n }, []); // Empty deps - run only on mount\r\n\r\n const getPublicAuthConfig = useCallback(async () => {\r\n try {\r\n const result = await insforge.auth.getPublicAuthConfig();\r\n if (result.data) {\r\n return result.data;\r\n } else {\r\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error(\r\n \"[InsforgeProvider] Failed to get public auth config:\",\r\n error\r\n );\r\n return null;\r\n }\r\n }, [insforge]);\r\n\r\n /**\r\n * Helper function to handle successful authentication\r\n */\r\n const handleAuthSuccess = useCallback(\r\n async (\r\n authToken: string,\r\n fallbackUser?: { id?: string; email?: string; name?: string }\r\n ) => {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Try to sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(authToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay\r\n }\r\n }\r\n } else if (fallbackUser) {\r\n // Fallback to basic user data if getCurrentUser fails\r\n const userData: InsforgeUser = {\r\n id: fallbackUser.id || \"\",\r\n email: fallbackUser.email || \"\",\r\n name: fallbackUser.name || \"\",\r\n avatarUrl: \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie]\r\n );\r\n\r\n /**\r\n * Handle authentication callback from OAuth or email/password redirects\r\n * This is specifically for callback pages - saves token and verifies authentication\r\n */\r\n const handleAuthCallback = useCallback(\r\n async (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }): Promise<{ success: boolean; error?: string }> => {\r\n try {\r\n // Save the token to localStorage using SDK's setSession\r\n await insforge.auth.setSession({\r\n accessToken: params.accessToken,\r\n user: {\r\n id: params.userId || '',\r\n email: params.email || '',\r\n name: params.name || '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n },\r\n });\r\n\r\n // Now verify the token and get full user data\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (!userResult.data) {\r\n // Token is invalid\r\n await insforge.auth.signOut();\r\n \r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n return { success: false, error: 'invalid_token' };\r\n }\r\n\r\n // Update user state with full profile data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || params.name || '',\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(params.accessToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay, continue\r\n }\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n // Authentication failed\r\n console.error('[InsforgeProvider] Auth callback failed:', error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'authentication_failed',\r\n };\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie, clearCookie]\r\n );\r\n\r\n const signIn = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signInWithPassword({\r\n email,\r\n password,\r\n });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage =\r\n sdkResult.error?.message || \"Invalid email or password\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signUp = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signUp({ email, password });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage = sdkResult.error?.message || \"Sign up failed\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signOut = useCallback(async () => {\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n // Clear refresh interval if exists\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n }, [insforge, onAuthChange, clearCookie]);\r\n\r\n const updateUser = useCallback(\r\n async (data: Partial<InsforgeUser>) => {\r\n if (!user) throw new Error(\"No user signed in\");\r\n\r\n const profileUpdate: Record<string, any> = {\r\n nickname: data.name,\r\n avatarUrl: data.avatarUrl,\r\n };\r\n\r\n const result = await insforge.auth.setProfile(profileUpdate);\r\n\r\n if (result.data) {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const updatedUser: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n setUser(updatedUser);\r\n if (onAuthChange) {\r\n onAuthChange(updatedUser);\r\n }\r\n }\r\n }\r\n },\r\n [user, onAuthChange, insforge]\r\n );\r\n\r\n const sendPasswordResetCode = useCallback(\r\n async (email: string) => {\r\n const sdkResult = await insforge.auth.sendPasswordResetCode({ email });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const resetPassword = useCallback(\r\n async (token: string, newPassword: string) => {\r\n const sdkResult = await insforge.auth.resetPassword({\r\n newPassword,\r\n otp: token,\r\n });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const verifyEmail = useCallback(\r\n async (token: string) => {\r\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n return (\r\n <InsforgeContext.Provider\r\n value={{\r\n user,\r\n isLoaded,\r\n isSignedIn: !!user,\r\n setUser,\r\n signIn,\r\n signUp,\r\n signOut,\r\n updateUser,\r\n reloadAuth: loadAuthState,\r\n handleAuthCallback,\r\n baseUrl,\r\n sendPasswordResetCode,\r\n resetPassword,\r\n verifyEmail,\r\n getPublicAuthConfig,\r\n }}\r\n >\r\n {children}\r\n </InsforgeContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Insforge context\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useInsforge();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useInsforge(): InsforgeContextValue {\r\n const context = useContext(InsforgeContext);\r\n if (!context) {\r\n throw new Error(\"useInsforge must be used within InsforgeProvider\");\r\n }\r\n return context;\r\n}\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n * \r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n * \r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n * \r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth() {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access current user data\r\n * \r\n * @returns Object containing:\r\n * - `user`: Current user object (InsforgeUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data\r\n * - `setUser`: Internal function to manually set user state\r\n * \r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n * \r\n * async function handleUpdate(name: string) {\r\n * await updateUser({ name });\r\n * }\r\n * \r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user, isLoaded, updateUser, setUser } = useInsforge();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n\r\n","import { useState, useEffect } from 'react';\r\nimport type {\r\n GetPublicAuthConfigResponse\r\n} from '@insforge/shared-schemas';\r\nimport { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\r\n * - `emailConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { oauthProviders, emailConfig, isLoaded } = usePublicAuthConfig();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {oauthProviders.length}</p>\r\n * <p>Password min length: {emailConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within InsforgeProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n emailConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useInsforge();\r\n const [emailConfig, setEmailConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setEmailConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setEmailConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { emailConfig, isLoaded };\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["useState","useEffect"],"mappings":";;;;AAyEA,IAAM,eAAA,GAAkB,aAAA;AAAA,EACtB;AACF,CAAA;AA4fO,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAU,WAAW,eAAe,CAAA;AAC1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,OAAA;AACT;;;ACziBO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,SAA6C,IAAI,CAAA;AACvF,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,SAAS,KAAK,CAAA;AAE9C,EAAAC,UAAU,MAAM;AACd,IAAA,eAAe,WAAA,GAAc;AAC3B,MAAA,MAAM,MAAA,GAAS,MAAM,mBAAA,EAAoB;AACzC,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,cAAA,CAAe,MAAM,CAAA;AAAA,MACvB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,cAAA,CAAe,IAAI,CAAA;AAAA,MACrB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,aAAa,QAAA,EAAS;AACjC","file":"hooks.mjs","sourcesContent":["\"use client\";\r\n\r\nimport {\r\n createContext,\r\n useContext,\r\n useEffect,\r\n useState,\r\n useCallback,\r\n useRef,\r\n type ReactNode,\r\n} from \"react\";\r\nimport { createClient } from \"@insforge/sdk\";\r\nimport type { InsforgeUser } from \"../types\";\r\nimport { GetPublicAuthConfigResponse } from \"@insforge/shared-schemas\";\r\n\r\ninterface InsforgeContextValue {\r\n // Auth state\r\n user: InsforgeUser | null;\r\n isLoaded: boolean;\r\n isSignedIn: boolean;\r\n\r\n // Auth methods\r\n setUser: (user: InsforgeUser | null) => void;\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<\r\n | {\r\n user?: { id: string; email: string; name: string };\r\n accessToken: string | null;\r\n }\r\n | { error: string }\r\n >;\r\n signOut: () => Promise<void>;\r\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\r\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\r\n \r\n // Callback handling for OAuth and email/password redirects\r\n handleAuthCallback: (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }) => Promise<{ success: boolean; error?: string }>;\r\n\r\n // Email verification methods\r\n sendPasswordResetCode: (\r\n email: string\r\n ) => Promise<{ success: boolean; message: string } | null>;\r\n resetPassword: (\r\n token: string,\r\n newPassword: string\r\n ) => Promise<{ message: string; redirectTo?: string } | null>;\r\n verifyEmail: (\r\n token: string\r\n ) => Promise<{ accessToken: string; user?: any } | null>;\r\n\r\n // Public auth config\r\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\r\n // Base config\r\n baseUrl: string;\r\n}\r\n\r\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\r\n undefined\r\n);\r\n\r\nexport interface InsforgeProviderProps {\r\n children: ReactNode;\r\n baseUrl: string;\r\n onAuthChange?: (user: InsforgeUser | null) => void;\r\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\r\n syncTokenToCookie?: (token: string) => Promise<boolean>;\r\n clearCookie?: () => Promise<void>;\r\n}\r\n\r\n/**\r\n * Unified Insforge Provider - manages authentication state and configuration\r\n *\r\n * Manages user authentication state and provides all necessary context to child components.\r\n * Works with any React framework (Next.js, Vite, Remix, etc.).\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { InsforgeProvider } from '@insforge/react';\r\n *\r\n * export default function App() {\r\n * return (\r\n * <InsforgeProvider baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\r\n * {children}\r\n * </InsforgeProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <InsforgeProvider\r\n * baseUrl={baseUrl}\r\n * syncTokenToCookie={async (token) => {\r\n * await fetch('/api/auth', {\r\n * method: 'POST',\r\n * body: JSON.stringify({ token })\r\n * });\r\n * return true;\r\n * }}\r\n * clearCookie={async () => {\r\n * await fetch('/api/auth', { method: 'DELETE' });\r\n * }}\r\n * >\r\n * {children}\r\n * </InsforgeProvider>\r\n * ```\r\n */\r\nexport function InsforgeProvider({\r\n children,\r\n baseUrl,\r\n onAuthChange,\r\n syncTokenToCookie,\r\n clearCookie,\r\n}: InsforgeProviderProps) {\r\n // Auth state\r\n const [user, setUser] = useState<InsforgeUser | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n // Initialize SDK client with lazy initialization - only runs once\r\n const [insforge] = useState(() => createClient({ baseUrl }));\r\n\r\n // Load auth state - returns explicit success/error status\r\n const loadAuthState = useCallback(async (): Promise<{\r\n success: boolean;\r\n error?: string;\r\n }> => {\r\n try {\r\n // Use SDK's getCurrentSession() to check for existing session\r\n const sessionResult = insforge.auth.getCurrentSession();\r\n const session = sessionResult.data?.session;\r\n const token = session?.accessToken || null;\r\n\r\n if (!token) {\r\n // No token, user is not authenticated\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"no_session\" };\r\n }\r\n\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n // Token is valid, update user state with fresh data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n setIsLoaded(true);\r\n return { success: true };\r\n } else {\r\n // Token invalid or expired\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n return { success: false, error: \"invalid_token\" };\r\n }\r\n } catch (error) {\r\n // Token validation failed\r\n console.error(\"[InsforgeProvider] Token validation failed:\", error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n setIsLoaded(true);\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : \"authentication_failed\",\r\n };\r\n }\r\n }, [insforge, onAuthChange, syncTokenToCookie, clearCookie]);\r\n\r\n useEffect(() => {\r\n // Run loadAuthState only once on mount\r\n loadAuthState();\r\n\r\n return () => {\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n };\r\n }, []); // Empty deps - run only on mount\r\n\r\n const getPublicAuthConfig = useCallback(async () => {\r\n try {\r\n const result = await insforge.auth.getPublicAuthConfig();\r\n if (result.data) {\r\n return result.data;\r\n } else {\r\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\r\n return null;\r\n }\r\n } catch (error) {\r\n console.error(\r\n \"[InsforgeProvider] Failed to get public auth config:\",\r\n error\r\n );\r\n return null;\r\n }\r\n }, [insforge]);\r\n\r\n /**\r\n * Helper function to handle successful authentication\r\n */\r\n const handleAuthSuccess = useCallback(\r\n async (\r\n authToken: string,\r\n fallbackUser?: { id?: string; email?: string; name?: string }\r\n ) => {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Try to sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(authToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay\r\n }\r\n }\r\n } else if (fallbackUser) {\r\n // Fallback to basic user data if getCurrentUser fails\r\n const userData: InsforgeUser = {\r\n id: fallbackUser.id || \"\",\r\n email: fallbackUser.email || \"\",\r\n name: fallbackUser.name || \"\",\r\n avatarUrl: \"\",\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie]\r\n );\r\n\r\n /**\r\n * Handle authentication callback from OAuth or email/password redirects\r\n * This is specifically for callback pages - saves token and verifies authentication\r\n */\r\n const handleAuthCallback = useCallback(\r\n async (params: {\r\n accessToken: string;\r\n userId?: string;\r\n email?: string;\r\n name?: string;\r\n }): Promise<{ success: boolean; error?: string }> => {\r\n try {\r\n await insforge.auth.setSession({\r\n accessToken: params.accessToken,\r\n user: {\r\n id: params.userId || '',\r\n email: params.email || '',\r\n name: params.name || '',\r\n emailVerified: false,\r\n createdAt: new Date().toISOString(),\r\n updatedAt: new Date().toISOString(),\r\n },\r\n });\r\n\r\n // Now verify the token and get full user data\r\n const userResult = await insforge.auth.getCurrentUser();\r\n\r\n if (!userResult.data) {\r\n // Token is invalid\r\n await insforge.auth.signOut();\r\n \r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n return { success: false, error: 'invalid_token' };\r\n }\r\n\r\n // Update user state with full profile data\r\n const profile = userResult.data.profile;\r\n const userData: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || params.name || '',\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\r\n };\r\n\r\n setUser(userData);\r\n\r\n if (onAuthChange) {\r\n onAuthChange(userData);\r\n }\r\n\r\n // Sync token to cookie if function provided\r\n if (syncTokenToCookie) {\r\n try {\r\n await syncTokenToCookie(params.accessToken);\r\n } catch (error) {\r\n // Cookie sync failed - that's okay, continue\r\n }\r\n }\r\n\r\n return { success: true };\r\n } catch (error) {\r\n // Authentication failed\r\n console.error('[InsforgeProvider] Auth callback failed:', error);\r\n\r\n await insforge.auth.signOut();\r\n\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n\r\n return {\r\n success: false,\r\n error: error instanceof Error ? error.message : 'authentication_failed',\r\n };\r\n }\r\n },\r\n [insforge, onAuthChange, syncTokenToCookie, clearCookie]\r\n );\r\n\r\n const signIn = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signInWithPassword({\r\n email,\r\n password,\r\n });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage =\r\n sdkResult.error?.message || \"Invalid email or password\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signUp = useCallback(\r\n async (email: string, password: string) => {\r\n const sdkResult = await insforge.auth.signUp({ email, password });\r\n\r\n if (sdkResult.data) {\r\n await handleAuthSuccess(\r\n sdkResult.data.accessToken || \"\",\r\n sdkResult.data.user\r\n ? {\r\n id: sdkResult.data.user.id,\r\n email: sdkResult.data.user.email,\r\n name: sdkResult.data.user.name,\r\n }\r\n : undefined\r\n );\r\n return sdkResult.data;\r\n } else {\r\n const errorMessage = sdkResult.error?.message || \"Sign up failed\";\r\n return { error: errorMessage };\r\n }\r\n },\r\n [insforge, handleAuthSuccess]\r\n );\r\n\r\n const signOut = useCallback(async () => {\r\n await insforge.auth.signOut();\r\n\r\n // Clear cookie if function provided\r\n if (clearCookie) {\r\n try {\r\n await clearCookie();\r\n } catch (error) {\r\n // Ignore errors\r\n }\r\n }\r\n\r\n // Clear refresh interval if exists\r\n if (refreshIntervalRef.current) {\r\n clearInterval(refreshIntervalRef.current);\r\n }\r\n\r\n setUser(null);\r\n if (onAuthChange) {\r\n onAuthChange(null);\r\n }\r\n }, [insforge, onAuthChange, clearCookie]);\r\n\r\n const updateUser = useCallback(\r\n async (data: Partial<InsforgeUser>) => {\r\n if (!user) throw new Error(\"No user signed in\");\r\n\r\n const profileUpdate: Record<string, any> = {\r\n nickname: data.name,\r\n avatarUrl: data.avatarUrl,\r\n };\r\n\r\n const result = await insforge.auth.setProfile(profileUpdate);\r\n\r\n if (result.data) {\r\n const userResult = await insforge.auth.getCurrentUser();\r\n if (userResult.data) {\r\n const profile = userResult.data.profile;\r\n const updatedUser: InsforgeUser = {\r\n id: userResult.data.user.id,\r\n email: userResult.data.user.email,\r\n name: (profile?.nickname as string | undefined) || \"\",\r\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\r\n };\r\n setUser(updatedUser);\r\n if (onAuthChange) {\r\n onAuthChange(updatedUser);\r\n }\r\n }\r\n }\r\n },\r\n [user, onAuthChange, insforge]\r\n );\r\n\r\n const sendPasswordResetCode = useCallback(\r\n async (email: string) => {\r\n const sdkResult = await insforge.auth.sendPasswordResetCode({ email });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const resetPassword = useCallback(\r\n async (token: string, newPassword: string) => {\r\n const sdkResult = await insforge.auth.resetPassword({\r\n newPassword,\r\n otp: token,\r\n });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n const verifyEmail = useCallback(\r\n async (token: string) => {\r\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\r\n return sdkResult.data;\r\n },\r\n [insforge]\r\n );\r\n\r\n return (\r\n <InsforgeContext.Provider\r\n value={{\r\n user,\r\n isLoaded,\r\n isSignedIn: !!user,\r\n setUser,\r\n signIn,\r\n signUp,\r\n signOut,\r\n updateUser,\r\n reloadAuth: loadAuthState,\r\n handleAuthCallback,\r\n baseUrl,\r\n sendPasswordResetCode,\r\n resetPassword,\r\n verifyEmail,\r\n getPublicAuthConfig,\r\n }}\r\n >\r\n {children}\r\n </InsforgeContext.Provider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Insforge context\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useInsforge();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useInsforge(): InsforgeContextValue {\r\n const context = useContext(InsforgeContext);\r\n if (!context) {\r\n throw new Error(\"useInsforge must be used within InsforgeProvider\");\r\n }\r\n return context;\r\n}\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n * \r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n * \r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n * \r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth() {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n\r\n","import { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to access current user data\r\n * \r\n * @returns Object containing:\r\n * - `user`: Current user object (InsforgeUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data\r\n * - `setUser`: Internal function to manually set user state\r\n * \r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n * \r\n * async function handleUpdate(name: string) {\r\n * await updateUser({ name });\r\n * }\r\n * \r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser() {\r\n const { user, isLoaded, updateUser, setUser } = useInsforge();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n\r\n","import { useState, useEffect } from 'react';\r\nimport type {\r\n GetPublicAuthConfigResponse\r\n} from '@insforge/shared-schemas';\r\nimport { useInsforge } from '../provider/InsforgeProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\r\n * - `emailConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { oauthProviders, emailConfig, isLoaded } = usePublicAuthConfig();\r\n * \r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * \r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {oauthProviders.length}</p>\r\n * <p>Password min length: {emailConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within InsforgeProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n emailConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useInsforge();\r\n const [emailConfig, setEmailConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setEmailConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setEmailConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { emailConfig, isLoaded };\r\n}\r\n\r\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1262,10 +1262,8 @@ function SignInForm({
|
|
|
1262
1262
|
);
|
|
1263
1263
|
}
|
|
1264
1264
|
function SignIn({
|
|
1265
|
-
afterSignInUrl = "/",
|
|
1266
1265
|
onSuccess,
|
|
1267
1266
|
onError,
|
|
1268
|
-
onRedirect,
|
|
1269
1267
|
...uiProps
|
|
1270
1268
|
}) {
|
|
1271
1269
|
const { signIn, baseUrl } = useInsforge();
|
|
@@ -1291,11 +1289,6 @@ function SignIn({
|
|
|
1291
1289
|
if (onSuccess) {
|
|
1292
1290
|
if (user) onSuccess(user, accessToken || "");
|
|
1293
1291
|
}
|
|
1294
|
-
if (onRedirect) {
|
|
1295
|
-
onRedirect(afterSignInUrl);
|
|
1296
|
-
} else {
|
|
1297
|
-
window.location.href = afterSignInUrl;
|
|
1298
|
-
}
|
|
1299
1292
|
} catch (err) {
|
|
1300
1293
|
const errorMessage = err.message || "Sign in failed";
|
|
1301
1294
|
setError(errorMessage);
|
|
@@ -1309,7 +1302,6 @@ function SignIn({
|
|
|
1309
1302
|
setOauthLoading(provider);
|
|
1310
1303
|
setError("");
|
|
1311
1304
|
const redirectTo = `${window.location.origin}/auth/callback`;
|
|
1312
|
-
sessionStorage.setItem("oauth_final_destination", afterSignInUrl || "/");
|
|
1313
1305
|
await insforge.auth.signInWithOAuth({
|
|
1314
1306
|
provider,
|
|
1315
1307
|
redirectTo
|
|
@@ -1491,10 +1483,8 @@ function SignUpForm({
|
|
|
1491
1483
|
);
|
|
1492
1484
|
}
|
|
1493
1485
|
function SignUp({
|
|
1494
|
-
afterSignUpUrl = "/",
|
|
1495
1486
|
onSuccess,
|
|
1496
1487
|
onError,
|
|
1497
|
-
onRedirect,
|
|
1498
1488
|
...uiProps
|
|
1499
1489
|
}) {
|
|
1500
1490
|
const { signUp, baseUrl } = useInsforge();
|
|
@@ -1525,11 +1515,6 @@ function SignUp({
|
|
|
1525
1515
|
if (onSuccess) {
|
|
1526
1516
|
if (user) onSuccess(user, accessToken || "");
|
|
1527
1517
|
}
|
|
1528
|
-
if (onRedirect) {
|
|
1529
|
-
onRedirect(afterSignUpUrl);
|
|
1530
|
-
} else {
|
|
1531
|
-
window.location.href = afterSignUpUrl;
|
|
1532
|
-
}
|
|
1533
1518
|
} catch (err) {
|
|
1534
1519
|
const errorMessage = err.message || "Sign up failed";
|
|
1535
1520
|
setError(errorMessage);
|
|
@@ -1543,7 +1528,6 @@ function SignUp({
|
|
|
1543
1528
|
setOauthLoading(provider);
|
|
1544
1529
|
setError("");
|
|
1545
1530
|
const redirectTo = `${window.location.origin}/auth/callback`;
|
|
1546
|
-
sessionStorage.setItem("oauth_final_destination", afterSignUpUrl || "/");
|
|
1547
1531
|
await insforge.auth.signInWithOAuth({
|
|
1548
1532
|
provider,
|
|
1549
1533
|
redirectTo
|
|
@@ -1780,10 +1764,7 @@ function InsforgeCallback({
|
|
|
1780
1764
|
}
|
|
1781
1765
|
return;
|
|
1782
1766
|
}
|
|
1783
|
-
const accessToken = searchParams.get("access_token")
|
|
1784
|
-
const userId = searchParams.get("user_id");
|
|
1785
|
-
const email = searchParams.get("email");
|
|
1786
|
-
const name = searchParams.get("name");
|
|
1767
|
+
const accessToken = searchParams.get("access_token");
|
|
1787
1768
|
if (!accessToken) {
|
|
1788
1769
|
const errorMsg = "no_token";
|
|
1789
1770
|
if (onError) {
|
|
@@ -1799,10 +1780,7 @@ function InsforgeCallback({
|
|
|
1799
1780
|
return;
|
|
1800
1781
|
}
|
|
1801
1782
|
const result = await handleAuthCallback({
|
|
1802
|
-
accessToken
|
|
1803
|
-
userId: userId || void 0,
|
|
1804
|
-
email: email || void 0,
|
|
1805
|
-
name: name || void 0
|
|
1783
|
+
accessToken
|
|
1806
1784
|
});
|
|
1807
1785
|
if (!result.success) {
|
|
1808
1786
|
const errorMsg = result.error || "authentication_failed";
|
|
@@ -2203,16 +2181,9 @@ function getInsforgeRoutes(config) {
|
|
|
2203
2181
|
signUp = "/sign-up",
|
|
2204
2182
|
verifyEmail = "/verify-email",
|
|
2205
2183
|
forgotPassword = "/forgot-password",
|
|
2206
|
-
resetPassword = "/reset-password"
|
|
2207
|
-
callback = "/auth/callback"
|
|
2184
|
+
resetPassword = "/reset-password"
|
|
2208
2185
|
} = paths;
|
|
2209
|
-
const routes = [
|
|
2210
|
-
// Always include callback route
|
|
2211
|
-
{
|
|
2212
|
-
path: callback,
|
|
2213
|
-
element: /* @__PURE__ */ jsxRuntime.jsx(InsforgeCallback, {})
|
|
2214
|
-
}
|
|
2215
|
-
];
|
|
2186
|
+
const routes = [];
|
|
2216
2187
|
if (builtInAuth) {
|
|
2217
2188
|
routes.push(
|
|
2218
2189
|
{
|