@insforge/react 1.0.7 → 1.0.9-dev.2

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
@@ -24,7 +24,7 @@ function useInsforge() {
24
24
  sendVerificationEmail: () => Promise.resolve(null),
25
25
  sendResetPasswordEmail: () => Promise.resolve(null),
26
26
  resetPassword: () => Promise.resolve(null),
27
- verifyEmail: () => Promise.resolve(null),
27
+ verifyEmail: () => Promise.resolve({ error: "SSR mode" }),
28
28
  exchangeResetPasswordToken: () => Promise.resolve({ error: { message: "SSR mode" } }),
29
29
  loginWithOAuth: () => Promise.resolve(),
30
30
  getPublicAuthConfig: () => Promise.resolve(null),
@@ -1 +1 @@
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","InsforgeContext","useState","useEffect"],"mappings":"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","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 return {\n useSearchParams: () => new URLSearchParams(),\n Link: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n };\n }\n\n return adapter;\n}\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\nimport { InsforgeManager, type InsforgeManagerState } from '../core/InsforgeManager';\nimport { InsforgeContext, type InsforgeContextValue } from '../contexts';\nimport { StyleProvider } from '../styles/StyleProvider';\nimport { InsForgeClient } from '@insforge/sdk';\n\nexport interface InitialAuthState {\n user?: InsforgeUser | null;\n userId?: string | null;\n}\n\nexport interface InsforgeProviderProps {\n /**\n * The base URL of the InsForge backend.\n * @deprecated This prop is no longer used.\n * Use the client prop instead.\n */\n baseUrl?: string;\n children: ReactNode;\n /**\n * The InsForge SDK client instance.\n */\n client: InsForgeClient;\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 onRefresh?: (authToken: string) => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal - Not intended for public use, used by Next.js package\n */\n initialState?: InitialAuthState;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Uses singleton InsforgeManager to manage state across packages.\n * Context only subscribes to Manager and triggers React re-renders.\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n * import { createClient } from '@insforge/sdk';\n *\n * const client = createClient({\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\n * });\n *\n * export default function MyApp() {\n * return (\n * <InsforgeProvider client={client}>\n * <App />\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * client={client}\n * afterSignInUrl=\"/dashboard\"\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 InsforgeProviderCore({\n children,\n client,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n initialState,\n}: InsforgeProviderProps) {\n // Get singleton Manager instance\n const manager: InsforgeManager = useMemo(\n () =>\n InsforgeManager.getInstance({\n client,\n afterSignInUrl,\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n }),\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\n );\n\n // Set initialState if provided (from Server Component)\n // This must happen during render, before useState initialization\n if (initialState) {\n const currentState = manager.getState();\n if (currentState.userId === undefined && initialState.userId !== undefined) {\n manager.setInitialState(initialState);\n }\n }\n\n // Subscribe to Manager state\n // Start with initial state from manager (will include initialState if set above)\n const [state, setState] = useState<InsforgeManagerState>(() => manager.getState());\n\n useEffect(() => {\n // Subscribe to state changes\n const unsubscribe = manager.subscribe((newState: InsforgeManagerState) => {\n setState(newState);\n });\n\n // Initialize auth state only on client side (after hydration)\n // This prevents hydration mismatches by ensuring SSR and client initial render match\n void manager.initialize().then(() => {\n const params = new URLSearchParams(window.location.search);\n if (params.has('access_token')) {\n const url = new URL(window.location.href);\n url.searchParams.delete('access_token');\n url.searchParams.delete('user_id');\n url.searchParams.delete('email');\n url.searchParams.delete('name');\n url.searchParams.delete('csrf_token');\n url.searchParams.delete('error');\n\n window.history.replaceState({}, document.title, url.toString());\n }\n });\n\n return () => {\n unsubscribe();\n };\n }, [manager]);\n\n // Create stable method references that delegate to manager\n const contextValue = useMemo<InsforgeContextValue>(\n () => ({\n // State from Manager\n user: state.user,\n userId: state.userId,\n isLoaded: state.isLoaded,\n isSignedIn: state.isSignedIn,\n\n // Methods delegated to Manager\n setUser: (user: InsforgeUser | null) => manager.setUser(user),\n signIn: (email: string, password: string) => manager.signIn(email, password),\n signUp: (email: string, password: string) => manager.signUp(email, password),\n signOut: () => manager.signOut(),\n updateUser: (data: InsforgeUser['profile']) => manager.updateUser(data),\n reloadAuth: () => manager.reloadAuth(),\n sendVerificationEmail: (email: string) => manager.sendVerificationEmail(email),\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\n resetPassword: (token: string, newPassword: string) =>\n manager.resetPassword(token, newPassword),\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\n exchangeResetPasswordToken: (email: string, code: string) =>\n manager.exchangeResetPasswordToken(email, code),\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\n manager.loginWithOAuth(provider, redirectTo),\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\n\n // Config\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\n }),\n [state, manager]\n );\n\n return <InsforgeContext.Provider value={contextValue}>{children}</InsforgeContext.Provider>;\n}\n\nexport function InsforgeProvider(props: InsforgeProviderProps) {\n return (\n <StyleProvider>\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeProviderCore {...props} />\n </NavigationProvider>\n </StyleProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * Works seamlessly across packages thanks to singleton Manager.\n * Context instance is guaranteed to be consistent.\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: InsforgeContextValue | undefined = useContext(InsforgeContext);\n\n if (!context) {\n return {\n user: undefined,\n userId: undefined,\n isLoaded: false,\n isSignedIn: undefined,\n setUser: () => {},\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\n signOut: () => Promise.resolve(),\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\n sendVerificationEmail: () => Promise.resolve(null),\n sendResetPasswordEmail: () => Promise.resolve(null),\n resetPassword: () => Promise.resolve(null),\n verifyEmail: () => Promise.resolve(null),\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\n loginWithOAuth: () => Promise.resolve(),\n getPublicAuthConfig: () => Promise.resolve(null),\n baseUrl: '',\n afterSignInUrl: '/',\n };\n }\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 { InsforgeUser } from '@insforge/shared';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\nexport interface useUserReturn {\n user: InsforgeUser | null | undefined;\n isLoaded: boolean;\n updateUser: (data: InsforgeUser['profile']) => Promise<{ error: string } | null>;\n setUser: (user: InsforgeUser | null) => void;\n}\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 (returns { error: string } | null)\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 * const result = await updateUser({ name });\n * if (result?.error) {\n * console.error(result.error);\n * } else {\n * console.log('User updated successfully');\n * }\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(): useUserReturn {\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 * - `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 { authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {authConfig?.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 void 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","InsforgeContext","useState","useEffect"],"mappings":";;;;;;;AAG0BA,oBAAwC,IAAI;ACwM/D,SAAS,WAAA,GAAoC;AAClD,EAAA,MAAM,OAAA,GAA4CC,iBAAWC,uBAAe,CAAA;AAE5E,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,MAAA;AAAA,MACN,MAAA,EAAQ,MAAA;AAAA,MACR,QAAA,EAAU,KAAA;AAAA,MACV,UAAA,EAAY,MAAA;AAAA,MACZ,SAAS,MAAM;AAAA,MAAC,CAAA;AAAA,MAChB,QAAQ,MAAM,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,YAAY,CAAA;AAAA,MACnD,QAAQ,MAAM,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,YAAY,CAAA;AAAA,MACnD,OAAA,EAAS,MAAM,OAAA,CAAQ,OAAA,EAAQ;AAAA,MAC/B,YAAY,MAAM,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,YAAY,CAAA;AAAA,MACvD,UAAA,EAAY,MAAM,OAAA,CAAQ,OAAA,CAAQ,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,UAAA,EAAY,CAAA;AAAA,MACvE,qBAAA,EAAuB,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AAAA,MACjD,sBAAA,EAAwB,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AAAA,MAClD,aAAA,EAAe,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AAAA,MACzC,aAAa,MAAM,OAAA,CAAQ,QAAQ,EAAE,KAAA,EAAO,YAAY,CAAA;AAAA,MACxD,0BAAA,EAA4B,MAAM,OAAA,CAAQ,OAAA,CAAQ,EAAE,OAAO,EAAE,OAAA,EAAS,UAAA,EAAW,EAAG,CAAA;AAAA,MACpF,cAAA,EAAgB,MAAM,OAAA,CAAQ,OAAA,EAAQ;AAAA,MACtC,mBAAA,EAAqB,MAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AAAA,MAC/C,OAAA,EAAS,EAAA;AAAA,MACT,cAAA,EAAgB;AAAA,KAClB;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;;;AClMO,SAAS,OAAA,GAMd;AACA,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;;;ACAO,SAAS,OAAA,GAAyB;AACvC,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;ACbO,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,KAAK,WAAA,EAAY;AAAA,EACnB,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 return {\n useSearchParams: () => new URLSearchParams(),\n Link: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n };\n }\n\n return adapter;\n}\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\nimport { InsforgeManager, type InsforgeManagerState } from '../core/InsforgeManager';\nimport { InsforgeContext, type InsforgeContextValue } from '../contexts';\nimport { StyleProvider } from '../styles/StyleProvider';\nimport { InsForgeClient } from '@insforge/sdk';\n\nexport interface InitialAuthState {\n user?: InsforgeUser | null;\n userId?: string | null;\n}\n\nexport interface InsforgeProviderProps {\n /**\n * The base URL of the InsForge backend.\n * @deprecated This prop is no longer used.\n * Use the client prop instead.\n */\n baseUrl?: string;\n children: ReactNode;\n /**\n * The InsForge SDK client instance.\n */\n client: InsForgeClient;\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, user: InsforgeUser) => Promise<void>;\n onSignOut?: () => Promise<void>;\n onRefresh?: (authToken: string, user: InsforgeUser) => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal - Not intended for public use, used by Next.js package\n */\n initialState?: InitialAuthState;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Uses singleton InsforgeManager to manage state across packages.\n * Context only subscribes to Manager and triggers React re-renders.\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n * import { createClient } from '@insforge/sdk';\n *\n * const client = createClient({\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\n * });\n *\n * export default function MyApp() {\n * return (\n * <InsforgeProvider client={client}>\n * <App />\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * client={client}\n * afterSignInUrl=\"/dashboard\"\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 InsforgeProviderCore({\n children,\n client,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n initialState,\n}: InsforgeProviderProps) {\n // Get singleton Manager instance\n const manager: InsforgeManager = useMemo(\n () =>\n InsforgeManager.getInstance({\n client,\n afterSignInUrl,\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n }),\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\n );\n\n // Set initialState if provided (from Server Component)\n // This must happen during render, before useState initialization\n if (initialState) {\n const currentState = manager.getState();\n if (currentState.userId === undefined && initialState.userId !== undefined) {\n manager.setInitialState(initialState);\n }\n }\n\n // Subscribe to Manager state\n // Start with initial state from manager (will include initialState if set above)\n const [state, setState] = useState<InsforgeManagerState>(() => manager.getState());\n\n useEffect(() => {\n // Subscribe to state changes\n const unsubscribe = manager.subscribe((newState: InsforgeManagerState) => {\n setState(newState);\n });\n\n // Initialize auth state\n void manager.initialize();\n\n return () => {\n unsubscribe();\n };\n }, [manager]);\n\n // Create stable method references that delegate to manager\n const contextValue = useMemo<InsforgeContextValue>(\n () => ({\n // State from Manager\n user: state.user,\n userId: state.userId,\n isLoaded: state.isLoaded,\n isSignedIn: state.isSignedIn,\n\n // Methods delegated to Manager\n setUser: (user: InsforgeUser | null) => manager.setUser(user),\n signIn: (email: string, password: string) => manager.signIn(email, password),\n signUp: (email: string, password: string) => manager.signUp(email, password),\n signOut: () => manager.signOut(),\n updateUser: (data: InsforgeUser['profile']) => manager.updateUser(data),\n reloadAuth: () => manager.reloadAuth(),\n sendVerificationEmail: (email: string) => manager.sendVerificationEmail(email),\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\n resetPassword: (token: string, newPassword: string) =>\n manager.resetPassword(token, newPassword),\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\n exchangeResetPasswordToken: (email: string, code: string) =>\n manager.exchangeResetPasswordToken(email, code),\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\n manager.loginWithOAuth(provider, redirectTo),\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\n\n // Config\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\n }),\n [state, manager]\n );\n\n return <InsforgeContext.Provider value={contextValue}>{children}</InsforgeContext.Provider>;\n}\n\nexport function InsforgeProvider(props: InsforgeProviderProps) {\n return (\n <StyleProvider>\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeProviderCore {...props} />\n </NavigationProvider>\n </StyleProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * Works seamlessly across packages thanks to singleton Manager.\n * Context instance is guaranteed to be consistent.\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: InsforgeContextValue | undefined = useContext(InsforgeContext);\n\n if (!context) {\n return {\n user: undefined,\n userId: undefined,\n isLoaded: false,\n isSignedIn: undefined,\n setUser: () => {},\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\n signOut: () => Promise.resolve(),\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\n sendVerificationEmail: () => Promise.resolve(null),\n sendResetPasswordEmail: () => Promise.resolve(null),\n resetPassword: () => Promise.resolve(null),\n verifyEmail: () => Promise.resolve({ error: 'SSR mode' }),\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\n loginWithOAuth: () => Promise.resolve(),\n getPublicAuthConfig: () => Promise.resolve(null),\n baseUrl: '',\n afterSignInUrl: '/',\n };\n }\n\n return context;\n}\n","import { CreateSessionResponse, CreateUserResponse } from '@insforge/shared-schemas';\nimport { 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 signIn: (email: string, password: string) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (email: string, password: string) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<any>;\n isLoaded: boolean;\n isSignedIn: boolean | undefined;\n} {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { InsforgeUser } from '@insforge/shared';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\nexport interface useUserReturn {\n user: InsforgeUser | null | undefined;\n isLoaded: boolean;\n updateUser: (data: InsforgeUser['profile']) => Promise<{ error: string } | null>;\n setUser: (user: InsforgeUser | null) => void;\n}\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 (returns { error: string } | null)\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 * const result = await updateUser({ name });\n * if (result?.error) {\n * console.error(result.error);\n * } else {\n * console.log('User updated successfully');\n * }\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(): useUserReturn {\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 * - `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 { authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {authConfig?.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 void fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
package/dist/hooks.d.cts CHANGED
@@ -1,5 +1,4 @@
1
- import * as _insforge_shared_schemas from '@insforge/shared-schemas';
2
- import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
1
+ import { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
3
2
  import { InsforgeUser } from '@insforge/shared';
4
3
 
5
4
  /**
@@ -37,17 +36,17 @@ import { InsforgeUser } from '@insforge/shared';
37
36
  * ```
38
37
  */
39
38
  declare function useAuth(): {
40
- signIn: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateSessionResponse | {
39
+ signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
41
40
  error: string;
42
41
  statusCode?: number;
43
42
  errorCode?: string;
44
43
  }>;
45
- signUp: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateUserResponse | {
44
+ signUp: (email: string, password: string) => Promise<CreateUserResponse | {
46
45
  error: string;
47
46
  statusCode?: number;
48
47
  errorCode?: string;
49
48
  }>;
50
- signOut: () => Promise<void>;
49
+ signOut: () => Promise<any>;
51
50
  isLoaded: boolean;
52
51
  isSignedIn: boolean | undefined;
53
52
  };
package/dist/hooks.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import * as _insforge_shared_schemas from '@insforge/shared-schemas';
2
- import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
1
+ import { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
3
2
  import { InsforgeUser } from '@insforge/shared';
4
3
 
5
4
  /**
@@ -37,17 +36,17 @@ import { InsforgeUser } from '@insforge/shared';
37
36
  * ```
38
37
  */
39
38
  declare function useAuth(): {
40
- signIn: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateSessionResponse | {
39
+ signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
41
40
  error: string;
42
41
  statusCode?: number;
43
42
  errorCode?: string;
44
43
  }>;
45
- signUp: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateUserResponse | {
44
+ signUp: (email: string, password: string) => Promise<CreateUserResponse | {
46
45
  error: string;
47
46
  statusCode?: number;
48
47
  errorCode?: string;
49
48
  }>;
50
- signOut: () => Promise<void>;
49
+ signOut: () => Promise<any>;
51
50
  isLoaded: boolean;
52
51
  isSignedIn: boolean | undefined;
53
52
  };
package/dist/hooks.js CHANGED
@@ -22,7 +22,7 @@ function useInsforge() {
22
22
  sendVerificationEmail: () => Promise.resolve(null),
23
23
  sendResetPasswordEmail: () => Promise.resolve(null),
24
24
  resetPassword: () => Promise.resolve(null),
25
- verifyEmail: () => Promise.resolve(null),
25
+ verifyEmail: () => Promise.resolve({ error: "SSR mode" }),
26
26
  exchangeResetPasswordToken: () => Promise.resolve({ error: { message: "SSR mode" } }),
27
27
  loginWithOAuth: () => Promise.resolve(),
28
28
  getPublicAuthConfig: () => Promise.resolve(null),
package/dist/hooks.js.map CHANGED
@@ -1 +1 @@
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":["useContext","useState","useEffect"],"mappings":"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","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 return {\n useSearchParams: () => new URLSearchParams(),\n Link: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n };\n }\n\n return adapter;\n}\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\nimport { InsforgeManager, type InsforgeManagerState } from '../core/InsforgeManager';\nimport { InsforgeContext, type InsforgeContextValue } from '../contexts';\nimport { StyleProvider } from '../styles/StyleProvider';\nimport { InsForgeClient } from '@insforge/sdk';\n\nexport interface InitialAuthState {\n user?: InsforgeUser | null;\n userId?: string | null;\n}\n\nexport interface InsforgeProviderProps {\n /**\n * The base URL of the InsForge backend.\n * @deprecated This prop is no longer used.\n * Use the client prop instead.\n */\n baseUrl?: string;\n children: ReactNode;\n /**\n * The InsForge SDK client instance.\n */\n client: InsForgeClient;\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 onRefresh?: (authToken: string) => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal - Not intended for public use, used by Next.js package\n */\n initialState?: InitialAuthState;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Uses singleton InsforgeManager to manage state across packages.\n * Context only subscribes to Manager and triggers React re-renders.\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n * import { createClient } from '@insforge/sdk';\n *\n * const client = createClient({\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\n * });\n *\n * export default function MyApp() {\n * return (\n * <InsforgeProvider client={client}>\n * <App />\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * client={client}\n * afterSignInUrl=\"/dashboard\"\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 InsforgeProviderCore({\n children,\n client,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n initialState,\n}: InsforgeProviderProps) {\n // Get singleton Manager instance\n const manager: InsforgeManager = useMemo(\n () =>\n InsforgeManager.getInstance({\n client,\n afterSignInUrl,\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n }),\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\n );\n\n // Set initialState if provided (from Server Component)\n // This must happen during render, before useState initialization\n if (initialState) {\n const currentState = manager.getState();\n if (currentState.userId === undefined && initialState.userId !== undefined) {\n manager.setInitialState(initialState);\n }\n }\n\n // Subscribe to Manager state\n // Start with initial state from manager (will include initialState if set above)\n const [state, setState] = useState<InsforgeManagerState>(() => manager.getState());\n\n useEffect(() => {\n // Subscribe to state changes\n const unsubscribe = manager.subscribe((newState: InsforgeManagerState) => {\n setState(newState);\n });\n\n // Initialize auth state only on client side (after hydration)\n // This prevents hydration mismatches by ensuring SSR and client initial render match\n void manager.initialize().then(() => {\n const params = new URLSearchParams(window.location.search);\n if (params.has('access_token')) {\n const url = new URL(window.location.href);\n url.searchParams.delete('access_token');\n url.searchParams.delete('user_id');\n url.searchParams.delete('email');\n url.searchParams.delete('name');\n url.searchParams.delete('csrf_token');\n url.searchParams.delete('error');\n\n window.history.replaceState({}, document.title, url.toString());\n }\n });\n\n return () => {\n unsubscribe();\n };\n }, [manager]);\n\n // Create stable method references that delegate to manager\n const contextValue = useMemo<InsforgeContextValue>(\n () => ({\n // State from Manager\n user: state.user,\n userId: state.userId,\n isLoaded: state.isLoaded,\n isSignedIn: state.isSignedIn,\n\n // Methods delegated to Manager\n setUser: (user: InsforgeUser | null) => manager.setUser(user),\n signIn: (email: string, password: string) => manager.signIn(email, password),\n signUp: (email: string, password: string) => manager.signUp(email, password),\n signOut: () => manager.signOut(),\n updateUser: (data: InsforgeUser['profile']) => manager.updateUser(data),\n reloadAuth: () => manager.reloadAuth(),\n sendVerificationEmail: (email: string) => manager.sendVerificationEmail(email),\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\n resetPassword: (token: string, newPassword: string) =>\n manager.resetPassword(token, newPassword),\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\n exchangeResetPasswordToken: (email: string, code: string) =>\n manager.exchangeResetPasswordToken(email, code),\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\n manager.loginWithOAuth(provider, redirectTo),\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\n\n // Config\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\n }),\n [state, manager]\n );\n\n return <InsforgeContext.Provider value={contextValue}>{children}</InsforgeContext.Provider>;\n}\n\nexport function InsforgeProvider(props: InsforgeProviderProps) {\n return (\n <StyleProvider>\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeProviderCore {...props} />\n </NavigationProvider>\n </StyleProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * Works seamlessly across packages thanks to singleton Manager.\n * Context instance is guaranteed to be consistent.\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: InsforgeContextValue | undefined = useContext(InsforgeContext);\n\n if (!context) {\n return {\n user: undefined,\n userId: undefined,\n isLoaded: false,\n isSignedIn: undefined,\n setUser: () => {},\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\n signOut: () => Promise.resolve(),\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\n sendVerificationEmail: () => Promise.resolve(null),\n sendResetPasswordEmail: () => Promise.resolve(null),\n resetPassword: () => Promise.resolve(null),\n verifyEmail: () => Promise.resolve(null),\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\n loginWithOAuth: () => Promise.resolve(),\n getPublicAuthConfig: () => Promise.resolve(null),\n baseUrl: '',\n afterSignInUrl: '/',\n };\n }\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 { InsforgeUser } from '@insforge/shared';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\nexport interface useUserReturn {\n user: InsforgeUser | null | undefined;\n isLoaded: boolean;\n updateUser: (data: InsforgeUser['profile']) => Promise<{ error: string } | null>;\n setUser: (user: InsforgeUser | null) => void;\n}\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 (returns { error: string } | null)\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 * const result = await updateUser({ name });\n * if (result?.error) {\n * console.error(result.error);\n * } else {\n * console.log('User updated successfully');\n * }\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(): useUserReturn {\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 * - `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 { authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {authConfig?.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 void 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":["useContext","useState","useEffect"],"mappings":"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","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 return {\n useSearchParams: () => new URLSearchParams(),\n Link: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n };\n }\n\n return adapter;\n}\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\nimport type { InsforgeUser, OAuthProvider } from '../types';\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\nimport { InsforgeManager, type InsforgeManagerState } from '../core/InsforgeManager';\nimport { InsforgeContext, type InsforgeContextValue } from '../contexts';\nimport { StyleProvider } from '../styles/StyleProvider';\nimport { InsForgeClient } from '@insforge/sdk';\n\nexport interface InitialAuthState {\n user?: InsforgeUser | null;\n userId?: string | null;\n}\n\nexport interface InsforgeProviderProps {\n /**\n * The base URL of the InsForge backend.\n * @deprecated This prop is no longer used.\n * Use the client prop instead.\n */\n baseUrl?: string;\n children: ReactNode;\n /**\n * The InsForge SDK client instance.\n */\n client: InsForgeClient;\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, user: InsforgeUser) => Promise<void>;\n onSignOut?: () => Promise<void>;\n onRefresh?: (authToken: string, user: InsforgeUser) => Promise<void>;\n /**\n * Initial auth state from server (for SSR hydration)\n * @internal - Not intended for public use, used by Next.js package\n */\n initialState?: InitialAuthState;\n}\n\n/**\n * Unified Insforge Provider - manages authentication state and configuration\n *\n * Uses singleton InsforgeManager to manage state across packages.\n * Context only subscribes to Manager and triggers React re-renders.\n *\n * @example\n * ```tsx\n * // Basic usage (React/Vite)\n * import { InsforgeProvider } from '@insforge/react';\n * import { createClient } from '@insforge/sdk';\n *\n * const client = createClient({\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\n * });\n *\n * export default function MyApp() {\n * return (\n * <InsforgeProvider client={client}>\n * <App />\n * </InsforgeProvider>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // With cookie sync (Next.js optimization)\n * <InsforgeProvider\n * client={client}\n * afterSignInUrl=\"/dashboard\"\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 InsforgeProviderCore({\n children,\n client,\n afterSignInUrl = '/',\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n initialState,\n}: InsforgeProviderProps) {\n // Get singleton Manager instance\n const manager: InsforgeManager = useMemo(\n () =>\n InsforgeManager.getInstance({\n client,\n afterSignInUrl,\n onAuthChange,\n onSignIn,\n onSignOut,\n onRefresh,\n }),\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\n );\n\n // Set initialState if provided (from Server Component)\n // This must happen during render, before useState initialization\n if (initialState) {\n const currentState = manager.getState();\n if (currentState.userId === undefined && initialState.userId !== undefined) {\n manager.setInitialState(initialState);\n }\n }\n\n // Subscribe to Manager state\n // Start with initial state from manager (will include initialState if set above)\n const [state, setState] = useState<InsforgeManagerState>(() => manager.getState());\n\n useEffect(() => {\n // Subscribe to state changes\n const unsubscribe = manager.subscribe((newState: InsforgeManagerState) => {\n setState(newState);\n });\n\n // Initialize auth state\n void manager.initialize();\n\n return () => {\n unsubscribe();\n };\n }, [manager]);\n\n // Create stable method references that delegate to manager\n const contextValue = useMemo<InsforgeContextValue>(\n () => ({\n // State from Manager\n user: state.user,\n userId: state.userId,\n isLoaded: state.isLoaded,\n isSignedIn: state.isSignedIn,\n\n // Methods delegated to Manager\n setUser: (user: InsforgeUser | null) => manager.setUser(user),\n signIn: (email: string, password: string) => manager.signIn(email, password),\n signUp: (email: string, password: string) => manager.signUp(email, password),\n signOut: () => manager.signOut(),\n updateUser: (data: InsforgeUser['profile']) => manager.updateUser(data),\n reloadAuth: () => manager.reloadAuth(),\n sendVerificationEmail: (email: string) => manager.sendVerificationEmail(email),\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\n resetPassword: (token: string, newPassword: string) =>\n manager.resetPassword(token, newPassword),\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\n exchangeResetPasswordToken: (email: string, code: string) =>\n manager.exchangeResetPasswordToken(email, code),\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\n manager.loginWithOAuth(provider, redirectTo),\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\n\n // Config\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\n }),\n [state, manager]\n );\n\n return <InsforgeContext.Provider value={contextValue}>{children}</InsforgeContext.Provider>;\n}\n\nexport function InsforgeProvider(props: InsforgeProviderProps) {\n return (\n <StyleProvider>\n <NavigationProvider adapter={BrowserNavigationAdapter}>\n <InsforgeProviderCore {...props} />\n </NavigationProvider>\n </StyleProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n *\n * Works seamlessly across packages thanks to singleton Manager.\n * Context instance is guaranteed to be consistent.\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: InsforgeContextValue | undefined = useContext(InsforgeContext);\n\n if (!context) {\n return {\n user: undefined,\n userId: undefined,\n isLoaded: false,\n isSignedIn: undefined,\n setUser: () => {},\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\n signOut: () => Promise.resolve(),\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\n sendVerificationEmail: () => Promise.resolve(null),\n sendResetPasswordEmail: () => Promise.resolve(null),\n resetPassword: () => Promise.resolve(null),\n verifyEmail: () => Promise.resolve({ error: 'SSR mode' }),\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\n loginWithOAuth: () => Promise.resolve(),\n getPublicAuthConfig: () => Promise.resolve(null),\n baseUrl: '',\n afterSignInUrl: '/',\n };\n }\n\n return context;\n}\n","import { CreateSessionResponse, CreateUserResponse } from '@insforge/shared-schemas';\nimport { 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 signIn: (email: string, password: string) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signUp: (email: string, password: string) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\n signOut: () => Promise<any>;\n isLoaded: boolean;\n isSignedIn: boolean | undefined;\n} {\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useInsforge();\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\n}\n","import { InsforgeUser } from '@insforge/shared';\nimport { useInsforge } from '../provider/InsforgeProvider';\n\nexport interface useUserReturn {\n user: InsforgeUser | null | undefined;\n isLoaded: boolean;\n updateUser: (data: InsforgeUser['profile']) => Promise<{ error: string } | null>;\n setUser: (user: InsforgeUser | null) => void;\n}\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 (returns { error: string } | null)\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 * const result = await updateUser({ name });\n * if (result?.error) {\n * console.error(result.error);\n * } else {\n * console.log('User updated successfully');\n * }\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(): useUserReturn {\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 * - `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 { authConfig, isLoaded } = usePublicAuthConfig();\n *\n * if (!isLoaded) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * <p>OAuth providers: {authConfig?.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 void fetchConfig();\n }, [getPublicAuthConfig]);\n\n return { authConfig, isLoaded };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -583,7 +583,7 @@ var InsforgeManager = class _InsforgeManager {
583
583
  }
584
584
  if (this.config.onRefresh && session.accessToken) {
585
585
  try {
586
- await this.config.onRefresh(session.accessToken);
586
+ await this.config.onRefresh(session.accessToken, session.user);
587
587
  } catch (error) {
588
588
  if (error instanceof Error) {
589
589
  console.error("[InsforgeManager] Error syncing token on refresh:", error.message);
@@ -628,39 +628,22 @@ var InsforgeManager = class _InsforgeManager {
628
628
  }
629
629
  }
630
630
  // Helper to handle auth success
631
- async handleAuthSuccess(authToken, fallbackUser) {
632
- const userResult = await this.sdk.auth.getCurrentUser();
633
- if (userResult.data) {
634
- const userData = {
635
- id: userResult.data.user.id,
636
- email: userResult.data.user.email,
637
- profile: userResult.data.user.profile
638
- };
639
- this.user = userData;
631
+ async handleAuthSuccess(accessToken, user) {
632
+ if (user) {
633
+ this.user = user;
640
634
  this.notifyListeners();
641
635
  if (this.config.onAuthChange) {
642
- this.config.onAuthChange(userData);
636
+ this.config.onAuthChange(user);
643
637
  }
644
638
  if (this.config.onSignIn) {
645
639
  try {
646
- await this.config.onSignIn(authToken);
640
+ await this.config.onSignIn(accessToken, user);
647
641
  } catch (error) {
648
642
  if (error instanceof Error) {
649
643
  console.error("[InsforgeManager] Error syncing token to cookie:", error.message);
650
644
  }
651
645
  }
652
646
  }
653
- } else if (fallbackUser) {
654
- const userData = {
655
- id: fallbackUser.id || "",
656
- email: fallbackUser.email || "",
657
- profile: fallbackUser.profile || null
658
- };
659
- this.user = userData;
660
- this.notifyListeners();
661
- if (this.config.onAuthChange) {
662
- this.config.onAuthChange(userData);
663
- }
664
647
  }
665
648
  }
666
649
  // Business methods
@@ -675,7 +658,7 @@ var InsforgeManager = class _InsforgeManager {
675
658
  sdkResult.data.user ? {
676
659
  id: sdkResult.data.user.id,
677
660
  email: sdkResult.data.user.email,
678
- profile: sdkResult.data.user.profile || void 0
661
+ profile: sdkResult.data.user.profile ?? null
679
662
  } : void 0
680
663
  );
681
664
  return sdkResult.data;
@@ -690,13 +673,16 @@ var InsforgeManager = class _InsforgeManager {
690
673
  async signUp(email, password) {
691
674
  const sdkResult = await this.sdk.auth.signUp({ email, password });
692
675
  if (sdkResult.data) {
676
+ if (sdkResult.data.requireEmailVerification) {
677
+ return sdkResult.data;
678
+ }
693
679
  if (sdkResult.data.accessToken) {
694
680
  await this.handleAuthSuccess(
695
681
  sdkResult.data.accessToken,
696
682
  sdkResult.data.user ? {
697
683
  id: sdkResult.data.user.id,
698
684
  email: sdkResult.data.user.email,
699
- profile: sdkResult.data.user.profile || void 0
685
+ profile: sdkResult.data.user.profile ?? null
700
686
  } : void 0
701
687
  );
702
688
  }
@@ -775,21 +761,20 @@ var InsforgeManager = class _InsforgeManager {
775
761
  async verifyEmail(otp, email) {
776
762
  const sdkResult = await this.sdk.auth.verifyEmail({ otp, email: email || void 0 });
777
763
  if (sdkResult.data) {
764
+ await this.handleAuthSuccess(
765
+ sdkResult.data.accessToken,
766
+ sdkResult.data.user ? {
767
+ id: sdkResult.data.user.id,
768
+ email: sdkResult.data.user.email,
769
+ profile: sdkResult.data.user.profile ?? null
770
+ } : void 0
771
+ );
778
772
  return sdkResult.data;
779
773
  } else {
780
774
  return {
781
- user: {
782
- id: "",
783
- createdAt: "",
784
- email: "",
785
- emailVerified: false,
786
- updatedAt: "",
787
- metadata: null,
788
- profile: null
789
- },
790
- accessToken: "",
791
- redirectTo: void 0,
792
- csrfToken: void 0
775
+ error: sdkResult.error?.message || "Failed to verify email",
776
+ statusCode: sdkResult.error?.statusCode,
777
+ errorCode: sdkResult.error?.error
793
778
  };
794
779
  }
795
780
  }
@@ -2221,19 +2206,7 @@ function InsforgeProviderCore({
2221
2206
  const unsubscribe = manager.subscribe((newState) => {
2222
2207
  setState(newState);
2223
2208
  });
2224
- void manager.initialize().then(() => {
2225
- const params = new URLSearchParams(window.location.search);
2226
- if (params.has("access_token")) {
2227
- const url = new URL(window.location.href);
2228
- url.searchParams.delete("access_token");
2229
- url.searchParams.delete("user_id");
2230
- url.searchParams.delete("email");
2231
- url.searchParams.delete("name");
2232
- url.searchParams.delete("csrf_token");
2233
- url.searchParams.delete("error");
2234
- window.history.replaceState({}, document.title, url.toString());
2235
- }
2236
- });
2209
+ void manager.initialize();
2237
2210
  return () => {
2238
2211
  unsubscribe();
2239
2212
  };
@@ -2288,7 +2261,7 @@ function useInsforge() {
2288
2261
  sendVerificationEmail: () => Promise.resolve(null),
2289
2262
  sendResetPasswordEmail: () => Promise.resolve(null),
2290
2263
  resetPassword: () => Promise.resolve(null),
2291
- verifyEmail: () => Promise.resolve(null),
2264
+ verifyEmail: () => Promise.resolve({ error: "SSR mode" }),
2292
2265
  exchangeResetPasswordToken: () => Promise.resolve({ error: { message: "SSR mode" } }),
2293
2266
  loginWithOAuth: () => Promise.resolve(),
2294
2267
  getPublicAuthConfig: () => Promise.resolve(null),
@@ -4197,8 +4170,7 @@ function SignIn({ onError, ...uiProps }) {
4197
4170
  }
4198
4171
  throw new Error(result.error);
4199
4172
  }
4200
- const { user, accessToken, redirectTo } = result;
4201
- const csrfToken = result.csrfToken;
4173
+ const { user, accessToken, redirectTo, csrfToken } = result;
4202
4174
  if (user) {
4203
4175
  const finalUrl = new URL(redirectTo || redirectUrl || "", window.location.origin);
4204
4176
  finalUrl.searchParams.set("access_token", accessToken);
@@ -4224,7 +4196,7 @@ function SignIn({ onError, ...uiProps }) {
4224
4196
  setError("");
4225
4197
  try {
4226
4198
  const result = await verifyEmail(code, email);
4227
- if (!result) {
4199
+ if ("error" in result) {
4228
4200
  throw new Error("Verification failed");
4229
4201
  }
4230
4202
  const finalUrl = new URL(result.redirectTo || redirectUrl || "", window.location.origin);
@@ -4483,7 +4455,7 @@ function SignUp({ onError, ...uiProps }) {
4483
4455
  if ("error" in result) {
4484
4456
  throw new Error(result.error);
4485
4457
  }
4486
- if (result.requireEmailVerification && !result.accessToken) {
4458
+ if (result.requireEmailVerification) {
4487
4459
  setStep("awaiting-verification");
4488
4460
  setLoading(false);
4489
4461
  return;
@@ -4514,7 +4486,7 @@ function SignUp({ onError, ...uiProps }) {
4514
4486
  setError("");
4515
4487
  try {
4516
4488
  const result = await verifyEmail(code, email);
4517
- if (!result) {
4489
+ if ("error" in result) {
4518
4490
  throw new Error("Verification failed");
4519
4491
  }
4520
4492
  const finalUrl = new URL(result.redirectTo || redirectUrl || "", window.location.origin);
@@ -5098,8 +5070,8 @@ function VerifyEmail({ token: token2, onSuccess, onError, ...uiProps }) {
5098
5070
  }
5099
5071
  try {
5100
5072
  const result = await verifyEmail(token2);
5101
- if (!result?.accessToken) {
5102
- const errorMessage = result ? "Verification succeeded but no access token received" : "Email verification failed";
5073
+ if ("error" in result) {
5074
+ const errorMessage = result.error || "Email verification failed";
5103
5075
  setError(errorMessage);
5104
5076
  setStatus("error");
5105
5077
  if (onError) {