@insforge/react 0.4.12 → 0.5.0

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/hooks.cjs CHANGED
@@ -5,6 +5,7 @@ require('@insforge/sdk');
5
5
  require('react/jsx-runtime');
6
6
 
7
7
  // src/provider/InsforgeProvider.tsx
8
+ react.createContext(null);
8
9
  var InsforgeContext = react.createContext(void 0);
9
10
  function useInsforge() {
10
11
  const context = react.useContext(InsforgeContext);
@@ -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":";;;;;;;AA8DA,IAAM,eAAA,GAAkBA,oBAAgD,MAAS,CAAA;AA8f1E,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;;;AC9hBO,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;ACCO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,eAA6C,IAAI,CAAA;AACrF,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,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,YAAY,QAAA,EAAS;AAChC","file":"hooks.cjs","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { UserSchema, createClient } from '@insforge/sdk';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport {\n CreateSessionResponse,\n CreateUserResponse,\n GetPublicAuthConfigResponse,\n ResetPasswordResponse,\n} from '@insforge/shared-schemas';\n\ninterface InsforgeContextValue {\n // Auth state\n user: InsforgeUser | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n\n // Auth methods\n setUser: (user: InsforgeUser | null) => void;\n signIn: (\n email: string,\n password: string\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (\n email: string,\n password: string\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendVerificationEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n sendResetPasswordEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n otp: string,\n email?: string\n ) => Promise<{\n accessToken: string;\n user?: UserSchema;\n redirectTo?: string;\n error?: { message: string };\n } | null>;\n exchangeResetPasswordToken: (\n email: string,\n code: string\n ) => Promise<{ token: string; expiresAt?: string } | { error: { message: string } }>;\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) => Promise<void>;\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(undefined);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Manages user authentication state and provides all necessary context to child components.\n * Works with any React framework (Next.js, Vite, Remix, etc.).\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n *\n * export default function App() {\n * return (\n * <InsforgeProvider\n * baseUrl={process.env.VITE_INSFORGE_BASE_URL}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * baseUrl={baseUrl}\n * onSignIn={async (authToken) => {\n * await signIn(authToken);\n * }}\n * onSignOut={async () => {\n * await signOut();\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n}: InsforgeProviderProps) {\n // Auth state\n const [user, setUser] = useState<InsforgeUser | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const hasProcessedCallbackRef = useRef(false);\n\n // Initialize SDK client with lazy initialization - only runs once\n const [insforge] = useState(() => createClient({ baseUrl }));\n\n // Load auth state - returns explicit success/error status\n const loadAuthState = useCallback(async (): Promise<{\n success: boolean;\n error?: string;\n }> => {\n try {\n // Use SDK's getCurrentSession() to check for existing session\n const sessionResult = insforge.auth.getCurrentSession();\n const session = sessionResult.data?.session;\n const token = session?.accessToken || null;\n\n if (!token) {\n // No token, user is not authenticated\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'no_session' };\n }\n\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n // Token is valid, update user state with fresh data\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n setIsLoaded(true);\n return { success: true };\n } else {\n // Token invalid or expired\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'invalid_token' };\n }\n } catch (error) {\n // Token validation failed\n console.error('[InsforgeProvider] Token validation failed:', error);\n\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Authentication failed',\n };\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n // Capture the ref value when effect runs\n const intervalId = refreshIntervalRef.current;\n\n return () => {\n // Use the captured value in cleanup\n if (intervalId) {\n clearInterval(intervalId);\n }\n };\n }, [loadAuthState]);\n\n // Handle authentication callback from URL\n useEffect(() => {\n // Only run once and only after auth is loaded\n if (!isLoaded || hasProcessedCallbackRef.current) {\n return;\n }\n\n const searchParams = new URLSearchParams(window.location.search);\n const accessToken = searchParams.get('access_token');\n\n if (accessToken && !!user) {\n // Mark as processed\n hasProcessedCallbackRef.current = true;\n\n // Clean URL by removing query parameters\n const url = new URL(window.location.href);\n url.search = '';\n window.history.replaceState({}, '', url.toString());\n\n // Redirect to afterSignInUrl\n setTimeout(() => {\n window.location.href = afterSignInUrl;\n }, 100);\n }\n }, [isLoaded, user, afterSignInUrl]);\n\n const getPublicAuthConfig = useCallback(async () => {\n try {\n const result = await insforge.auth.getPublicAuthConfig();\n if (result.data) {\n return result.data;\n } else {\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\n return null;\n }\n } catch (error) {\n console.error('[InsforgeProvider] Failed to get public auth config:', error);\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (authToken: string, fallbackUser?: { id?: string; email?: string; name?: string }) => {\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n\n // Try to sync token to cookie if function provided\n if (onSignIn) {\n try {\n await onSignIn(authToken);\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error syncing token to cookie:', error.message);\n }\n }\n }\n } else if (fallbackUser) {\n // Fallback to basic user data if getCurrentUser fails\n const userData: InsforgeUser = {\n id: fallbackUser.id || '',\n email: fallbackUser.email || '',\n name: fallbackUser.name || '',\n avatarUrl: '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n }\n },\n [insforge, onAuthChange, onSignIn]\n );\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signInWithPassword({\n email,\n password,\n });\n\n if (sdkResult.data) {\n await handleAuthSuccess(\n sdkResult.data.accessToken || '',\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Invalid email or password',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signUp({ email, password });\n\n if (sdkResult.data) {\n // Only set auth state if we got a token back (email verification might be required)\n if (sdkResult.data.accessToken) {\n await handleAuthSuccess(\n sdkResult.data.accessToken,\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n }\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Sign up failed',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signOut = useCallback(async () => {\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n // Clear refresh interval if exists\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) {\n throw new Error('No user signed in');\n }\n\n const profileUpdate: Record<string, string | undefined> = {\n nickname: data.name,\n avatarUrl: data.avatarUrl,\n };\n\n const result = await insforge.auth.setProfile(profileUpdate);\n\n if (result.data) {\n const userResult = await insforge.auth.getCurrentUser();\n if (userResult.data) {\n const profile = userResult.data.profile;\n const updatedUser: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: profile?.nickname || '',\n avatarUrl: profile?.avatarUrl || '',\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, insforge]\n );\n\n const sendVerificationEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendVerificationEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const sendResetPasswordEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendResetPasswordEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const resetPassword = useCallback(\n async (token: string, newPassword: string) => {\n const sdkResult = await insforge.auth.resetPassword({\n newPassword,\n otp: token,\n });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const verifyEmail = useCallback(\n async (otp: string, email?: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp, email: email || undefined });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n accessToken: '',\n error: {\n message: sdkResult.error?.message || 'Email verification failed',\n },\n };\n }\n },\n [insforge]\n );\n\n const exchangeResetPasswordToken = useCallback(\n async (email: string, code: string) => {\n const sdkResult = await insforge.auth.exchangeResetPasswordToken({ email, code });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n error: {\n message: sdkResult.error?.message || 'Failed to exchange reset password token',\n },\n };\n }\n },\n [insforge]\n );\n\n const loginWithOAuth = useCallback(\n async (provider: OAuthProvider, redirectTo: string) => {\n const sdkResult = await insforge.auth.signInWithOAuth({\n provider,\n redirectTo: redirectTo || window.location.origin + afterSignInUrl,\n });\n if (sdkResult.error) {\n throw new Error(sdkResult.error.message);\n }\n },\n [insforge, afterSignInUrl]\n );\n\n return (\n <InsforgeContext.Provider\n value={{\n user,\n isLoaded,\n isSignedIn: !!user,\n setUser,\n signIn,\n signUp,\n signOut,\n updateUser,\n reloadAuth: loadAuthState,\n baseUrl,\n sendVerificationEmail,\n sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n exchangeResetPasswordToken,\n getPublicAuthConfig,\n loginWithOAuth,\n }}\n >\n {children}\n </InsforgeContext.Provider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n *\n * if (!isSignedIn) return <SignIn />;\n *\n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useInsforge(): InsforgeContextValue {\n const context = useContext(InsforgeContext);\n if (!context) {\n throw new Error('useInsforge must be used within InsforgeProvider');\n }\n return context;\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access authentication methods\n *\n * @returns Object containing:\n * - `signIn`: Function to sign in with email and password\n * - `signUp`: Function to sign up with email and password\n * - `signOut`: Function to sign out the current user\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `isSignedIn`: Boolean indicating if user is currently signed in\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\n *\n * async function handleLogin(email: string, password: string) {\n * try {\n * await signIn(email, password);\n * // User is now signed in\n * } catch (error) {\n * console.error('Sign in failed:', error);\n * }\n * }\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\n * {/* form fields *\\/}\n * </form>\n * );\n * }\n * ```\n */\nexport function useAuth() {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access current user data\n *\n * @returns Object containing:\n * - `user`: Current user object (InsforgeUser | null)\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `updateUser`: Function to update user profile data\n * - `setUser`: Internal function to manually set user state\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, isLoaded, updateUser } = useUser();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n * if (!user) return <div>Not signed in</div>;\n *\n * async function handleUpdate(name: string) {\n * await updateUser({ name });\n * }\n *\n * return (\n * <div>\n * <p>Email: {user.email}</p>\n * {user.name && <p>Name: {user.name}</p>}\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useUser() {\n const { user, isLoaded, updateUser, setUser } = useInsforge();\n return { user, isLoaded, updateUser, setUser };\n}\n","import { useState, useEffect } from 'react';\nimport type { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\n *\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\n *\n * This hook lazily fetches all public authentication configuration from the backend\n * only when the component mounts. Using it in other components will cause unnecessary\n * API calls on every page load.\n *\n * @returns Object containing OAuth providers, email auth config, and loading state\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\n * - `authConfig`: Email authentication configuration object with password rules\n * - `isLoaded`: Boolean indicating if the config has been fetched\n *\n * @example\n * ```tsx\n * // ✅ Correct usage - only in SignIn/SignUp components\n * function SignUp() {\n * const { oauthProviders, authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {oauthProviders.length}</p>\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @requires Must be used within InsforgeProvider\n */\nexport function usePublicAuthConfig(): {\n authConfig: GetPublicAuthConfigResponse | null;\n isLoaded: boolean;\n} {\n const { getPublicAuthConfig } = useInsforge();\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n async function fetchConfig() {\n const result = await getPublicAuthConfig();\n if (result) {\n setAuthConfig(result);\n } else {\n console.error('[usePublicAuthConfig] Failed to get public auth config');\n setAuthConfig(null);\n }\n setIsLoaded(true);\n }\n\n fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
1
+ {"version":3,"sources":["../src/navigation/NavigationContext.tsx","../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;;;AAG0BA,oBAAwC,IAAI;AC4DtE,IAAM,eAAA,GAAkBA,oBAAgD,MAAS,CAAA;AAggB1E,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;;;ACjiBO,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;ACCO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,eAA6C,IAAI,CAAA;AACrF,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,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,YAAY,QAAA,EAAS;AAChC","file":"hooks.cjs","sourcesContent":["import { createContext, useContext, ReactNode } from 'react';\nimport type { NavigationAdapter } from './types';\n\nconst NavigationContext = createContext<NavigationAdapter | null>(null);\n\nexport interface NavigationProviderProps {\n adapter: NavigationAdapter;\n children: ReactNode;\n}\n\n/**\n * Navigation Provider\n * Injects navigation adapter into the component tree\n */\nexport function NavigationProvider({ adapter, children }: NavigationProviderProps) {\n return <NavigationContext.Provider value={adapter}>{children}</NavigationContext.Provider>;\n}\n\n/**\n * Hook to access navigation adapter\n * @throws Error if used outside NavigationProvider\n */\nexport function useNavigationAdapter(): NavigationAdapter {\n const adapter = useContext(NavigationContext);\n\n if (!adapter) {\n throw new Error('useNavigationAdapter must be used within NavigationProvider');\n }\n\n return adapter;\n}\n","import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { UserSchema, createClient } from '@insforge/sdk';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport {\n CreateSessionResponse,\n CreateUserResponse,\n GetPublicAuthConfigResponse,\n ResetPasswordResponse,\n} from '@insforge/shared-schemas';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\n\ninterface InsforgeContextValue {\n // Auth state\n user: InsforgeUser | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n\n // Auth methods\n setUser: (user: InsforgeUser | null) => void;\n signIn: (\n email: string,\n password: string\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (\n email: string,\n password: string\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendVerificationEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n sendResetPasswordEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n otp: string,\n email?: string\n ) => Promise<{\n accessToken: string;\n user?: UserSchema;\n redirectTo?: string;\n error?: { message: string };\n } | null>;\n exchangeResetPasswordToken: (\n email: string,\n code: string\n ) => Promise<{ token: string; expiresAt?: string } | { error: { message: string } }>;\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) => Promise<void>;\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(undefined);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Manages user authentication state and provides all necessary context to child components.\n * Works with any React framework (Next.js, Vite, Remix, etc.).\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n *\n * export default function App() {\n * return (\n * <InsforgeProvider\n * baseUrl={process.env.VITE_INSFORGE_BASE_URL}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * baseUrl={baseUrl}\n * onSignIn={async (authToken) => {\n * await signIn(authToken);\n * }}\n * onSignOut={async () => {\n * await signOut();\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n}: InsforgeProviderProps) {\n // Auth state\n const [user, setUser] = useState<InsforgeUser | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const hasProcessedCallbackRef = useRef(false);\n\n // Initialize SDK client with lazy initialization - only runs once\n const [insforge] = useState(() => createClient({ baseUrl }));\n\n // Load auth state - returns explicit success/error status\n const loadAuthState = useCallback(async (): Promise<{\n success: boolean;\n error?: string;\n }> => {\n try {\n // Use SDK's getCurrentSession() to check for existing session\n const sessionResult = insforge.auth.getCurrentSession();\n const session = sessionResult.data?.session;\n const token = session?.accessToken || null;\n\n if (!token) {\n // No token, user is not authenticated\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'no_session' };\n }\n\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n // Token is valid, update user state with fresh data\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n setIsLoaded(true);\n return { success: true };\n } else {\n // Token invalid or expired\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'invalid_token' };\n }\n } catch (error) {\n // Token validation failed\n console.error('[InsforgeProvider] Token validation failed:', error);\n\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Authentication failed',\n };\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n // Capture the ref value when effect runs\n const intervalId = refreshIntervalRef.current;\n\n return () => {\n // Use the captured value in cleanup\n if (intervalId) {\n clearInterval(intervalId);\n }\n };\n }, [loadAuthState]);\n\n // Handle authentication callback from URL\n useEffect(() => {\n // Only run once and only after auth is loaded\n if (!isLoaded || hasProcessedCallbackRef.current) {\n return;\n }\n\n const searchParams = new URLSearchParams(window.location.search);\n const accessToken = searchParams.get('access_token');\n\n if (accessToken && !!user) {\n // Mark as processed\n hasProcessedCallbackRef.current = true;\n\n // Clean URL by removing query parameters\n const url = new URL(window.location.href);\n url.search = '';\n window.history.replaceState({}, '', url.toString());\n\n // Redirect to afterSignInUrl\n setTimeout(() => {\n window.location.href = afterSignInUrl;\n }, 100);\n }\n }, [isLoaded, user, afterSignInUrl]);\n\n const getPublicAuthConfig = useCallback(async () => {\n try {\n const result = await insforge.auth.getPublicAuthConfig();\n if (result.data) {\n return result.data;\n } else {\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\n return null;\n }\n } catch (error) {\n console.error('[InsforgeProvider] Failed to get public auth config:', error);\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (authToken: string, fallbackUser?: { id?: string; email?: string; name?: string }) => {\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n\n // Try to sync token to cookie if function provided\n if (onSignIn) {\n try {\n await onSignIn(authToken);\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error syncing token to cookie:', error.message);\n }\n }\n }\n } else if (fallbackUser) {\n // Fallback to basic user data if getCurrentUser fails\n const userData: InsforgeUser = {\n id: fallbackUser.id || '',\n email: fallbackUser.email || '',\n name: fallbackUser.name || '',\n avatarUrl: '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n }\n },\n [insforge, onAuthChange, onSignIn]\n );\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signInWithPassword({\n email,\n password,\n });\n\n if (sdkResult.data) {\n await handleAuthSuccess(\n sdkResult.data.accessToken || '',\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Invalid email or password',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signUp({ email, password });\n\n if (sdkResult.data) {\n // Only set auth state if we got a token back (email verification might be required)\n if (sdkResult.data.accessToken) {\n await handleAuthSuccess(\n sdkResult.data.accessToken,\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n }\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Sign up failed',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signOut = useCallback(async () => {\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n // Clear refresh interval if exists\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) {\n throw new Error('No user signed in');\n }\n\n const profileUpdate: Record<string, string | undefined> = {\n nickname: data.name,\n avatarUrl: data.avatarUrl,\n };\n\n const result = await insforge.auth.setProfile(profileUpdate);\n\n if (result.data) {\n const userResult = await insforge.auth.getCurrentUser();\n if (userResult.data) {\n const profile = userResult.data.profile;\n const updatedUser: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: profile?.nickname || '',\n avatarUrl: profile?.avatarUrl || '',\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, insforge]\n );\n\n const sendVerificationEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendVerificationEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const sendResetPasswordEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendResetPasswordEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const resetPassword = useCallback(\n async (token: string, newPassword: string) => {\n const sdkResult = await insforge.auth.resetPassword({\n newPassword,\n otp: token,\n });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const verifyEmail = useCallback(\n async (otp: string, email?: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp, email: email || undefined });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n accessToken: '',\n error: {\n message: sdkResult.error?.message || 'Email verification failed',\n },\n };\n }\n },\n [insforge]\n );\n\n const exchangeResetPasswordToken = useCallback(\n async (email: string, code: string) => {\n const sdkResult = await insforge.auth.exchangeResetPasswordToken({ email, code });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n error: {\n message: sdkResult.error?.message || 'Failed to exchange reset password token',\n },\n };\n }\n },\n [insforge]\n );\n\n const loginWithOAuth = useCallback(\n async (provider: OAuthProvider, redirectTo: string) => {\n const sdkResult = await insforge.auth.signInWithOAuth({\n provider,\n redirectTo: redirectTo || window.location.origin + afterSignInUrl,\n });\n if (sdkResult.error) {\n throw new Error(sdkResult.error.message);\n }\n },\n [insforge, afterSignInUrl]\n );\n\n return (\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeContext.Provider\n value={{\n user,\n isLoaded,\n isSignedIn: !!user,\n setUser,\n signIn,\n signUp,\n signOut,\n updateUser,\n reloadAuth: loadAuthState,\n baseUrl,\n sendVerificationEmail,\n sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n exchangeResetPasswordToken,\n getPublicAuthConfig,\n loginWithOAuth,\n }}\n >\n {children}\n </InsforgeContext.Provider>\n </NavigationProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n *\n * if (!isSignedIn) return <SignIn />;\n *\n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useInsforge(): InsforgeContextValue {\n const context = useContext(InsforgeContext);\n if (!context) {\n throw new Error('useInsforge must be used within InsforgeProvider');\n }\n return context;\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access authentication methods\n *\n * @returns Object containing:\n * - `signIn`: Function to sign in with email and password\n * - `signUp`: Function to sign up with email and password\n * - `signOut`: Function to sign out the current user\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `isSignedIn`: Boolean indicating if user is currently signed in\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\n *\n * async function handleLogin(email: string, password: string) {\n * try {\n * await signIn(email, password);\n * // User is now signed in\n * } catch (error) {\n * console.error('Sign in failed:', error);\n * }\n * }\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\n * {/* form fields *\\/}\n * </form>\n * );\n * }\n * ```\n */\nexport function useAuth() {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access current user data\n *\n * @returns Object containing:\n * - `user`: Current user object (InsforgeUser | null)\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `updateUser`: Function to update user profile data\n * - `setUser`: Internal function to manually set user state\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, isLoaded, updateUser } = useUser();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n * if (!user) return <div>Not signed in</div>;\n *\n * async function handleUpdate(name: string) {\n * await updateUser({ name });\n * }\n *\n * return (\n * <div>\n * <p>Email: {user.email}</p>\n * {user.name && <p>Name: {user.name}</p>}\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useUser() {\n const { user, isLoaded, updateUser, setUser } = useInsforge();\n return { user, isLoaded, updateUser, setUser };\n}\n","import { useState, useEffect } from 'react';\nimport type { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\n *\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\n *\n * This hook lazily fetches all public authentication configuration from the backend\n * only when the component mounts. Using it in other components will cause unnecessary\n * API calls on every page load.\n *\n * @returns Object containing OAuth providers, email auth config, and loading state\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\n * - `authConfig`: Email authentication configuration object with password rules\n * - `isLoaded`: Boolean indicating if the config has been fetched\n *\n * @example\n * ```tsx\n * // ✅ Correct usage - only in SignIn/SignUp components\n * function SignUp() {\n * const { oauthProviders, authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {oauthProviders.length}</p>\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @requires Must be used within InsforgeProvider\n */\nexport function usePublicAuthConfig(): {\n authConfig: GetPublicAuthConfigResponse | null;\n isLoaded: boolean;\n} {\n const { getPublicAuthConfig } = useInsforge();\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n async function fetchConfig() {\n const result = await getPublicAuthConfig();\n if (result) {\n setAuthConfig(result);\n } else {\n console.error('[usePublicAuthConfig] Failed to get public auth config');\n setAuthConfig(null);\n }\n setIsLoaded(true);\n }\n\n fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
package/dist/hooks.js CHANGED
@@ -3,6 +3,7 @@ import '@insforge/sdk';
3
3
  import 'react/jsx-runtime';
4
4
 
5
5
  // src/provider/InsforgeProvider.tsx
6
+ createContext(null);
6
7
  var InsforgeContext = createContext(void 0);
7
8
  function useInsforge() {
8
9
  const context = useContext(InsforgeContext);
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":["useState","useEffect"],"mappings":";;;;;AA8DA,IAAM,eAAA,GAAkB,cAAgD,MAAS,CAAA;AA8f1E,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;;;AC9hBO,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;ACCO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIA,SAA6C,IAAI,CAAA;AACrF,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,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,YAAY,QAAA,EAAS;AAChC","file":"hooks.js","sourcesContent":["import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { UserSchema, createClient } from '@insforge/sdk';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport {\n CreateSessionResponse,\n CreateUserResponse,\n GetPublicAuthConfigResponse,\n ResetPasswordResponse,\n} from '@insforge/shared-schemas';\n\ninterface InsforgeContextValue {\n // Auth state\n user: InsforgeUser | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n\n // Auth methods\n setUser: (user: InsforgeUser | null) => void;\n signIn: (\n email: string,\n password: string\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (\n email: string,\n password: string\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendVerificationEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n sendResetPasswordEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n otp: string,\n email?: string\n ) => Promise<{\n accessToken: string;\n user?: UserSchema;\n redirectTo?: string;\n error?: { message: string };\n } | null>;\n exchangeResetPasswordToken: (\n email: string,\n code: string\n ) => Promise<{ token: string; expiresAt?: string } | { error: { message: string } }>;\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) => Promise<void>;\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(undefined);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Manages user authentication state and provides all necessary context to child components.\n * Works with any React framework (Next.js, Vite, Remix, etc.).\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n *\n * export default function App() {\n * return (\n * <InsforgeProvider\n * baseUrl={process.env.VITE_INSFORGE_BASE_URL}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * baseUrl={baseUrl}\n * onSignIn={async (authToken) => {\n * await signIn(authToken);\n * }}\n * onSignOut={async () => {\n * await signOut();\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n}: InsforgeProviderProps) {\n // Auth state\n const [user, setUser] = useState<InsforgeUser | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const hasProcessedCallbackRef = useRef(false);\n\n // Initialize SDK client with lazy initialization - only runs once\n const [insforge] = useState(() => createClient({ baseUrl }));\n\n // Load auth state - returns explicit success/error status\n const loadAuthState = useCallback(async (): Promise<{\n success: boolean;\n error?: string;\n }> => {\n try {\n // Use SDK's getCurrentSession() to check for existing session\n const sessionResult = insforge.auth.getCurrentSession();\n const session = sessionResult.data?.session;\n const token = session?.accessToken || null;\n\n if (!token) {\n // No token, user is not authenticated\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'no_session' };\n }\n\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n // Token is valid, update user state with fresh data\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n setIsLoaded(true);\n return { success: true };\n } else {\n // Token invalid or expired\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'invalid_token' };\n }\n } catch (error) {\n // Token validation failed\n console.error('[InsforgeProvider] Token validation failed:', error);\n\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Authentication failed',\n };\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n // Capture the ref value when effect runs\n const intervalId = refreshIntervalRef.current;\n\n return () => {\n // Use the captured value in cleanup\n if (intervalId) {\n clearInterval(intervalId);\n }\n };\n }, [loadAuthState]);\n\n // Handle authentication callback from URL\n useEffect(() => {\n // Only run once and only after auth is loaded\n if (!isLoaded || hasProcessedCallbackRef.current) {\n return;\n }\n\n const searchParams = new URLSearchParams(window.location.search);\n const accessToken = searchParams.get('access_token');\n\n if (accessToken && !!user) {\n // Mark as processed\n hasProcessedCallbackRef.current = true;\n\n // Clean URL by removing query parameters\n const url = new URL(window.location.href);\n url.search = '';\n window.history.replaceState({}, '', url.toString());\n\n // Redirect to afterSignInUrl\n setTimeout(() => {\n window.location.href = afterSignInUrl;\n }, 100);\n }\n }, [isLoaded, user, afterSignInUrl]);\n\n const getPublicAuthConfig = useCallback(async () => {\n try {\n const result = await insforge.auth.getPublicAuthConfig();\n if (result.data) {\n return result.data;\n } else {\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\n return null;\n }\n } catch (error) {\n console.error('[InsforgeProvider] Failed to get public auth config:', error);\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (authToken: string, fallbackUser?: { id?: string; email?: string; name?: string }) => {\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n\n // Try to sync token to cookie if function provided\n if (onSignIn) {\n try {\n await onSignIn(authToken);\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error syncing token to cookie:', error.message);\n }\n }\n }\n } else if (fallbackUser) {\n // Fallback to basic user data if getCurrentUser fails\n const userData: InsforgeUser = {\n id: fallbackUser.id || '',\n email: fallbackUser.email || '',\n name: fallbackUser.name || '',\n avatarUrl: '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n }\n },\n [insforge, onAuthChange, onSignIn]\n );\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signInWithPassword({\n email,\n password,\n });\n\n if (sdkResult.data) {\n await handleAuthSuccess(\n sdkResult.data.accessToken || '',\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Invalid email or password',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signUp({ email, password });\n\n if (sdkResult.data) {\n // Only set auth state if we got a token back (email verification might be required)\n if (sdkResult.data.accessToken) {\n await handleAuthSuccess(\n sdkResult.data.accessToken,\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n }\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Sign up failed',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signOut = useCallback(async () => {\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n // Clear refresh interval if exists\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) {\n throw new Error('No user signed in');\n }\n\n const profileUpdate: Record<string, string | undefined> = {\n nickname: data.name,\n avatarUrl: data.avatarUrl,\n };\n\n const result = await insforge.auth.setProfile(profileUpdate);\n\n if (result.data) {\n const userResult = await insforge.auth.getCurrentUser();\n if (userResult.data) {\n const profile = userResult.data.profile;\n const updatedUser: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: profile?.nickname || '',\n avatarUrl: profile?.avatarUrl || '',\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, insforge]\n );\n\n const sendVerificationEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendVerificationEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const sendResetPasswordEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendResetPasswordEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const resetPassword = useCallback(\n async (token: string, newPassword: string) => {\n const sdkResult = await insforge.auth.resetPassword({\n newPassword,\n otp: token,\n });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const verifyEmail = useCallback(\n async (otp: string, email?: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp, email: email || undefined });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n accessToken: '',\n error: {\n message: sdkResult.error?.message || 'Email verification failed',\n },\n };\n }\n },\n [insforge]\n );\n\n const exchangeResetPasswordToken = useCallback(\n async (email: string, code: string) => {\n const sdkResult = await insforge.auth.exchangeResetPasswordToken({ email, code });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n error: {\n message: sdkResult.error?.message || 'Failed to exchange reset password token',\n },\n };\n }\n },\n [insforge]\n );\n\n const loginWithOAuth = useCallback(\n async (provider: OAuthProvider, redirectTo: string) => {\n const sdkResult = await insforge.auth.signInWithOAuth({\n provider,\n redirectTo: redirectTo || window.location.origin + afterSignInUrl,\n });\n if (sdkResult.error) {\n throw new Error(sdkResult.error.message);\n }\n },\n [insforge, afterSignInUrl]\n );\n\n return (\n <InsforgeContext.Provider\n value={{\n user,\n isLoaded,\n isSignedIn: !!user,\n setUser,\n signIn,\n signUp,\n signOut,\n updateUser,\n reloadAuth: loadAuthState,\n baseUrl,\n sendVerificationEmail,\n sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n exchangeResetPasswordToken,\n getPublicAuthConfig,\n loginWithOAuth,\n }}\n >\n {children}\n </InsforgeContext.Provider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n *\n * if (!isSignedIn) return <SignIn />;\n *\n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useInsforge(): InsforgeContextValue {\n const context = useContext(InsforgeContext);\n if (!context) {\n throw new Error('useInsforge must be used within InsforgeProvider');\n }\n return context;\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access authentication methods\n *\n * @returns Object containing:\n * - `signIn`: Function to sign in with email and password\n * - `signUp`: Function to sign up with email and password\n * - `signOut`: Function to sign out the current user\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `isSignedIn`: Boolean indicating if user is currently signed in\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\n *\n * async function handleLogin(email: string, password: string) {\n * try {\n * await signIn(email, password);\n * // User is now signed in\n * } catch (error) {\n * console.error('Sign in failed:', error);\n * }\n * }\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\n * {/* form fields *\\/}\n * </form>\n * );\n * }\n * ```\n */\nexport function useAuth() {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access current user data\n *\n * @returns Object containing:\n * - `user`: Current user object (InsforgeUser | null)\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `updateUser`: Function to update user profile data\n * - `setUser`: Internal function to manually set user state\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, isLoaded, updateUser } = useUser();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n * if (!user) return <div>Not signed in</div>;\n *\n * async function handleUpdate(name: string) {\n * await updateUser({ name });\n * }\n *\n * return (\n * <div>\n * <p>Email: {user.email}</p>\n * {user.name && <p>Name: {user.name}</p>}\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useUser() {\n const { user, isLoaded, updateUser, setUser } = useInsforge();\n return { user, isLoaded, updateUser, setUser };\n}\n","import { useState, useEffect } from 'react';\nimport type { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\n *\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\n *\n * This hook lazily fetches all public authentication configuration from the backend\n * only when the component mounts. Using it in other components will cause unnecessary\n * API calls on every page load.\n *\n * @returns Object containing OAuth providers, email auth config, and loading state\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\n * - `authConfig`: Email authentication configuration object with password rules\n * - `isLoaded`: Boolean indicating if the config has been fetched\n *\n * @example\n * ```tsx\n * // ✅ Correct usage - only in SignIn/SignUp components\n * function SignUp() {\n * const { oauthProviders, authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {oauthProviders.length}</p>\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @requires Must be used within InsforgeProvider\n */\nexport function usePublicAuthConfig(): {\n authConfig: GetPublicAuthConfigResponse | null;\n isLoaded: boolean;\n} {\n const { getPublicAuthConfig } = useInsforge();\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n async function fetchConfig() {\n const result = await getPublicAuthConfig();\n if (result) {\n setAuthConfig(result);\n } else {\n console.error('[usePublicAuthConfig] Failed to get public auth config');\n setAuthConfig(null);\n }\n setIsLoaded(true);\n }\n\n fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
1
+ {"version":3,"sources":["../src/navigation/NavigationContext.tsx","../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;AAG0B,cAAwC,IAAI;AC4DtE,IAAM,eAAA,GAAkBA,cAAgD,MAAS,CAAA;AAggB1E,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAAUC,WAAW,eAAe,CAAA;AAC1C,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,kDAAkD,CAAA;AAAA,EACpE;AACA,EAAA,OAAO,OAAA;AACT;;;ACjiBO,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;ACCO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,SAA6C,IAAI,CAAA;AACrF,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,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,MAAM,wDAAwD,CAAA;AACtE,QAAA,aAAA,CAAc,IAAI,CAAA;AAAA,MACpB;AACA,MAAA,WAAA,CAAY,IAAI,CAAA;AAAA,IAClB;AAEA,IAAA,WAAA,EAAY;AAAA,EACd,CAAA,EAAG,CAAC,mBAAmB,CAAC,CAAA;AAExB,EAAA,OAAO,EAAE,YAAY,QAAA,EAAS;AAChC","file":"hooks.js","sourcesContent":["import { createContext, useContext, ReactNode } from 'react';\nimport type { NavigationAdapter } from './types';\n\nconst NavigationContext = createContext<NavigationAdapter | null>(null);\n\nexport interface NavigationProviderProps {\n adapter: NavigationAdapter;\n children: ReactNode;\n}\n\n/**\n * Navigation Provider\n * Injects navigation adapter into the component tree\n */\nexport function NavigationProvider({ adapter, children }: NavigationProviderProps) {\n return <NavigationContext.Provider value={adapter}>{children}</NavigationContext.Provider>;\n}\n\n/**\n * Hook to access navigation adapter\n * @throws Error if used outside NavigationProvider\n */\nexport function useNavigationAdapter(): NavigationAdapter {\n const adapter = useContext(NavigationContext);\n\n if (!adapter) {\n throw new Error('useNavigationAdapter must be used within NavigationProvider');\n }\n\n return adapter;\n}\n","import {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from 'react';\nimport { UserSchema, createClient } from '@insforge/sdk';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport {\n CreateSessionResponse,\n CreateUserResponse,\n GetPublicAuthConfigResponse,\n ResetPasswordResponse,\n} from '@insforge/shared-schemas';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\n\ninterface InsforgeContextValue {\n // Auth state\n user: InsforgeUser | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n\n // Auth methods\n setUser: (user: InsforgeUser | null) => void;\n signIn: (\n email: string,\n password: string\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (\n email: string,\n password: string\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendVerificationEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n sendResetPasswordEmail: (email: string) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n otp: string,\n email?: string\n ) => Promise<{\n accessToken: string;\n user?: UserSchema;\n redirectTo?: string;\n error?: { message: string };\n } | null>;\n exchangeResetPasswordToken: (\n email: string,\n code: string\n ) => Promise<{ token: string; expiresAt?: string } | { error: { message: string } }>;\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) => Promise<void>;\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(undefined);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Manages user authentication state and provides all necessary context to child components.\n * Works with any React framework (Next.js, Vite, Remix, etc.).\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n *\n * export default function App() {\n * return (\n * <InsforgeProvider\n * baseUrl={process.env.VITE_INSFORGE_BASE_URL}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * baseUrl={baseUrl}\n * onSignIn={async (authToken) => {\n * await signIn(authToken);\n * }}\n * onSignOut={async () => {\n * await signOut();\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n}: InsforgeProviderProps) {\n // Auth state\n const [user, setUser] = useState<InsforgeUser | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);\n const hasProcessedCallbackRef = useRef(false);\n\n // Initialize SDK client with lazy initialization - only runs once\n const [insforge] = useState(() => createClient({ baseUrl }));\n\n // Load auth state - returns explicit success/error status\n const loadAuthState = useCallback(async (): Promise<{\n success: boolean;\n error?: string;\n }> => {\n try {\n // Use SDK's getCurrentSession() to check for existing session\n const sessionResult = insforge.auth.getCurrentSession();\n const session = sessionResult.data?.session;\n const token = session?.accessToken || null;\n\n if (!token) {\n // No token, user is not authenticated\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'no_session' };\n }\n\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n // Token is valid, update user state with fresh data\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n setIsLoaded(true);\n return { success: true };\n } else {\n // Token invalid or expired\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n return { success: false, error: 'invalid_token' };\n }\n } catch (error) {\n // Token validation failed\n console.error('[InsforgeProvider] Token validation failed:', error);\n\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n setIsLoaded(true);\n\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Authentication failed',\n };\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n // Capture the ref value when effect runs\n const intervalId = refreshIntervalRef.current;\n\n return () => {\n // Use the captured value in cleanup\n if (intervalId) {\n clearInterval(intervalId);\n }\n };\n }, [loadAuthState]);\n\n // Handle authentication callback from URL\n useEffect(() => {\n // Only run once and only after auth is loaded\n if (!isLoaded || hasProcessedCallbackRef.current) {\n return;\n }\n\n const searchParams = new URLSearchParams(window.location.search);\n const accessToken = searchParams.get('access_token');\n\n if (accessToken && !!user) {\n // Mark as processed\n hasProcessedCallbackRef.current = true;\n\n // Clean URL by removing query parameters\n const url = new URL(window.location.href);\n url.search = '';\n window.history.replaceState({}, '', url.toString());\n\n // Redirect to afterSignInUrl\n setTimeout(() => {\n window.location.href = afterSignInUrl;\n }, 100);\n }\n }, [isLoaded, user, afterSignInUrl]);\n\n const getPublicAuthConfig = useCallback(async () => {\n try {\n const result = await insforge.auth.getPublicAuthConfig();\n if (result.data) {\n return result.data;\n } else {\n console.error('[InsforgeProvider] Failed to get public auth config:', result.error);\n return null;\n }\n } catch (error) {\n console.error('[InsforgeProvider] Failed to get public auth config:', error);\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (authToken: string, fallbackUser?: { id?: string; email?: string; name?: string }) => {\n const userResult = await insforge.auth.getCurrentUser();\n\n if (userResult.data) {\n const profile = userResult.data.profile;\n const userData: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: (profile?.nickname as string | undefined) || '',\n avatarUrl: (profile?.avatarUrl as string | undefined) || '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n\n // Try to sync token to cookie if function provided\n if (onSignIn) {\n try {\n await onSignIn(authToken);\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error syncing token to cookie:', error.message);\n }\n }\n }\n } else if (fallbackUser) {\n // Fallback to basic user data if getCurrentUser fails\n const userData: InsforgeUser = {\n id: fallbackUser.id || '',\n email: fallbackUser.email || '',\n name: fallbackUser.name || '',\n avatarUrl: '',\n };\n\n setUser(userData);\n\n if (onAuthChange) {\n onAuthChange(userData);\n }\n }\n },\n [insforge, onAuthChange, onSignIn]\n );\n\n const signIn = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signInWithPassword({\n email,\n password,\n });\n\n if (sdkResult.data) {\n await handleAuthSuccess(\n sdkResult.data.accessToken || '',\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Invalid email or password',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signUp = useCallback(\n async (email: string, password: string) => {\n const sdkResult = await insforge.auth.signUp({ email, password });\n\n if (sdkResult.data) {\n // Only set auth state if we got a token back (email verification might be required)\n if (sdkResult.data.accessToken) {\n await handleAuthSuccess(\n sdkResult.data.accessToken,\n sdkResult.data.user\n ? {\n id: sdkResult.data.user.id,\n email: sdkResult.data.user.email,\n name: sdkResult.data.user.name,\n }\n : undefined\n );\n }\n return sdkResult.data;\n } else {\n // Return the full error object to preserve statusCode and other properties\n return {\n error: sdkResult.error?.message || 'Sign up failed',\n statusCode: sdkResult.error?.statusCode,\n errorCode: sdkResult.error?.error,\n };\n }\n },\n [insforge, handleAuthSuccess]\n );\n\n const signOut = useCallback(async () => {\n await insforge.auth.signOut();\n\n // Clear cookie if function provided\n if (onSignOut) {\n try {\n await onSignOut();\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('[InsforgeProvider] Error clearing cookie:', error.message);\n }\n }\n }\n\n // Clear refresh interval if exists\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n\n setUser(null);\n if (onAuthChange) {\n onAuthChange(null);\n }\n }, [insforge, onAuthChange, onSignOut]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) {\n throw new Error('No user signed in');\n }\n\n const profileUpdate: Record<string, string | undefined> = {\n nickname: data.name,\n avatarUrl: data.avatarUrl,\n };\n\n const result = await insforge.auth.setProfile(profileUpdate);\n\n if (result.data) {\n const userResult = await insforge.auth.getCurrentUser();\n if (userResult.data) {\n const profile = userResult.data.profile;\n const updatedUser: InsforgeUser = {\n id: userResult.data.user.id,\n email: userResult.data.user.email,\n name: profile?.nickname || '',\n avatarUrl: profile?.avatarUrl || '',\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, insforge]\n );\n\n const sendVerificationEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendVerificationEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const sendResetPasswordEmail = useCallback(\n async (email: string) => {\n const sdkResult = await insforge.auth.sendResetPasswordEmail({ email });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const resetPassword = useCallback(\n async (token: string, newPassword: string) => {\n const sdkResult = await insforge.auth.resetPassword({\n newPassword,\n otp: token,\n });\n return sdkResult.data;\n },\n [insforge]\n );\n\n const verifyEmail = useCallback(\n async (otp: string, email?: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp, email: email || undefined });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n accessToken: '',\n error: {\n message: sdkResult.error?.message || 'Email verification failed',\n },\n };\n }\n },\n [insforge]\n );\n\n const exchangeResetPasswordToken = useCallback(\n async (email: string, code: string) => {\n const sdkResult = await insforge.auth.exchangeResetPasswordToken({ email, code });\n if (sdkResult.data) {\n return sdkResult.data;\n } else {\n return {\n error: {\n message: sdkResult.error?.message || 'Failed to exchange reset password token',\n },\n };\n }\n },\n [insforge]\n );\n\n const loginWithOAuth = useCallback(\n async (provider: OAuthProvider, redirectTo: string) => {\n const sdkResult = await insforge.auth.signInWithOAuth({\n provider,\n redirectTo: redirectTo || window.location.origin + afterSignInUrl,\n });\n if (sdkResult.error) {\n throw new Error(sdkResult.error.message);\n }\n },\n [insforge, afterSignInUrl]\n );\n\n return (\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeContext.Provider\n value={{\n user,\n isLoaded,\n isSignedIn: !!user,\n setUser,\n signIn,\n signUp,\n signOut,\n updateUser,\n reloadAuth: loadAuthState,\n baseUrl,\n sendVerificationEmail,\n sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n exchangeResetPasswordToken,\n getPublicAuthConfig,\n loginWithOAuth,\n }}\n >\n {children}\n </InsforgeContext.Provider>\n </NavigationProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n *\n * if (!isSignedIn) return <SignIn />;\n *\n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useInsforge(): InsforgeContextValue {\n const context = useContext(InsforgeContext);\n if (!context) {\n throw new Error('useInsforge must be used within InsforgeProvider');\n }\n return context;\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access authentication methods\n *\n * @returns Object containing:\n * - `signIn`: Function to sign in with email and password\n * - `signUp`: Function to sign up with email and password\n * - `signOut`: Function to sign out the current user\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `isSignedIn`: Boolean indicating if user is currently signed in\n *\n * @example\n * ```tsx\n * function LoginForm() {\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\n *\n * async function handleLogin(email: string, password: string) {\n * try {\n * await signIn(email, password);\n * // User is now signed in\n * } catch (error) {\n * console.error('Sign in failed:', error);\n * }\n * }\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\n * {/* form fields *\\/}\n * </form>\n * );\n * }\n * ```\n */\nexport function useAuth() {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to access current user data\n *\n * @returns Object containing:\n * - `user`: Current user object (InsforgeUser | null)\n * - `isLoaded`: Boolean indicating if auth state has been loaded\n * - `updateUser`: Function to update user profile data\n * - `setUser`: Internal function to manually set user state\n *\n * @example\n * ```tsx\n * function UserProfile() {\n * const { user, isLoaded, updateUser } = useUser();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n * if (!user) return <div>Not signed in</div>;\n *\n * async function handleUpdate(name: string) {\n * await updateUser({ name });\n * }\n *\n * return (\n * <div>\n * <p>Email: {user.email}</p>\n * {user.name && <p>Name: {user.name}</p>}\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useUser() {\n const { user, isLoaded, updateUser, setUser } = useInsforge();\n return { user, isLoaded, updateUser, setUser };\n}\n","import { useState, useEffect } from 'react';\nimport type { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\n/**\n * Hook to get all public authentication configuration (OAuth + Email) from Insforge backend\n *\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\n *\n * This hook lazily fetches all public authentication configuration from the backend\n * only when the component mounts. Using it in other components will cause unnecessary\n * API calls on every page load.\n *\n * @returns Object containing OAuth providers, email auth config, and loading state\n * - `oauthProviders`: Array of enabled OAuth provider names (e.g., ['google', 'github'])\n * - `authConfig`: Email authentication configuration object with password rules\n * - `isLoaded`: Boolean indicating if the config has been fetched\n *\n * @example\n * ```tsx\n * // ✅ Correct usage - only in SignIn/SignUp components\n * function SignUp() {\n * const { oauthProviders, authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {oauthProviders.length}</p>\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @requires Must be used within InsforgeProvider\n */\nexport function usePublicAuthConfig(): {\n authConfig: GetPublicAuthConfigResponse | null;\n isLoaded: boolean;\n} {\n const { getPublicAuthConfig } = useInsforge();\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n useEffect(() => {\n async function fetchConfig() {\n const result = await getPublicAuthConfig();\n if (result) {\n setAuthConfig(result);\n } else {\n console.error('[usePublicAuthConfig] Failed to get public auth config');\n setAuthConfig(null);\n }\n setIsLoaded(true);\n }\n\n fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -15,13 +15,52 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
15
15
  'use strict';
16
16
 
17
17
  var react = require('react');
18
- var reactRouterDom = require('react-router-dom');
19
- var sdk = require('@insforge/sdk');
20
18
  var jsxRuntime = require('react/jsx-runtime');
19
+ var sdk = require('@insforge/sdk');
21
20
  var lucideReact = require('lucide-react');
22
21
  var zod = require('zod');
23
22
 
24
23
  // src/components/SignIn.tsx
24
+ var NavigationContext = react.createContext(null);
25
+ function NavigationProvider({ adapter, children }) {
26
+ return /* @__PURE__ */ jsxRuntime.jsx(NavigationContext.Provider, { value: adapter, children });
27
+ }
28
+ function useNavigationAdapter() {
29
+ const adapter = react.useContext(NavigationContext);
30
+ if (!adapter) {
31
+ throw new Error("useNavigationAdapter must be used within NavigationProvider");
32
+ }
33
+ return adapter;
34
+ }
35
+ var BrowserNavigationAdapter = {
36
+ /**
37
+ * Returns URLSearchParams from current window.location.search
38
+ * Note: Not reactive - reads once on component mount
39
+ * This is sufficient for auth flows where we read initial URL params
40
+ */
41
+ useSearchParams() {
42
+ const [searchParams] = react.useState(() => {
43
+ if (typeof window === "undefined") {
44
+ return new URLSearchParams();
45
+ }
46
+ return new URLSearchParams(window.location.search);
47
+ });
48
+ return searchParams;
49
+ },
50
+ /**
51
+ * Native <a> tag for navigation
52
+ * Uses full page reload
53
+ */
54
+ Link({ href, className, children }) {
55
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, className, children });
56
+ }
57
+ };
58
+
59
+ // src/navigation/useSearchParams.ts
60
+ function useSearchParams() {
61
+ const adapter = useNavigationAdapter();
62
+ return adapter.useSearchParams();
63
+ }
25
64
  var InsforgeContext = react.createContext(void 0);
26
65
  function InsforgeProvider({
27
66
  children,
@@ -349,7 +388,7 @@ function InsforgeProvider({
349
388
  },
350
389
  [insforge, afterSignInUrl]
351
390
  );
352
- return /* @__PURE__ */ jsxRuntime.jsx(
391
+ return /* @__PURE__ */ jsxRuntime.jsx(NavigationProvider, { adapter: BrowserNavigationAdapter, children: /* @__PURE__ */ jsxRuntime.jsx(
353
392
  InsforgeContext.Provider,
354
393
  {
355
394
  value: {
@@ -373,7 +412,7 @@ function InsforgeProvider({
373
412
  },
374
413
  children
375
414
  }
376
- );
415
+ ) });
377
416
  }
378
417
  function useInsforge() {
379
418
  const context = react.useContext(InsforgeContext);
@@ -591,7 +630,8 @@ function AuthPasswordField({
591
630
  onFocus,
592
631
  ...props
593
632
  }) {
594
- const [searchParams] = reactRouterDom.useSearchParams();
633
+ const searchParams = useSearchParams();
634
+ const { Link } = useNavigationAdapter();
595
635
  const [showPassword, setShowPassword] = react.useState(false);
596
636
  const [showStrength, setShowStrength] = react.useState(false);
597
637
  const resolvedForgotPasswordHref = react.useMemo(
@@ -607,7 +647,7 @@ function AuthPasswordField({
607
647
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-passwordField if-internal-p5w9m7", children: [
608
648
  (label || forgotPasswordLink) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-passwordField-labelRow", children: [
609
649
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "if-passwordField-label", children: label }),
610
- forgotPasswordLink && resolvedForgotPasswordHref && /* @__PURE__ */ jsxRuntime.jsx("a", { href: resolvedForgotPasswordHref, className: "if-passwordField-forgotLink", children: forgotPasswordLink.text || "Forget Password?" })
650
+ forgotPasswordLink && resolvedForgotPasswordHref && /* @__PURE__ */ jsxRuntime.jsx(Link, { href: resolvedForgotPasswordHref, className: "if-passwordField-forgotLink", children: forgotPasswordLink.text || "Forget Password?" })
611
651
  ] }),
612
652
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "if-passwordField-inputWrapper", children: [
613
653
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -659,12 +699,13 @@ function AuthSubmitButton({
659
699
  );
660
700
  }
661
701
  function AuthLink({ text, linkText, href }) {
662
- const [searchParams] = reactRouterDom.useSearchParams();
702
+ const searchParams = useSearchParams();
703
+ const { Link } = useNavigationAdapter();
663
704
  const finalHref = resolveAuthUrl(href, searchParams);
664
705
  return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "if-authLink if-internal-al5w9p", children: [
665
706
  text && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "if-authLink-text", children: text }),
666
707
  text && " ",
667
- /* @__PURE__ */ jsxRuntime.jsx("a", { href: finalHref, className: "if-authLink-link", children: linkText })
708
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { href: finalHref, className: "if-authLink-link", children: linkText })
668
709
  ] });
669
710
  }
670
711
  function AuthDivider({ text = "or" }) {
@@ -1307,7 +1348,7 @@ function SignIn({ onError, ...uiProps }) {
1307
1348
  const [loading, setLoading] = react.useState(false);
1308
1349
  const [step, setStep] = react.useState("form");
1309
1350
  const [oauthLoading] = react.useState(null);
1310
- const [searchParams] = reactRouterDom.useSearchParams();
1351
+ const searchParams = useSearchParams();
1311
1352
  const redirectUrl = searchParams.get("redirect");
1312
1353
  async function handleSubmit(e) {
1313
1354
  e.preventDefault();
@@ -1559,7 +1600,7 @@ function SignUp({ onError, ...uiProps }) {
1559
1600
  const [loading, setLoading] = react.useState(false);
1560
1601
  const [step, setStep] = react.useState("form");
1561
1602
  const [oauthLoading] = react.useState(null);
1562
- const [searchParams] = reactRouterDom.useSearchParams();
1603
+ const searchParams = useSearchParams();
1563
1604
  const redirectUrl = searchParams.get("redirect");
1564
1605
  async function handleSubmit(e) {
1565
1606
  e.preventDefault();
@@ -1824,7 +1865,7 @@ function ResetPasswordForm({
1824
1865
  function ForgotPassword({ onError, ...uiProps }) {
1825
1866
  const { sendResetPasswordEmail, exchangeResetPasswordToken, resetPassword } = useInsforge();
1826
1867
  const { authConfig } = usePublicAuthConfig();
1827
- const [searchParams] = reactRouterDom.useSearchParams();
1868
+ const searchParams = useSearchParams();
1828
1869
  const [step, setStep] = react.useState("email");
1829
1870
  const [email, setEmail] = react.useState("");
1830
1871
  const [resetToken, setResetToken] = react.useState("");
@@ -1980,7 +2021,7 @@ function ForgotPassword({ onError, ...uiProps }) {
1980
2021
  );
1981
2022
  }
1982
2023
  function ResetPassword({ onError, ...uiProps }) {
1983
- const [searchParams] = reactRouterDom.useSearchParams();
2024
+ const searchParams = useSearchParams();
1984
2025
  const token = searchParams.get("token");
1985
2026
  const { resetPassword } = useInsforge();
1986
2027
  const { authConfig } = usePublicAuthConfig();
@@ -2401,41 +2442,6 @@ function useUser() {
2401
2442
  const { user, isLoaded, updateUser, setUser } = useInsforge();
2402
2443
  return { user, isLoaded, updateUser, setUser };
2403
2444
  }
2404
- function RedirectToAuth({
2405
- baseUrl,
2406
- path,
2407
- afterSignInUrl
2408
- }) {
2409
- react.useEffect(() => {
2410
- const currentUrl = window.location.origin + afterSignInUrl;
2411
- const authUrl = new URL(path, baseUrl);
2412
- authUrl.searchParams.set("redirect", currentUrl);
2413
- window.location.replace(authUrl.toString());
2414
- }, [baseUrl, path, afterSignInUrl]);
2415
- return null;
2416
- }
2417
- function getInsforgeRoutes(config) {
2418
- const { baseUrl, builtInAuth = true, paths = {}, afterSignInUrl = "/" } = config;
2419
- const { signIn = "/sign-in", signUp = "/sign-up", forgotPassword = "/forgot-password" } = paths;
2420
- const routes = [];
2421
- if (builtInAuth) {
2422
- routes.push(
2423
- {
2424
- path: signIn,
2425
- element: /* @__PURE__ */ jsxRuntime.jsx(RedirectToAuth, { baseUrl, path: "/auth/sign-in", afterSignInUrl })
2426
- },
2427
- {
2428
- path: signUp,
2429
- element: /* @__PURE__ */ jsxRuntime.jsx(RedirectToAuth, { baseUrl, path: "/auth/sign-up", afterSignInUrl })
2430
- },
2431
- {
2432
- path: forgotPassword,
2433
- element: /* @__PURE__ */ jsxRuntime.jsx(RedirectToAuth, { baseUrl, path: "/auth/forgot-password" })
2434
- }
2435
- );
2436
- }
2437
- return routes;
2438
- }
2439
2445
 
2440
2446
  exports.AuthBranding = AuthBranding;
2441
2447
  exports.AuthContainer = AuthContainer;
@@ -2452,9 +2458,11 @@ exports.AuthPasswordStrengthIndicator = AuthPasswordStrengthIndicator;
2452
2458
  exports.AuthResetPasswordVerificationStep = AuthResetPasswordVerificationStep;
2453
2459
  exports.AuthSubmitButton = AuthSubmitButton;
2454
2460
  exports.AuthVerificationCodeInput = AuthVerificationCodeInput;
2461
+ exports.BrowserNavigationAdapter = BrowserNavigationAdapter;
2455
2462
  exports.ForgotPassword = ForgotPassword;
2456
2463
  exports.ForgotPasswordForm = ForgotPasswordForm;
2457
2464
  exports.InsforgeProvider = InsforgeProvider;
2465
+ exports.NavigationProvider = NavigationProvider;
2458
2466
  exports.OAUTH_PROVIDER_CONFIG = OAUTH_PROVIDER_CONFIG;
2459
2467
  exports.Protect = Protect;
2460
2468
  exports.ResetPassword = ResetPassword;
@@ -2472,14 +2480,15 @@ exports.checkPasswordStrength = checkPasswordStrength;
2472
2480
  exports.createPasswordSchema = createPasswordSchema;
2473
2481
  exports.emailSchema = emailSchema;
2474
2482
  exports.getAllProviderConfigs = getAllProviderConfigs;
2475
- exports.getInsforgeRoutes = getInsforgeRoutes;
2476
2483
  exports.getProviderConfig = getProviderConfig;
2477
2484
  exports.passwordSchema = passwordSchema;
2478
2485
  exports.resolveAuthPath = resolveAuthPath;
2479
2486
  exports.resolveAuthUrl = resolveAuthUrl;
2480
2487
  exports.useAuth = useAuth;
2481
2488
  exports.useInsforge = useInsforge;
2489
+ exports.useNavigationAdapter = useNavigationAdapter;
2482
2490
  exports.usePublicAuthConfig = usePublicAuthConfig;
2491
+ exports.useSearchParams = useSearchParams;
2483
2492
  exports.useUser = useUser;
2484
2493
  exports.validateEmail = validateEmail;
2485
2494
  exports.validatePassword = validatePassword;