@insforge/react 0.3.3 → 0.3.5
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/atoms.js.map +1 -1
- package/dist/atoms.mjs.map +1 -1
- package/dist/components.d.mts +2 -1
- package/dist/components.d.ts +2 -1
- package/dist/components.js +9 -8
- package/dist/components.js.map +1 -1
- package/dist/components.mjs +9 -8
- package/dist/components.mjs.map +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/forms.mjs.map +1 -1
- package/dist/hooks.d.mts +4 -17
- package/dist/hooks.d.ts +4 -17
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.mjs.map +1 -1
- package/dist/index.d.mts +5 -21
- package/dist/index.d.ts +5 -21
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -8
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +2 -2
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/dist/hooks.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _insforge_shared_schemas from '@insforge/shared-schemas';
|
|
2
2
|
import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
3
|
+
import { InsforgeUser } from './types.mjs';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -37,26 +38,12 @@ import 'react';
|
|
|
37
38
|
* ```
|
|
38
39
|
*/
|
|
39
40
|
declare function useAuth(): {
|
|
40
|
-
signIn: (email: string, password: string) => Promise<{
|
|
41
|
-
user?: {
|
|
42
|
-
id: string;
|
|
43
|
-
email: string;
|
|
44
|
-
name: string;
|
|
45
|
-
};
|
|
46
|
-
accessToken: string | null;
|
|
47
|
-
} | {
|
|
41
|
+
signIn: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateSessionResponse | {
|
|
48
42
|
error: string;
|
|
49
43
|
statusCode?: number;
|
|
50
44
|
errorCode?: string;
|
|
51
45
|
}>;
|
|
52
|
-
signUp: (email: string, password: string) => Promise<{
|
|
53
|
-
user?: {
|
|
54
|
-
id: string;
|
|
55
|
-
email: string;
|
|
56
|
-
name: string;
|
|
57
|
-
};
|
|
58
|
-
accessToken: string | null;
|
|
59
|
-
} | {
|
|
46
|
+
signUp: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateUserResponse | {
|
|
60
47
|
error: string;
|
|
61
48
|
statusCode?: number;
|
|
62
49
|
errorCode?: string;
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _insforge_shared_schemas from '@insforge/shared-schemas';
|
|
2
2
|
import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
3
|
+
import { InsforgeUser } from './types.js';
|
|
3
4
|
import 'react';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -37,26 +38,12 @@ import 'react';
|
|
|
37
38
|
* ```
|
|
38
39
|
*/
|
|
39
40
|
declare function useAuth(): {
|
|
40
|
-
signIn: (email: string, password: string) => Promise<{
|
|
41
|
-
user?: {
|
|
42
|
-
id: string;
|
|
43
|
-
email: string;
|
|
44
|
-
name: string;
|
|
45
|
-
};
|
|
46
|
-
accessToken: string | null;
|
|
47
|
-
} | {
|
|
41
|
+
signIn: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateSessionResponse | {
|
|
48
42
|
error: string;
|
|
49
43
|
statusCode?: number;
|
|
50
44
|
errorCode?: string;
|
|
51
45
|
}>;
|
|
52
|
-
signUp: (email: string, password: string) => Promise<{
|
|
53
|
-
user?: {
|
|
54
|
-
id: string;
|
|
55
|
-
email: string;
|
|
56
|
-
name: string;
|
|
57
|
-
};
|
|
58
|
-
accessToken: string | null;
|
|
59
|
-
} | {
|
|
46
|
+
signUp: (email: string, password: string) => Promise<_insforge_shared_schemas.CreateUserResponse | {
|
|
60
47
|
error: string;
|
|
61
48
|
statusCode?: number;
|
|
62
49
|
errorCode?: string;
|
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;;AAiEA,IAAM,eAAA,GAAkBA,mBAAA;AAAA,EACtB;AACF,CAAA;AAsaO,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;;;AC3cO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,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.js","sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { createClient } from \"@insforge/sdk\";\nimport type { InsforgeUser } from \"../types\";\nimport { GetPublicAuthConfigResponse } 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<\n | {\n user?: { id: string; email: string; name: string };\n accessToken: string | null;\n }\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signUp: (\n email: string,\n password: string\n ) => Promise<\n | {\n user?: { id: string; email: string; name: string };\n accessToken: string | null;\n }\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendResetPasswordEmail: (\n email: string\n ) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (\n token: string,\n newPassword: string\n ) => Promise<{ message: string; redirectTo?: string } | null>;\n verifyEmail: (\n token: string\n ) => Promise<{ accessToken: string; user?: any } | null>;\n\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\n undefined\n);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\n syncTokenToCookie?: (token: string) => Promise<boolean>;\n clearCookie?: () => 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 baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\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 * syncTokenToCookie={async (token) => {\n * await fetch('/api/auth', {\n * method: 'POST',\n * body: JSON.stringify({ token })\n * });\n * return true;\n * }}\n * clearCookie={async () => {\n * await fetch('/api/auth', { method: 'DELETE' });\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n onAuthChange,\n syncTokenToCookie,\n clearCookie,\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\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, syncTokenToCookie, clearCookie]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n return () => {\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n };\n }, []); // Empty deps - run only on mount\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(\n \"[InsforgeProvider] Failed to get public auth config:\",\n error\n );\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (\n authToken: string,\n fallbackUser?: { id?: string; email?: string; name?: string }\n ) => {\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 (syncTokenToCookie) {\n try {\n await syncTokenToCookie(authToken);\n } catch (error) {\n // Cookie sync failed - that's okay\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, syncTokenToCookie]\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, clearCookie]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) throw new Error(\"No user signed in\");\n\n const profileUpdate: Record<string, any> = {\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 as string | undefined) || \"\",\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, 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 (token: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\n return sdkResult.data;\n },\n [insforge]\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 sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n getPublicAuthConfig,\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\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\n","import { useState, useEffect } from 'react';\nimport type {\n GetPublicAuthConfigResponse\n} 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\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","useState","useEffect"],"mappings":";;;;;;AA2DA,IAAM,eAAA,GAAkBA,mBAAA;AAAA,EACtB;AACF,CAAA;AAsaO,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;;;ACrcO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,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.js","sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { createClient } from \"@insforge/sdk\";\nimport type { InsforgeUser } from \"../types\";\nimport { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse, ResetPasswordResponse } 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<\n | CreateSessionResponse\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signUp: (\n email: string,\n password: string\n ) => Promise<\n | CreateUserResponse\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendResetPasswordEmail: (\n email: string\n ) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (\n token: string,\n newPassword: string\n ) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n token: string\n ) => Promise<{ accessToken: string; user?: any, redirectTo?: string } | null>;\n\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\n undefined\n);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\n syncTokenToCookie?: (token: string) => Promise<boolean>;\n clearCookie?: () => 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 baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\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 * syncTokenToCookie={async (token) => {\n * await fetch('/api/auth', {\n * method: 'POST',\n * body: JSON.stringify({ token })\n * });\n * return true;\n * }}\n * clearCookie={async () => {\n * await fetch('/api/auth', { method: 'DELETE' });\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n onAuthChange,\n syncTokenToCookie,\n clearCookie,\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\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, syncTokenToCookie, clearCookie]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n return () => {\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n };\n }, []); // Empty deps - run only on mount\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(\n \"[InsforgeProvider] Failed to get public auth config:\",\n error\n );\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (\n authToken: string,\n fallbackUser?: { id?: string; email?: string; name?: string }\n ) => {\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 (syncTokenToCookie) {\n try {\n await syncTokenToCookie(authToken);\n } catch (error) {\n // Cookie sync failed - that's okay\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, syncTokenToCookie]\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, clearCookie]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) throw new Error(\"No user signed in\");\n\n const profileUpdate: Record<string, any> = {\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 as string | undefined) || \"\",\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, 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 (token: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\n return sdkResult.data;\n },\n [insforge]\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 sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n getPublicAuthConfig,\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\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\n","import { useState, useEffect } from 'react';\nimport type {\n GetPublicAuthConfigResponse\n} 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\n"]}
|
package/dist/hooks.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["useState","useEffect"],"mappings":";;;;AAiEA,IAAM,eAAA,GAAkB,aAAA;AAAA,EACtB;AACF,CAAA;AAsaO,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;;;AC3cO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,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.mjs","sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { createClient } from \"@insforge/sdk\";\nimport type { InsforgeUser } from \"../types\";\nimport { GetPublicAuthConfigResponse } 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<\n | {\n user?: { id: string; email: string; name: string };\n accessToken: string | null;\n }\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signUp: (\n email: string,\n password: string\n ) => Promise<\n | {\n user?: { id: string; email: string; name: string };\n accessToken: string | null;\n }\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendResetPasswordEmail: (\n email: string\n ) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (\n token: string,\n newPassword: string\n ) => Promise<{ message: string; redirectTo?: string } | null>;\n verifyEmail: (\n token: string\n ) => Promise<{ accessToken: string; user?: any } | null>;\n\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\n undefined\n);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\n syncTokenToCookie?: (token: string) => Promise<boolean>;\n clearCookie?: () => 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 baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\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 * syncTokenToCookie={async (token) => {\n * await fetch('/api/auth', {\n * method: 'POST',\n * body: JSON.stringify({ token })\n * });\n * return true;\n * }}\n * clearCookie={async () => {\n * await fetch('/api/auth', { method: 'DELETE' });\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n onAuthChange,\n syncTokenToCookie,\n clearCookie,\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\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, syncTokenToCookie, clearCookie]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n return () => {\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n };\n }, []); // Empty deps - run only on mount\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(\n \"[InsforgeProvider] Failed to get public auth config:\",\n error\n );\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (\n authToken: string,\n fallbackUser?: { id?: string; email?: string; name?: string }\n ) => {\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 (syncTokenToCookie) {\n try {\n await syncTokenToCookie(authToken);\n } catch (error) {\n // Cookie sync failed - that's okay\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, syncTokenToCookie]\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, clearCookie]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) throw new Error(\"No user signed in\");\n\n const profileUpdate: Record<string, any> = {\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 as string | undefined) || \"\",\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, 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 (token: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\n return sdkResult.data;\n },\n [insforge]\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 sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n getPublicAuthConfig,\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\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\n","import { useState, useEffect } from 'react';\nimport type {\n GetPublicAuthConfigResponse\n} 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\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/provider/InsforgeProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["useState","useEffect"],"mappings":";;;;AA2DA,IAAM,eAAA,GAAkB,aAAA;AAAA,EACtB;AACF,CAAA;AAsaO,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;;;ACrcO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,QAAA,EAAU,UAAA,KAAe,WAAA,EAAY;AACtE,EAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,UAAU,UAAA,EAAW;AACzD;;;ACNO,SAAS,OAAA,GAAU;AACxB,EAAA,MAAM,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,KAAY,WAAA,EAAY;AAC5D,EAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,UAAA,EAAY,OAAA,EAAQ;AAC/C;ACGO,SAAS,mBAAA,GAGd;AACA,EAAA,MAAM,EAAE,mBAAA,EAAoB,GAAI,WAAA,EAAY;AAC5C,EAAA,MAAM,CAAC,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.mjs","sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n type ReactNode,\n} from \"react\";\nimport { createClient } from \"@insforge/sdk\";\nimport type { InsforgeUser } from \"../types\";\nimport { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse, ResetPasswordResponse } 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<\n | CreateSessionResponse\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signUp: (\n email: string,\n password: string\n ) => Promise<\n | CreateUserResponse\n | { error: string; statusCode?: number; errorCode?: string }\n >;\n signOut: () => Promise<void>;\n updateUser: (data: Partial<InsforgeUser>) => Promise<void>;\n reloadAuth: () => Promise<{ success: boolean; error?: string }>;\n\n // Email verification methods\n sendResetPasswordEmail: (\n email: string\n ) => Promise<{ success: boolean; message: string } | null>;\n resetPassword: (\n token: string,\n newPassword: string\n ) => Promise<ResetPasswordResponse | null>;\n verifyEmail: (\n token: string\n ) => Promise<{ accessToken: string; user?: any, redirectTo?: string } | null>;\n\n // Public auth config\n getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;\n // Base config\n baseUrl: string;\n}\n\nconst InsforgeContext = createContext<InsforgeContextValue | undefined>(\n undefined\n);\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n // Optional: custom token sync functions (e.g., for Next.js cookie sync)\n syncTokenToCookie?: (token: string) => Promise<boolean>;\n clearCookie?: () => 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 baseUrl={process.env.VITE_INSFORGE_BASE_URL}>\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 * syncTokenToCookie={async (token) => {\n * await fetch('/api/auth', {\n * method: 'POST',\n * body: JSON.stringify({ token })\n * });\n * return true;\n * }}\n * clearCookie={async () => {\n * await fetch('/api/auth', { method: 'DELETE' });\n * }}\n * >\n * {children}\n * </InsforgeProvider>\n * ```\n */\nexport function InsforgeProvider({\n children,\n baseUrl,\n onAuthChange,\n syncTokenToCookie,\n clearCookie,\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\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, syncTokenToCookie, clearCookie]);\n\n useEffect(() => {\n // Run loadAuthState only once on mount\n loadAuthState();\n\n return () => {\n if (refreshIntervalRef.current) {\n clearInterval(refreshIntervalRef.current);\n }\n };\n }, []); // Empty deps - run only on mount\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(\n \"[InsforgeProvider] Failed to get public auth config:\",\n error\n );\n return null;\n }\n }, [insforge]);\n\n /**\n * Helper function to handle successful authentication\n */\n const handleAuthSuccess = useCallback(\n async (\n authToken: string,\n fallbackUser?: { id?: string; email?: string; name?: string }\n ) => {\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 (syncTokenToCookie) {\n try {\n await syncTokenToCookie(authToken);\n } catch (error) {\n // Cookie sync failed - that's okay\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, syncTokenToCookie]\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 (clearCookie) {\n try {\n await clearCookie();\n } catch (error) {\n // Ignore errors\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, clearCookie]);\n\n const updateUser = useCallback(\n async (data: Partial<InsforgeUser>) => {\n if (!user) throw new Error(\"No user signed in\");\n\n const profileUpdate: Record<string, any> = {\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 as string | undefined) || \"\",\n avatarUrl: (profile?.avatarUrl as string | undefined) || \"\",\n };\n setUser(updatedUser);\n if (onAuthChange) {\n onAuthChange(updatedUser);\n }\n }\n }\n },\n [user, onAuthChange, 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 (token: string) => {\n const sdkResult = await insforge.auth.verifyEmail({ otp: token });\n return sdkResult.data;\n },\n [insforge]\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 sendResetPasswordEmail,\n resetPassword,\n verifyEmail,\n getPublicAuthConfig,\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\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\n","import { useState, useEffect } from 'react';\nimport type {\n GetPublicAuthConfigResponse\n} 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\n"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
import { InsforgeUser, OAuthProvider, OAuthProviderConfig } from './types.mjs';
|
|
7
7
|
export { AuthConfig, AuthContainerProps, AuthDividerProps, AuthErrorBannerProps, AuthFormFieldProps, AuthHeaderProps, AuthLinkProps, AuthOAuthButtonProps, AuthOAuthProvidersProps, AuthPasswordFieldProps, AuthPasswordStrengthIndicatorProps, AuthSubmitButtonProps, AuthVerificationCodeInputProps, BaseAppearance, ConditionalProps, ForgotPasswordAppearance, ForgotPasswordFormProps, ProtectProps, ResetPasswordAppearance, ResetPasswordFormProps, SignInAppearance, SignInFormProps, SignInProps, SignUpAppearance, SignUpFormProps, SignUpProps, UserButtonProps, VerifyEmailStatusProps } from './types.mjs';
|
|
8
|
-
import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
8
|
+
import { CreateSessionResponse, CreateUserResponse, ResetPasswordResponse, GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
9
9
|
export { useAuth, usePublicAuthConfig, useUser } from './hooks.mjs';
|
|
10
10
|
export { checkPasswordStrength, cn, createPasswordSchema, emailSchema, passwordSchema, validateEmail, validatePassword } from './lib.mjs';
|
|
11
11
|
export { getInsforgeRoutes } from './router.mjs';
|
|
@@ -18,26 +18,12 @@ interface InsforgeContextValue {
|
|
|
18
18
|
isLoaded: boolean;
|
|
19
19
|
isSignedIn: boolean;
|
|
20
20
|
setUser: (user: InsforgeUser | null) => void;
|
|
21
|
-
signIn: (email: string, password: string) => Promise<{
|
|
22
|
-
user?: {
|
|
23
|
-
id: string;
|
|
24
|
-
email: string;
|
|
25
|
-
name: string;
|
|
26
|
-
};
|
|
27
|
-
accessToken: string | null;
|
|
28
|
-
} | {
|
|
21
|
+
signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
|
|
29
22
|
error: string;
|
|
30
23
|
statusCode?: number;
|
|
31
24
|
errorCode?: string;
|
|
32
25
|
}>;
|
|
33
|
-
signUp: (email: string, password: string) => Promise<{
|
|
34
|
-
user?: {
|
|
35
|
-
id: string;
|
|
36
|
-
email: string;
|
|
37
|
-
name: string;
|
|
38
|
-
};
|
|
39
|
-
accessToken: string | null;
|
|
40
|
-
} | {
|
|
26
|
+
signUp: (email: string, password: string) => Promise<CreateUserResponse | {
|
|
41
27
|
error: string;
|
|
42
28
|
statusCode?: number;
|
|
43
29
|
errorCode?: string;
|
|
@@ -52,13 +38,11 @@ interface InsforgeContextValue {
|
|
|
52
38
|
success: boolean;
|
|
53
39
|
message: string;
|
|
54
40
|
} | null>;
|
|
55
|
-
resetPassword: (token: string, newPassword: string) => Promise<
|
|
56
|
-
message: string;
|
|
57
|
-
redirectTo?: string;
|
|
58
|
-
} | null>;
|
|
41
|
+
resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;
|
|
59
42
|
verifyEmail: (token: string) => Promise<{
|
|
60
43
|
accessToken: string;
|
|
61
44
|
user?: any;
|
|
45
|
+
redirectTo?: string;
|
|
62
46
|
} | null>;
|
|
63
47
|
getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;
|
|
64
48
|
baseUrl: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
import { InsforgeUser, OAuthProvider, OAuthProviderConfig } from './types.js';
|
|
7
7
|
export { AuthConfig, AuthContainerProps, AuthDividerProps, AuthErrorBannerProps, AuthFormFieldProps, AuthHeaderProps, AuthLinkProps, AuthOAuthButtonProps, AuthOAuthProvidersProps, AuthPasswordFieldProps, AuthPasswordStrengthIndicatorProps, AuthSubmitButtonProps, AuthVerificationCodeInputProps, BaseAppearance, ConditionalProps, ForgotPasswordAppearance, ForgotPasswordFormProps, ProtectProps, ResetPasswordAppearance, ResetPasswordFormProps, SignInAppearance, SignInFormProps, SignInProps, SignUpAppearance, SignUpFormProps, SignUpProps, UserButtonProps, VerifyEmailStatusProps } from './types.js';
|
|
8
|
-
import { GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
8
|
+
import { CreateSessionResponse, CreateUserResponse, ResetPasswordResponse, GetPublicAuthConfigResponse } from '@insforge/shared-schemas';
|
|
9
9
|
export { useAuth, usePublicAuthConfig, useUser } from './hooks.js';
|
|
10
10
|
export { checkPasswordStrength, cn, createPasswordSchema, emailSchema, passwordSchema, validateEmail, validatePassword } from './lib.js';
|
|
11
11
|
export { getInsforgeRoutes } from './router.js';
|
|
@@ -18,26 +18,12 @@ interface InsforgeContextValue {
|
|
|
18
18
|
isLoaded: boolean;
|
|
19
19
|
isSignedIn: boolean;
|
|
20
20
|
setUser: (user: InsforgeUser | null) => void;
|
|
21
|
-
signIn: (email: string, password: string) => Promise<{
|
|
22
|
-
user?: {
|
|
23
|
-
id: string;
|
|
24
|
-
email: string;
|
|
25
|
-
name: string;
|
|
26
|
-
};
|
|
27
|
-
accessToken: string | null;
|
|
28
|
-
} | {
|
|
21
|
+
signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
|
|
29
22
|
error: string;
|
|
30
23
|
statusCode?: number;
|
|
31
24
|
errorCode?: string;
|
|
32
25
|
}>;
|
|
33
|
-
signUp: (email: string, password: string) => Promise<{
|
|
34
|
-
user?: {
|
|
35
|
-
id: string;
|
|
36
|
-
email: string;
|
|
37
|
-
name: string;
|
|
38
|
-
};
|
|
39
|
-
accessToken: string | null;
|
|
40
|
-
} | {
|
|
26
|
+
signUp: (email: string, password: string) => Promise<CreateUserResponse | {
|
|
41
27
|
error: string;
|
|
42
28
|
statusCode?: number;
|
|
43
29
|
errorCode?: string;
|
|
@@ -52,13 +38,11 @@ interface InsforgeContextValue {
|
|
|
52
38
|
success: boolean;
|
|
53
39
|
message: string;
|
|
54
40
|
} | null>;
|
|
55
|
-
resetPassword: (token: string, newPassword: string) => Promise<
|
|
56
|
-
message: string;
|
|
57
|
-
redirectTo?: string;
|
|
58
|
-
} | null>;
|
|
41
|
+
resetPassword: (token: string, newPassword: string) => Promise<ResetPasswordResponse | null>;
|
|
59
42
|
verifyEmail: (token: string) => Promise<{
|
|
60
43
|
accessToken: string;
|
|
61
44
|
user?: any;
|
|
45
|
+
redirectTo?: string;
|
|
62
46
|
} | null>;
|
|
63
47
|
getPublicAuthConfig: () => Promise<GetPublicAuthConfigResponse | null>;
|
|
64
48
|
baseUrl: string;
|
package/dist/index.js
CHANGED
|
@@ -1366,9 +1366,9 @@ function SignIn({
|
|
|
1366
1366
|
}
|
|
1367
1367
|
throw new Error(result.error);
|
|
1368
1368
|
}
|
|
1369
|
-
const { user, accessToken } = result;
|
|
1369
|
+
const { user, accessToken, redirectTo } = result;
|
|
1370
1370
|
if (onSuccess) {
|
|
1371
|
-
if (user) onSuccess(user, accessToken || "");
|
|
1371
|
+
if (user) onSuccess(user, accessToken || "", redirectTo);
|
|
1372
1372
|
}
|
|
1373
1373
|
} catch (err) {
|
|
1374
1374
|
const errorMessage = err.message || "Sign in failed";
|
|
@@ -1715,14 +1715,14 @@ function SignUp({
|
|
|
1715
1715
|
if ("error" in result) {
|
|
1716
1716
|
throw new Error(result.error);
|
|
1717
1717
|
}
|
|
1718
|
-
if (result.
|
|
1718
|
+
if (result.requireEmailVerification && !result.accessToken) {
|
|
1719
1719
|
setStep("awaiting-verification");
|
|
1720
1720
|
setLoading(false);
|
|
1721
1721
|
return;
|
|
1722
1722
|
}
|
|
1723
1723
|
if (result.accessToken && result.user) {
|
|
1724
1724
|
if (onSuccess) {
|
|
1725
|
-
onSuccess(result.user, result.accessToken);
|
|
1725
|
+
onSuccess(result.user, result.accessToken, result.redirectTo);
|
|
1726
1726
|
}
|
|
1727
1727
|
}
|
|
1728
1728
|
} catch (err) {
|
|
@@ -2094,7 +2094,7 @@ function ResetPassword({
|
|
|
2094
2094
|
if (result?.message) {
|
|
2095
2095
|
setSuccess(true);
|
|
2096
2096
|
if (onSuccess) {
|
|
2097
|
-
onSuccess(
|
|
2097
|
+
onSuccess();
|
|
2098
2098
|
}
|
|
2099
2099
|
} else {
|
|
2100
2100
|
const errorMessage = "Failed to reset password";
|
|
@@ -2213,12 +2213,12 @@ function ForgotPassword({
|
|
|
2213
2213
|
setError("");
|
|
2214
2214
|
setVerificationCode(code);
|
|
2215
2215
|
try {
|
|
2216
|
-
const result = await insforge.auth.
|
|
2216
|
+
const result = await insforge.auth.exchangeResetPasswordToken({ email, code });
|
|
2217
2217
|
if (result.error) {
|
|
2218
2218
|
throw new Error(result.error.message || "Failed to verify code");
|
|
2219
2219
|
}
|
|
2220
2220
|
if (result.data) {
|
|
2221
|
-
setResetToken(result.data.
|
|
2221
|
+
setResetToken(result.data.token);
|
|
2222
2222
|
setStep("password");
|
|
2223
2223
|
}
|
|
2224
2224
|
} catch (err) {
|
|
@@ -2405,7 +2405,8 @@ function VerifyEmail({
|
|
|
2405
2405
|
if (onSuccess) {
|
|
2406
2406
|
onSuccess({
|
|
2407
2407
|
accessToken: result.accessToken,
|
|
2408
|
-
user: result.user
|
|
2408
|
+
user: result.user,
|
|
2409
|
+
redirectTo: result.redirectTo
|
|
2409
2410
|
});
|
|
2410
2411
|
}
|
|
2411
2412
|
} catch (err) {
|