@aaspai/react 0.0.0 → 0.0.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.
Files changed (55) hide show
  1. package/README.md +720 -8
  2. package/dist/atoms.cjs +3565 -0
  3. package/dist/atoms.cjs.map +1 -0
  4. package/dist/atoms.d.cts +255 -0
  5. package/dist/atoms.d.ts +255 -0
  6. package/dist/atoms.js +3530 -0
  7. package/dist/atoms.js.map +1 -0
  8. package/dist/components.cjs +5397 -0
  9. package/dist/components.cjs.map +1 -0
  10. package/dist/components.d.cts +362 -0
  11. package/dist/components.d.ts +362 -0
  12. package/dist/components.js +5344 -0
  13. package/dist/components.js.map +1 -0
  14. package/dist/forms.cjs +3928 -0
  15. package/dist/forms.cjs.map +1 -0
  16. package/dist/forms.d.cts +135 -0
  17. package/dist/forms.d.ts +135 -0
  18. package/dist/forms.js +3903 -0
  19. package/dist/forms.js.map +1 -0
  20. package/dist/hooks.cjs +74 -0
  21. package/dist/hooks.cjs.map +1 -0
  22. package/dist/hooks.d.cts +138 -0
  23. package/dist/hooks.d.ts +138 -0
  24. package/dist/hooks.js +70 -0
  25. package/dist/hooks.js.map +1 -0
  26. package/dist/index.cjs +6030 -0
  27. package/dist/index.cjs.map +1 -0
  28. package/dist/index.d.cts +248 -0
  29. package/dist/index.d.ts +248 -0
  30. package/dist/index.js +5952 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/lib.cjs +139 -0
  33. package/dist/lib.cjs.map +1 -0
  34. package/dist/lib.d.cts +111 -0
  35. package/dist/lib.d.ts +111 -0
  36. package/dist/lib.js +128 -0
  37. package/dist/lib.js.map +1 -0
  38. package/dist/navigation.cjs +56 -0
  39. package/dist/navigation.cjs.map +1 -0
  40. package/dist/navigation.d.cts +65 -0
  41. package/dist/navigation.d.ts +65 -0
  42. package/dist/navigation.js +51 -0
  43. package/dist/navigation.js.map +1 -0
  44. package/dist/styles.css +839 -0
  45. package/dist/types.cjs +4 -0
  46. package/dist/types.cjs.map +1 -0
  47. package/dist/types.d.cts +17 -0
  48. package/dist/types.d.ts +17 -0
  49. package/dist/types.js +3 -0
  50. package/dist/types.js.map +1 -0
  51. package/package.json +67 -28
  52. package/index.cjs +0 -10
  53. package/index.d.ts +0 -6
  54. package/index.js +0 -5
  55. package/styles.css +0 -1
package/dist/hooks.cjs ADDED
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ require('react/jsx-runtime');
5
+ var react$1 = require('@aaspai/shared/react');
6
+
7
+ // src/provider/AaspaiProvider.tsx
8
+ react.createContext(null);
9
+ function useAaspai() {
10
+ const context = react.useContext(react$1.AaspaiContext);
11
+ if (!context) {
12
+ return {
13
+ user: void 0,
14
+ userId: void 0,
15
+ isLoaded: false,
16
+ isSignedIn: void 0,
17
+ setUser: () => {
18
+ },
19
+ signIn: () => Promise.resolve({ error: "SSR mode" }),
20
+ signUp: () => Promise.resolve({ error: "SSR mode" }),
21
+ signOut: () => Promise.resolve(),
22
+ updateUser: () => Promise.resolve({ error: "SSR mode" }),
23
+ reloadAuth: () => Promise.resolve({ success: false, error: "SSR mode" }),
24
+ resendVerificationEmail: () => Promise.resolve(null),
25
+ sendResetPasswordEmail: () => Promise.resolve(null),
26
+ resetPassword: () => Promise.resolve(null),
27
+ verifyEmail: () => Promise.resolve({ error: "SSR mode" }),
28
+ exchangeResetPasswordToken: () => Promise.resolve({ error: { message: "SSR mode" } }),
29
+ loginWithOAuth: () => Promise.resolve(),
30
+ getPublicAuthConfig: () => Promise.resolve(null),
31
+ getSession: () => Promise.resolve(null),
32
+ baseUrl: "",
33
+ afterSignInUrl: "/"
34
+ };
35
+ }
36
+ return context;
37
+ }
38
+
39
+ // src/hooks/useAuth.ts
40
+ function useAuth() {
41
+ const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAaspai();
42
+ return { signIn, signUp, signOut, isLoaded, isSignedIn };
43
+ }
44
+
45
+ // src/hooks/useUser.ts
46
+ function useUser() {
47
+ const { user, isLoaded, updateUser, setUser } = useAaspai();
48
+ return { user, isLoaded, updateUser, setUser };
49
+ }
50
+ function usePublicAuthConfig() {
51
+ const { getPublicAuthConfig } = useAaspai();
52
+ const [authConfig, setAuthConfig] = react.useState(null);
53
+ const [isLoaded, setIsLoaded] = react.useState(false);
54
+ react.useEffect(() => {
55
+ async function fetchConfig() {
56
+ const result = await getPublicAuthConfig();
57
+ if (result) {
58
+ setAuthConfig(result);
59
+ } else {
60
+ console.error("[usePublicAuthConfig] Failed to get public auth config");
61
+ setAuthConfig(null);
62
+ }
63
+ setIsLoaded(true);
64
+ }
65
+ void fetchConfig();
66
+ }, [getPublicAuthConfig]);
67
+ return { authConfig, isLoaded };
68
+ }
69
+
70
+ exports.useAuth = useAuth;
71
+ exports.usePublicAuthConfig = usePublicAuthConfig;
72
+ exports.useUser = useUser;
73
+ //# sourceMappingURL=hooks.cjs.map
74
+ //# sourceMappingURL=hooks.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/navigation/NavigationContext.tsx","../src/provider/AaspaiProvider.tsx","../src/hooks/useAuth.ts","../src/hooks/useUser.ts","../src/hooks/usePublicAuthConfig.ts"],"names":["createContext","useContext","AaspaiContext","useState","useEffect"],"mappings":"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","file":"hooks.cjs","sourcesContent":["import { createContext, useContext, ReactNode } from 'react';\r\nimport type { NavigationAdapter } from './types';\r\n\r\nconst NavigationContext = createContext<NavigationAdapter | null>(null);\r\n\r\nexport interface NavigationProviderProps {\r\n adapter: NavigationAdapter;\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Navigation Provider\r\n * Injects navigation adapter into the component tree\r\n */\r\nexport function NavigationProvider({ adapter, children }: NavigationProviderProps) {\r\n return <NavigationContext.Provider value={adapter}>{children}</NavigationContext.Provider>;\r\n}\r\n\r\n/**\r\n * Hook to access navigation adapter\r\n * @throws Error if used outside NavigationProvider\r\n */\r\nexport function useNavigationAdapter(): NavigationAdapter {\r\n const adapter = useContext(NavigationContext);\r\n\r\n if (!adapter) {\r\n return {\r\n useSearchParams: () => new URLSearchParams(),\r\n Link: ({ href, children, className }) => (\r\n <a href={href} className={className}>\r\n {children}\r\n </a>\r\n ),\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\r\nimport type { AaspaiUser, OAuthProvider } from '../types';\r\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\r\nimport { AaspaiManager, type AaspaiManagerState } from '../core/AaspaiManager';\r\nimport {\r\n AaspaiContext,\r\n AaspaiAuthStateContext,\r\n AaspaiMethodsContext,\r\n AaspaiConfigContext,\r\n type AaspaiContextValue,\r\n type AaspaiAuthState,\r\n type AaspaiAuthMethods,\r\n type AaspaiAuthConfig,\r\n} from '../contexts';\r\nimport { StyleProvider } from '../styles/StyleProvider';\r\nimport { AASPAIClient } from '@aaspai/sdk';\r\n\r\nexport interface InitialAuthState {\r\n user?: AaspaiUser | null;\r\n userId?: string | null;\r\n}\r\n\r\nexport interface AaspaiProviderProps {\r\n /**\r\n * The base URL of the AASPAI backend.\r\n * @deprecated This prop is no longer used.\r\n * Use the client prop instead.\r\n */\r\n baseUrl?: string;\r\n children: ReactNode;\r\n /**\r\n * The AASPAI SDK client instance.\r\n */\r\n client: AASPAIClient;\r\n /**\r\n * URL to redirect to after successful sign in (when token is detected in URL)\r\n * @default '/'\r\n */\r\n afterSignInUrl?: string;\r\n onAuthChange?: (user: AaspaiUser | null) => void;\r\n onSignIn?: (authToken: string, user: AaspaiUser) => Promise<void>;\r\n onSignOut?: () => Promise<void>;\r\n onRefresh?: (authToken: string, user: AaspaiUser) => Promise<void>;\r\n /**\r\n * Initial auth state from server (for SSR hydration)\r\n * @internal - Not intended for public use, used by Next.js package\r\n */\r\n initialState?: InitialAuthState;\r\n}\r\n\r\n/**\r\n * Unified Aaspai Provider - manages authentication state and configuration\r\n *\r\n * Uses singleton AaspaiManager to manage state across packages.\r\n * Context only subscribes to Manager and triggers React re-renders.\r\n *\r\n * Architecture (pattern learned from Clerk):\r\n * - AaspaiMethodsContext: Stable method references (NEVER change)\r\n * - AaspaiAuthStateContext: Reactive auth state (changes on sign in/out)\r\n * - AaspaiConfigContext: Static configuration values\r\n * - AaspaiContext: Combined context for backward compatibility\r\n *\r\n * This architecture prevents useEffect re-runs when auth state changes,\r\n * solving the \"duplicate request\" bug in components like VerifyEmail.\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { AaspaiProvider } from '@aaspai/react';\r\n * import { createClient } from '@aaspai/sdk';\r\n *\r\n * const client = createClient({\r\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\r\n * });\r\n *\r\n * export default function MyApp() {\r\n * return (\r\n * <AaspaiProvider client={client}>\r\n * <App />\r\n * </AaspaiProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <AaspaiProvider\r\n * client={client}\r\n * afterSignInUrl=\"/dashboard\"\r\n * onSignIn={async (authToken) => {\r\n * await signIn(authToken);\r\n * }}\r\n * onSignOut={async () => {\r\n * await signOut();\r\n * }}\r\n * >\r\n * {children}\r\n * </AaspaiProvider>\r\n * ```\r\n */\r\nexport function AaspaiProviderCore({\r\n children,\r\n client,\r\n afterSignInUrl = '/',\r\n onAuthChange,\r\n onSignIn,\r\n onSignOut,\r\n onRefresh,\r\n initialState,\r\n}: AaspaiProviderProps) {\r\n // Get singleton Manager instance\r\n const manager: AaspaiManager = useMemo(\r\n () =>\r\n AaspaiManager.getInstance({\r\n client,\r\n afterSignInUrl,\r\n onAuthChange,\r\n onSignIn,\r\n onSignOut,\r\n onRefresh,\r\n }),\r\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\r\n );\r\n\r\n // Set initialState if provided (from Server Component)\r\n // This must happen during render, before useState initialization\r\n if (initialState) {\r\n const currentState = manager.getState();\r\n if (currentState.userId === undefined && initialState.userId !== undefined) {\r\n manager.setInitialState(initialState);\r\n }\r\n }\r\n\r\n // Subscribe to Manager state\r\n // Start with initial state from manager (will include initialState if set above)\r\n const [state, setState] = useState<AaspaiManagerState>(() => manager.getState());\r\n\r\n useEffect(() => {\r\n // Subscribe to state changes\r\n const unsubscribe = manager.subscribe((newState: AaspaiManagerState) => {\r\n setState(newState);\r\n });\r\n\r\n // Initialize auth state\r\n void manager.initialize();\r\n\r\n return () => {\r\n unsubscribe();\r\n };\r\n }, [manager]);\r\n\r\n // ============================================\r\n // STABLE METHOD REFERENCES\r\n // These NEVER change once created (only depend on manager)\r\n // This prevents useEffect re-runs when state changes\r\n // ============================================\r\n const stableMethods = useMemo<AaspaiAuthMethods>(\r\n () => ({\r\n setUser: (user: AaspaiUser | null) => manager.setUser(user),\r\n signIn: (email: string, password: string) => manager.signIn(email, password),\r\n signUp: (email: string, password: string, options?: { emailRedirectTo?: string }) =>\r\n manager.signUp(email, password, options),\r\n signOut: () => manager.signOut(),\r\n updateUser: (data: AaspaiUser['profile']) => manager.updateUser(data),\r\n reloadAuth: () => manager.reloadAuth(),\r\n resendVerificationEmail: (email: string, options?: { emailRedirectTo?: string }) =>\r\n manager.resendVerificationEmail(email, options),\r\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\r\n resetPassword: (token: string, newPassword: string) =>\r\n manager.resetPassword(token, newPassword),\r\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\r\n exchangeResetPasswordToken: (email: string, code: string) =>\r\n manager.exchangeResetPasswordToken(email, code),\r\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\r\n manager.loginWithOAuth(provider, redirectTo),\r\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\r\n getSession: () => manager.getSession(),\r\n }),\r\n [manager] // Only depends on manager (stable)\r\n );\r\n\r\n // ============================================\r\n // STABLE CONFIG REFERENCES\r\n // These rarely change after initialization\r\n // ============================================\r\n const stableConfig = useMemo<AaspaiAuthConfig>(\r\n () => ({\r\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\r\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\r\n }),\r\n [manager] // Only depends on manager (stable)\r\n );\r\n\r\n // ============================================\r\n // REACTIVE AUTH STATE\r\n // This updates when auth state changes (sign in/out)\r\n // ============================================\r\n const authState = useMemo<AaspaiAuthState>(\r\n () => ({\r\n user: state.user,\r\n userId: state.userId,\r\n isLoaded: state.isLoaded,\r\n isSignedIn: state.isSignedIn,\r\n }),\r\n [state.user, state.userId, state.isLoaded, state.isSignedIn]\r\n );\r\n\r\n // ============================================\r\n // COMBINED CONTEXT VALUE (for backward compatibility)\r\n // Uses stable method references from stableMethods\r\n // ============================================\r\n const contextValue = useMemo<AaspaiContextValue>(\r\n () => ({\r\n // Reactive state\r\n ...authState,\r\n // Stable methods (references don't change!)\r\n ...stableMethods,\r\n // Stable config\r\n ...stableConfig,\r\n }),\r\n [authState, stableMethods, stableConfig]\r\n );\r\n\r\n // Multi-context architecture: each context has its own responsibility\r\n // - MethodsContext: stable method references\r\n // - AuthStateContext: reactive auth state\r\n // - ConfigContext: static configuration\r\n // - AaspaiContext: combined for backward compatibility\r\n return (\r\n <AaspaiMethodsContext.Provider value={stableMethods}>\r\n <AaspaiConfigContext.Provider value={stableConfig}>\r\n <AaspaiAuthStateContext.Provider value={authState}>\r\n <AaspaiContext.Provider value={contextValue}>{children}</AaspaiContext.Provider>\r\n </AaspaiAuthStateContext.Provider>\r\n </AaspaiConfigContext.Provider>\r\n </AaspaiMethodsContext.Provider>\r\n );\r\n}\r\n\r\nexport function AaspaiProvider(props: AaspaiProviderProps) {\r\n return (\r\n <StyleProvider>\r\n <NavigationProvider adapter={BrowserNavigationAdapter}>\r\n <AaspaiProviderCore {...props} />\r\n </NavigationProvider>\r\n </StyleProvider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Aaspai context\r\n *\r\n * Works seamlessly across packages thanks to singleton Manager.\r\n * Context instance is guaranteed to be consistent.\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useAaspai();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAaspai(): AaspaiContextValue {\r\n const context: AaspaiContextValue | undefined = useContext(AaspaiContext);\r\n\r\n if (!context) {\r\n return {\r\n user: undefined,\r\n userId: undefined,\r\n isLoaded: false,\r\n isSignedIn: undefined,\r\n setUser: () => {},\r\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\r\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\r\n signOut: () => Promise.resolve(),\r\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\r\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\r\n resendVerificationEmail: () => Promise.resolve(null),\r\n sendResetPasswordEmail: () => Promise.resolve(null),\r\n resetPassword: () => Promise.resolve(null),\r\n verifyEmail: () => Promise.resolve({ error: 'SSR mode' }),\r\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\r\n loginWithOAuth: () => Promise.resolve(),\r\n getPublicAuthConfig: () => Promise.resolve(null),\r\n getSession: () => Promise.resolve(null),\r\n baseUrl: '',\r\n afterSignInUrl: '/',\r\n };\r\n }\r\n\r\n return context;\r\n}\r\n","import { CreateSessionResponse, CreateUserResponse } from '@aaspai/shared-schemas';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n *\r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n *\r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n *\r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n *\r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth(): {\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\r\n signOut: () => Promise<void>;\r\n isLoaded: boolean;\r\n isSignedIn: boolean | undefined;\r\n} {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAaspai();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n","import { AaspaiUser } from '@aaspai/shared';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\nexport interface useUserReturn {\r\n user: AaspaiUser | null | undefined;\r\n isLoaded: boolean;\r\n updateUser: (data: AaspaiUser['profile']) => Promise<{ error: string } | null>;\r\n setUser: (user: AaspaiUser | null) => void;\r\n}\r\n\r\n/**\r\n * Hook to access current user data\r\n *\r\n * @returns Object containing:\r\n * - `user`: Current user object (AaspaiUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data (returns { error: string } | null)\r\n * - `setUser`: Internal function to manually set user state\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n *\r\n * async function handleUpdate(name: string) {\r\n * const result = await updateUser({ name });\r\n * if (result?.error) {\r\n * console.error(result.error);\r\n * } else {\r\n * console.log('User updated successfully');\r\n * }\r\n * }\r\n *\r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser(): useUserReturn {\r\n const { user, isLoaded, updateUser, setUser } = useAaspai();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport type { GetPublicAuthConfigResponse } from '@aaspai/shared-schemas';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Aaspai backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `authConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { authConfig, isLoaded } = usePublicAuthConfig();\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n *\r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {authConfig?.oauthProviders.length}</p>\r\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within AaspaiProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n authConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useAaspai();\r\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setAuthConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setAuthConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n void fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { authConfig, isLoaded };\r\n}\r\n"]}
@@ -0,0 +1,138 @@
1
+ import { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse } from '@aaspai/shared-schemas';
2
+ import { AaspaiUser } from '@aaspai/shared';
3
+
4
+ /**
5
+ * Hook to access authentication methods
6
+ *
7
+ * @returns Object containing:
8
+ * - `signIn`: Function to sign in with email and password
9
+ * - `signUp`: Function to sign up with email and password
10
+ * - `signOut`: Function to sign out the current user
11
+ * - `isLoaded`: Boolean indicating if auth state has been loaded
12
+ * - `isSignedIn`: Boolean indicating if user is currently signed in
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * function LoginForm() {
17
+ * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();
18
+ *
19
+ * async function handleLogin(email: string, password: string) {
20
+ * try {
21
+ * await signIn(email, password);
22
+ * // User is now signed in
23
+ * } catch (error) {
24
+ * console.error('Sign in failed:', error);
25
+ * }
26
+ * }
27
+ *
28
+ * if (!isLoaded) return <div>Loading...</div>;
29
+ *
30
+ * return (
31
+ * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>
32
+ * {/* form fields *\/}
33
+ * </form>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+ declare function useAuth(): {
39
+ signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
40
+ error: string;
41
+ statusCode?: number;
42
+ errorCode?: string;
43
+ }>;
44
+ signUp: (email: string, password: string) => Promise<CreateUserResponse | {
45
+ error: string;
46
+ statusCode?: number;
47
+ errorCode?: string;
48
+ }>;
49
+ signOut: () => Promise<void>;
50
+ isLoaded: boolean;
51
+ isSignedIn: boolean | undefined;
52
+ };
53
+
54
+ interface useUserReturn {
55
+ user: AaspaiUser | null | undefined;
56
+ isLoaded: boolean;
57
+ updateUser: (data: AaspaiUser['profile']) => Promise<{
58
+ error: string;
59
+ } | null>;
60
+ setUser: (user: AaspaiUser | null) => void;
61
+ }
62
+ /**
63
+ * Hook to access current user data
64
+ *
65
+ * @returns Object containing:
66
+ * - `user`: Current user object (AaspaiUser | null)
67
+ * - `isLoaded`: Boolean indicating if auth state has been loaded
68
+ * - `updateUser`: Function to update user profile data (returns { error: string } | null)
69
+ * - `setUser`: Internal function to manually set user state
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * function UserProfile() {
74
+ * const { user, isLoaded, updateUser } = useUser();
75
+ *
76
+ * if (!isLoaded) return <div>Loading...</div>;
77
+ * if (!user) return <div>Not signed in</div>;
78
+ *
79
+ * async function handleUpdate(name: string) {
80
+ * const result = await updateUser({ name });
81
+ * if (result?.error) {
82
+ * console.error(result.error);
83
+ * } else {
84
+ * console.log('User updated successfully');
85
+ * }
86
+ * }
87
+ *
88
+ * return (
89
+ * <div>
90
+ * <p>Email: {user.email}</p>
91
+ * {user.name && <p>Name: {user.name}</p>}
92
+ * {user.avatarUrl && <img src={user.avatarUrl} alt="Avatar" />}
93
+ * </div>
94
+ * );
95
+ * }
96
+ * ```
97
+ */
98
+ declare function useUser(): useUserReturn;
99
+
100
+ /**
101
+ * Hook to get all public authentication configuration (OAuth + Email) from Aaspai backend
102
+ *
103
+ * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**
104
+ *
105
+ * This hook lazily fetches all public authentication configuration from the backend
106
+ * only when the component mounts. Using it in other components will cause unnecessary
107
+ * API calls on every page load.
108
+ *
109
+ * @returns Object containing OAuth providers, email auth config, and loading state
110
+ * - `authConfig`: Email authentication configuration object with password rules
111
+ * - `isLoaded`: Boolean indicating if the config has been fetched
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * // ✅ Correct usage - only in SignIn/SignUp components
116
+ * function SignUp() {
117
+ * const { authConfig, isLoaded } = usePublicAuthConfig();
118
+ *
119
+ * if (!isLoaded) return <div>Loading...</div>;
120
+ *
121
+ * return (
122
+ * <div>
123
+ * <p>OAuth providers: {authConfig?.oauthProviders.length}</p>
124
+ * <p>Password min length: {authConfig?.passwordMinLength}</p>
125
+ * </div>
126
+ * );
127
+ * }
128
+ * ```
129
+ *
130
+ * @requires Must be used within AaspaiProvider
131
+ */
132
+ declare function usePublicAuthConfig(): {
133
+ authConfig: GetPublicAuthConfigResponse | null;
134
+ isLoaded: boolean;
135
+ };
136
+
137
+ export { useAuth, usePublicAuthConfig, useUser };
138
+
@@ -0,0 +1,138 @@
1
+ import { CreateSessionResponse, CreateUserResponse, GetPublicAuthConfigResponse } from '@aaspai/shared-schemas';
2
+ import { AaspaiUser } from '@aaspai/shared';
3
+
4
+ /**
5
+ * Hook to access authentication methods
6
+ *
7
+ * @returns Object containing:
8
+ * - `signIn`: Function to sign in with email and password
9
+ * - `signUp`: Function to sign up with email and password
10
+ * - `signOut`: Function to sign out the current user
11
+ * - `isLoaded`: Boolean indicating if auth state has been loaded
12
+ * - `isSignedIn`: Boolean indicating if user is currently signed in
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * function LoginForm() {
17
+ * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();
18
+ *
19
+ * async function handleLogin(email: string, password: string) {
20
+ * try {
21
+ * await signIn(email, password);
22
+ * // User is now signed in
23
+ * } catch (error) {
24
+ * console.error('Sign in failed:', error);
25
+ * }
26
+ * }
27
+ *
28
+ * if (!isLoaded) return <div>Loading...</div>;
29
+ *
30
+ * return (
31
+ * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>
32
+ * {/* form fields *\/}
33
+ * </form>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+ declare function useAuth(): {
39
+ signIn: (email: string, password: string) => Promise<CreateSessionResponse | {
40
+ error: string;
41
+ statusCode?: number;
42
+ errorCode?: string;
43
+ }>;
44
+ signUp: (email: string, password: string) => Promise<CreateUserResponse | {
45
+ error: string;
46
+ statusCode?: number;
47
+ errorCode?: string;
48
+ }>;
49
+ signOut: () => Promise<void>;
50
+ isLoaded: boolean;
51
+ isSignedIn: boolean | undefined;
52
+ };
53
+
54
+ interface useUserReturn {
55
+ user: AaspaiUser | null | undefined;
56
+ isLoaded: boolean;
57
+ updateUser: (data: AaspaiUser['profile']) => Promise<{
58
+ error: string;
59
+ } | null>;
60
+ setUser: (user: AaspaiUser | null) => void;
61
+ }
62
+ /**
63
+ * Hook to access current user data
64
+ *
65
+ * @returns Object containing:
66
+ * - `user`: Current user object (AaspaiUser | null)
67
+ * - `isLoaded`: Boolean indicating if auth state has been loaded
68
+ * - `updateUser`: Function to update user profile data (returns { error: string } | null)
69
+ * - `setUser`: Internal function to manually set user state
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * function UserProfile() {
74
+ * const { user, isLoaded, updateUser } = useUser();
75
+ *
76
+ * if (!isLoaded) return <div>Loading...</div>;
77
+ * if (!user) return <div>Not signed in</div>;
78
+ *
79
+ * async function handleUpdate(name: string) {
80
+ * const result = await updateUser({ name });
81
+ * if (result?.error) {
82
+ * console.error(result.error);
83
+ * } else {
84
+ * console.log('User updated successfully');
85
+ * }
86
+ * }
87
+ *
88
+ * return (
89
+ * <div>
90
+ * <p>Email: {user.email}</p>
91
+ * {user.name && <p>Name: {user.name}</p>}
92
+ * {user.avatarUrl && <img src={user.avatarUrl} alt="Avatar" />}
93
+ * </div>
94
+ * );
95
+ * }
96
+ * ```
97
+ */
98
+ declare function useUser(): useUserReturn;
99
+
100
+ /**
101
+ * Hook to get all public authentication configuration (OAuth + Email) from Aaspai backend
102
+ *
103
+ * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**
104
+ *
105
+ * This hook lazily fetches all public authentication configuration from the backend
106
+ * only when the component mounts. Using it in other components will cause unnecessary
107
+ * API calls on every page load.
108
+ *
109
+ * @returns Object containing OAuth providers, email auth config, and loading state
110
+ * - `authConfig`: Email authentication configuration object with password rules
111
+ * - `isLoaded`: Boolean indicating if the config has been fetched
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * // ✅ Correct usage - only in SignIn/SignUp components
116
+ * function SignUp() {
117
+ * const { authConfig, isLoaded } = usePublicAuthConfig();
118
+ *
119
+ * if (!isLoaded) return <div>Loading...</div>;
120
+ *
121
+ * return (
122
+ * <div>
123
+ * <p>OAuth providers: {authConfig?.oauthProviders.length}</p>
124
+ * <p>Password min length: {authConfig?.passwordMinLength}</p>
125
+ * </div>
126
+ * );
127
+ * }
128
+ * ```
129
+ *
130
+ * @requires Must be used within AaspaiProvider
131
+ */
132
+ declare function usePublicAuthConfig(): {
133
+ authConfig: GetPublicAuthConfigResponse | null;
134
+ isLoaded: boolean;
135
+ };
136
+
137
+ export { useAuth, usePublicAuthConfig, useUser };
138
+
package/dist/hooks.js ADDED
@@ -0,0 +1,70 @@
1
+ import { createContext, useState, useEffect, useContext } from 'react';
2
+ import 'react/jsx-runtime';
3
+ import { AaspaiContext } from '@aaspai/shared/react';
4
+
5
+ // src/provider/AaspaiProvider.tsx
6
+ createContext(null);
7
+ function useAaspai() {
8
+ const context = useContext(AaspaiContext);
9
+ if (!context) {
10
+ return {
11
+ user: void 0,
12
+ userId: void 0,
13
+ isLoaded: false,
14
+ isSignedIn: void 0,
15
+ setUser: () => {
16
+ },
17
+ signIn: () => Promise.resolve({ error: "SSR mode" }),
18
+ signUp: () => Promise.resolve({ error: "SSR mode" }),
19
+ signOut: () => Promise.resolve(),
20
+ updateUser: () => Promise.resolve({ error: "SSR mode" }),
21
+ reloadAuth: () => Promise.resolve({ success: false, error: "SSR mode" }),
22
+ resendVerificationEmail: () => Promise.resolve(null),
23
+ sendResetPasswordEmail: () => Promise.resolve(null),
24
+ resetPassword: () => Promise.resolve(null),
25
+ verifyEmail: () => Promise.resolve({ error: "SSR mode" }),
26
+ exchangeResetPasswordToken: () => Promise.resolve({ error: { message: "SSR mode" } }),
27
+ loginWithOAuth: () => Promise.resolve(),
28
+ getPublicAuthConfig: () => Promise.resolve(null),
29
+ getSession: () => Promise.resolve(null),
30
+ baseUrl: "",
31
+ afterSignInUrl: "/"
32
+ };
33
+ }
34
+ return context;
35
+ }
36
+
37
+ // src/hooks/useAuth.ts
38
+ function useAuth() {
39
+ const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAaspai();
40
+ return { signIn, signUp, signOut, isLoaded, isSignedIn };
41
+ }
42
+
43
+ // src/hooks/useUser.ts
44
+ function useUser() {
45
+ const { user, isLoaded, updateUser, setUser } = useAaspai();
46
+ return { user, isLoaded, updateUser, setUser };
47
+ }
48
+ function usePublicAuthConfig() {
49
+ const { getPublicAuthConfig } = useAaspai();
50
+ const [authConfig, setAuthConfig] = useState(null);
51
+ const [isLoaded, setIsLoaded] = useState(false);
52
+ useEffect(() => {
53
+ async function fetchConfig() {
54
+ const result = await getPublicAuthConfig();
55
+ if (result) {
56
+ setAuthConfig(result);
57
+ } else {
58
+ console.error("[usePublicAuthConfig] Failed to get public auth config");
59
+ setAuthConfig(null);
60
+ }
61
+ setIsLoaded(true);
62
+ }
63
+ void fetchConfig();
64
+ }, [getPublicAuthConfig]);
65
+ return { authConfig, isLoaded };
66
+ }
67
+
68
+ export { useAuth, usePublicAuthConfig, useUser };
69
+ //# sourceMappingURL=hooks.js.map
70
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/navigation/NavigationContext.tsx","../src/provider/AaspaiProvider.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';\r\nimport type { NavigationAdapter } from './types';\r\n\r\nconst NavigationContext = createContext<NavigationAdapter | null>(null);\r\n\r\nexport interface NavigationProviderProps {\r\n adapter: NavigationAdapter;\r\n children: ReactNode;\r\n}\r\n\r\n/**\r\n * Navigation Provider\r\n * Injects navigation adapter into the component tree\r\n */\r\nexport function NavigationProvider({ adapter, children }: NavigationProviderProps) {\r\n return <NavigationContext.Provider value={adapter}>{children}</NavigationContext.Provider>;\r\n}\r\n\r\n/**\r\n * Hook to access navigation adapter\r\n * @throws Error if used outside NavigationProvider\r\n */\r\nexport function useNavigationAdapter(): NavigationAdapter {\r\n const adapter = useContext(NavigationContext);\r\n\r\n if (!adapter) {\r\n return {\r\n useSearchParams: () => new URLSearchParams(),\r\n Link: ({ href, children, className }) => (\r\n <a href={href} className={className}>\r\n {children}\r\n </a>\r\n ),\r\n };\r\n }\r\n\r\n return adapter;\r\n}\r\n","import { useContext, useEffect, useState, useMemo, type ReactNode } from 'react';\r\nimport type { AaspaiUser, OAuthProvider } from '../types';\r\nimport { NavigationProvider, BrowserNavigationAdapter } from '../navigation';\r\nimport { AaspaiManager, type AaspaiManagerState } from '../core/AaspaiManager';\r\nimport {\r\n AaspaiContext,\r\n AaspaiAuthStateContext,\r\n AaspaiMethodsContext,\r\n AaspaiConfigContext,\r\n type AaspaiContextValue,\r\n type AaspaiAuthState,\r\n type AaspaiAuthMethods,\r\n type AaspaiAuthConfig,\r\n} from '../contexts';\r\nimport { StyleProvider } from '../styles/StyleProvider';\r\nimport { AASPAIClient } from '@aaspai/sdk';\r\n\r\nexport interface InitialAuthState {\r\n user?: AaspaiUser | null;\r\n userId?: string | null;\r\n}\r\n\r\nexport interface AaspaiProviderProps {\r\n /**\r\n * The base URL of the AASPAI backend.\r\n * @deprecated This prop is no longer used.\r\n * Use the client prop instead.\r\n */\r\n baseUrl?: string;\r\n children: ReactNode;\r\n /**\r\n * The AASPAI SDK client instance.\r\n */\r\n client: AASPAIClient;\r\n /**\r\n * URL to redirect to after successful sign in (when token is detected in URL)\r\n * @default '/'\r\n */\r\n afterSignInUrl?: string;\r\n onAuthChange?: (user: AaspaiUser | null) => void;\r\n onSignIn?: (authToken: string, user: AaspaiUser) => Promise<void>;\r\n onSignOut?: () => Promise<void>;\r\n onRefresh?: (authToken: string, user: AaspaiUser) => Promise<void>;\r\n /**\r\n * Initial auth state from server (for SSR hydration)\r\n * @internal - Not intended for public use, used by Next.js package\r\n */\r\n initialState?: InitialAuthState;\r\n}\r\n\r\n/**\r\n * Unified Aaspai Provider - manages authentication state and configuration\r\n *\r\n * Uses singleton AaspaiManager to manage state across packages.\r\n * Context only subscribes to Manager and triggers React re-renders.\r\n *\r\n * Architecture (pattern learned from Clerk):\r\n * - AaspaiMethodsContext: Stable method references (NEVER change)\r\n * - AaspaiAuthStateContext: Reactive auth state (changes on sign in/out)\r\n * - AaspaiConfigContext: Static configuration values\r\n * - AaspaiContext: Combined context for backward compatibility\r\n *\r\n * This architecture prevents useEffect re-runs when auth state changes,\r\n * solving the \"duplicate request\" bug in components like VerifyEmail.\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage (React/Vite)\r\n * import { AaspaiProvider } from '@aaspai/react';\r\n * import { createClient } from '@aaspai/sdk';\r\n *\r\n * const client = createClient({\r\n * baseUrl: import.meta.env.VITE_INSFORGE_BASE_URL,\r\n * });\r\n *\r\n * export default function MyApp() {\r\n * return (\r\n * <AaspaiProvider client={client}>\r\n * <App />\r\n * </AaspaiProvider>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```tsx\r\n * // With cookie sync (Next.js optimization)\r\n * <AaspaiProvider\r\n * client={client}\r\n * afterSignInUrl=\"/dashboard\"\r\n * onSignIn={async (authToken) => {\r\n * await signIn(authToken);\r\n * }}\r\n * onSignOut={async () => {\r\n * await signOut();\r\n * }}\r\n * >\r\n * {children}\r\n * </AaspaiProvider>\r\n * ```\r\n */\r\nexport function AaspaiProviderCore({\r\n children,\r\n client,\r\n afterSignInUrl = '/',\r\n onAuthChange,\r\n onSignIn,\r\n onSignOut,\r\n onRefresh,\r\n initialState,\r\n}: AaspaiProviderProps) {\r\n // Get singleton Manager instance\r\n const manager: AaspaiManager = useMemo(\r\n () =>\r\n AaspaiManager.getInstance({\r\n client,\r\n afterSignInUrl,\r\n onAuthChange,\r\n onSignIn,\r\n onSignOut,\r\n onRefresh,\r\n }),\r\n [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]\r\n );\r\n\r\n // Set initialState if provided (from Server Component)\r\n // This must happen during render, before useState initialization\r\n if (initialState) {\r\n const currentState = manager.getState();\r\n if (currentState.userId === undefined && initialState.userId !== undefined) {\r\n manager.setInitialState(initialState);\r\n }\r\n }\r\n\r\n // Subscribe to Manager state\r\n // Start with initial state from manager (will include initialState if set above)\r\n const [state, setState] = useState<AaspaiManagerState>(() => manager.getState());\r\n\r\n useEffect(() => {\r\n // Subscribe to state changes\r\n const unsubscribe = manager.subscribe((newState: AaspaiManagerState) => {\r\n setState(newState);\r\n });\r\n\r\n // Initialize auth state\r\n void manager.initialize();\r\n\r\n return () => {\r\n unsubscribe();\r\n };\r\n }, [manager]);\r\n\r\n // ============================================\r\n // STABLE METHOD REFERENCES\r\n // These NEVER change once created (only depend on manager)\r\n // This prevents useEffect re-runs when state changes\r\n // ============================================\r\n const stableMethods = useMemo<AaspaiAuthMethods>(\r\n () => ({\r\n setUser: (user: AaspaiUser | null) => manager.setUser(user),\r\n signIn: (email: string, password: string) => manager.signIn(email, password),\r\n signUp: (email: string, password: string, options?: { emailRedirectTo?: string }) =>\r\n manager.signUp(email, password, options),\r\n signOut: () => manager.signOut(),\r\n updateUser: (data: AaspaiUser['profile']) => manager.updateUser(data),\r\n reloadAuth: () => manager.reloadAuth(),\r\n resendVerificationEmail: (email: string, options?: { emailRedirectTo?: string }) =>\r\n manager.resendVerificationEmail(email, options),\r\n sendResetPasswordEmail: (email: string) => manager.sendResetPasswordEmail(email),\r\n resetPassword: (token: string, newPassword: string) =>\r\n manager.resetPassword(token, newPassword),\r\n verifyEmail: (otp: string, email?: string) => manager.verifyEmail(otp, email),\r\n exchangeResetPasswordToken: (email: string, code: string) =>\r\n manager.exchangeResetPasswordToken(email, code),\r\n loginWithOAuth: (provider: OAuthProvider, redirectTo: string) =>\r\n manager.loginWithOAuth(provider, redirectTo),\r\n getPublicAuthConfig: () => manager.getPublicAuthConfig(),\r\n getSession: () => manager.getSession(),\r\n }),\r\n [manager] // Only depends on manager (stable)\r\n );\r\n\r\n // ============================================\r\n // STABLE CONFIG REFERENCES\r\n // These rarely change after initialization\r\n // ============================================\r\n const stableConfig = useMemo<AaspaiAuthConfig>(\r\n () => ({\r\n baseUrl: manager.getConfig().client.getHttpClient().baseUrl,\r\n afterSignInUrl: manager.getConfig().afterSignInUrl || '/',\r\n }),\r\n [manager] // Only depends on manager (stable)\r\n );\r\n\r\n // ============================================\r\n // REACTIVE AUTH STATE\r\n // This updates when auth state changes (sign in/out)\r\n // ============================================\r\n const authState = useMemo<AaspaiAuthState>(\r\n () => ({\r\n user: state.user,\r\n userId: state.userId,\r\n isLoaded: state.isLoaded,\r\n isSignedIn: state.isSignedIn,\r\n }),\r\n [state.user, state.userId, state.isLoaded, state.isSignedIn]\r\n );\r\n\r\n // ============================================\r\n // COMBINED CONTEXT VALUE (for backward compatibility)\r\n // Uses stable method references from stableMethods\r\n // ============================================\r\n const contextValue = useMemo<AaspaiContextValue>(\r\n () => ({\r\n // Reactive state\r\n ...authState,\r\n // Stable methods (references don't change!)\r\n ...stableMethods,\r\n // Stable config\r\n ...stableConfig,\r\n }),\r\n [authState, stableMethods, stableConfig]\r\n );\r\n\r\n // Multi-context architecture: each context has its own responsibility\r\n // - MethodsContext: stable method references\r\n // - AuthStateContext: reactive auth state\r\n // - ConfigContext: static configuration\r\n // - AaspaiContext: combined for backward compatibility\r\n return (\r\n <AaspaiMethodsContext.Provider value={stableMethods}>\r\n <AaspaiConfigContext.Provider value={stableConfig}>\r\n <AaspaiAuthStateContext.Provider value={authState}>\r\n <AaspaiContext.Provider value={contextValue}>{children}</AaspaiContext.Provider>\r\n </AaspaiAuthStateContext.Provider>\r\n </AaspaiConfigContext.Provider>\r\n </AaspaiMethodsContext.Provider>\r\n );\r\n}\r\n\r\nexport function AaspaiProvider(props: AaspaiProviderProps) {\r\n return (\r\n <StyleProvider>\r\n <NavigationProvider adapter={BrowserNavigationAdapter}>\r\n <AaspaiProviderCore {...props} />\r\n </NavigationProvider>\r\n </StyleProvider>\r\n );\r\n}\r\n\r\n/**\r\n * Hook to access Aaspai context\r\n *\r\n * Works seamlessly across packages thanks to singleton Manager.\r\n * Context instance is guaranteed to be consistent.\r\n *\r\n * @example\r\n * ```tsx\r\n * function MyComponent() {\r\n * const { user, isSignedIn, signOut } = useAaspai();\r\n *\r\n * if (!isSignedIn) return <SignIn />;\r\n *\r\n * return (\r\n * <div>\r\n * <p>Welcome {user.email}</p>\r\n * <button onClick={signOut}>Sign Out</button>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAaspai(): AaspaiContextValue {\r\n const context: AaspaiContextValue | undefined = useContext(AaspaiContext);\r\n\r\n if (!context) {\r\n return {\r\n user: undefined,\r\n userId: undefined,\r\n isLoaded: false,\r\n isSignedIn: undefined,\r\n setUser: () => {},\r\n signIn: () => Promise.resolve({ error: 'SSR mode' }),\r\n signUp: () => Promise.resolve({ error: 'SSR mode' }),\r\n signOut: () => Promise.resolve(),\r\n updateUser: () => Promise.resolve({ error: 'SSR mode' }),\r\n reloadAuth: () => Promise.resolve({ success: false, error: 'SSR mode' }),\r\n resendVerificationEmail: () => Promise.resolve(null),\r\n sendResetPasswordEmail: () => Promise.resolve(null),\r\n resetPassword: () => Promise.resolve(null),\r\n verifyEmail: () => Promise.resolve({ error: 'SSR mode' }),\r\n exchangeResetPasswordToken: () => Promise.resolve({ error: { message: 'SSR mode' } }),\r\n loginWithOAuth: () => Promise.resolve(),\r\n getPublicAuthConfig: () => Promise.resolve(null),\r\n getSession: () => Promise.resolve(null),\r\n baseUrl: '',\r\n afterSignInUrl: '/',\r\n };\r\n }\r\n\r\n return context;\r\n}\r\n","import { CreateSessionResponse, CreateUserResponse } from '@aaspai/shared-schemas';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\n/**\r\n * Hook to access authentication methods\r\n *\r\n * @returns Object containing:\r\n * - `signIn`: Function to sign in with email and password\r\n * - `signUp`: Function to sign up with email and password\r\n * - `signOut`: Function to sign out the current user\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `isSignedIn`: Boolean indicating if user is currently signed in\r\n *\r\n * @example\r\n * ```tsx\r\n * function LoginForm() {\r\n * const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAuth();\r\n *\r\n * async function handleLogin(email: string, password: string) {\r\n * try {\r\n * await signIn(email, password);\r\n * // User is now signed in\r\n * } catch (error) {\r\n * console.error('Sign in failed:', error);\r\n * }\r\n * }\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n *\r\n * return (\r\n * <form onSubmit={(e) => { e.preventDefault(); handleLogin(email, password); }}>\r\n * {/* form fields *\\/}\r\n * </form>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useAuth(): {\r\n signIn: (\r\n email: string,\r\n password: string\r\n ) => Promise<CreateSessionResponse | { error: string; statusCode?: number; errorCode?: string }>;\r\n signUp: (\r\n email: string,\r\n password: string\r\n ) => Promise<CreateUserResponse | { error: string; statusCode?: number; errorCode?: string }>;\r\n signOut: () => Promise<void>;\r\n isLoaded: boolean;\r\n isSignedIn: boolean | undefined;\r\n} {\r\n const { signIn, signUp, signOut, isLoaded, isSignedIn } = useAaspai();\r\n return { signIn, signUp, signOut, isLoaded, isSignedIn };\r\n}\r\n","import { AaspaiUser } from '@aaspai/shared';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\nexport interface useUserReturn {\r\n user: AaspaiUser | null | undefined;\r\n isLoaded: boolean;\r\n updateUser: (data: AaspaiUser['profile']) => Promise<{ error: string } | null>;\r\n setUser: (user: AaspaiUser | null) => void;\r\n}\r\n\r\n/**\r\n * Hook to access current user data\r\n *\r\n * @returns Object containing:\r\n * - `user`: Current user object (AaspaiUser | null)\r\n * - `isLoaded`: Boolean indicating if auth state has been loaded\r\n * - `updateUser`: Function to update user profile data (returns { error: string } | null)\r\n * - `setUser`: Internal function to manually set user state\r\n *\r\n * @example\r\n * ```tsx\r\n * function UserProfile() {\r\n * const { user, isLoaded, updateUser } = useUser();\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n * if (!user) return <div>Not signed in</div>;\r\n *\r\n * async function handleUpdate(name: string) {\r\n * const result = await updateUser({ name });\r\n * if (result?.error) {\r\n * console.error(result.error);\r\n * } else {\r\n * console.log('User updated successfully');\r\n * }\r\n * }\r\n *\r\n * return (\r\n * <div>\r\n * <p>Email: {user.email}</p>\r\n * {user.name && <p>Name: {user.name}</p>}\r\n * {user.avatarUrl && <img src={user.avatarUrl} alt=\"Avatar\" />}\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport function useUser(): useUserReturn {\r\n const { user, isLoaded, updateUser, setUser } = useAaspai();\r\n return { user, isLoaded, updateUser, setUser };\r\n}\r\n","import { useState, useEffect } from 'react';\r\nimport type { GetPublicAuthConfigResponse } from '@aaspai/shared-schemas';\r\nimport { useAaspai } from '../provider/AaspaiProvider';\r\n\r\n/**\r\n * Hook to get all public authentication configuration (OAuth + Email) from Aaspai backend\r\n *\r\n * **IMPORTANT: This hook should ONLY be used in SignIn and SignUp components.**\r\n *\r\n * This hook lazily fetches all public authentication configuration from the backend\r\n * only when the component mounts. Using it in other components will cause unnecessary\r\n * API calls on every page load.\r\n *\r\n * @returns Object containing OAuth providers, email auth config, and loading state\r\n * - `authConfig`: Email authentication configuration object with password rules\r\n * - `isLoaded`: Boolean indicating if the config has been fetched\r\n *\r\n * @example\r\n * ```tsx\r\n * // ✅ Correct usage - only in SignIn/SignUp components\r\n * function SignUp() {\r\n * const { authConfig, isLoaded } = usePublicAuthConfig();\r\n *\r\n * if (!isLoaded) return <div>Loading...</div>;\r\n *\r\n * return (\r\n * <div>\r\n * <p>OAuth providers: {authConfig?.oauthProviders.length}</p>\r\n * <p>Password min length: {authConfig?.passwordMinLength}</p>\r\n * </div>\r\n * );\r\n * }\r\n * ```\r\n *\r\n * @requires Must be used within AaspaiProvider\r\n */\r\nexport function usePublicAuthConfig(): {\r\n authConfig: GetPublicAuthConfigResponse | null;\r\n isLoaded: boolean;\r\n} {\r\n const { getPublicAuthConfig } = useAaspai();\r\n const [authConfig, setAuthConfig] = useState<GetPublicAuthConfigResponse | null>(null);\r\n const [isLoaded, setIsLoaded] = useState(false);\r\n\r\n useEffect(() => {\r\n async function fetchConfig() {\r\n const result = await getPublicAuthConfig();\r\n if (result) {\r\n setAuthConfig(result);\r\n } else {\r\n console.error('[usePublicAuthConfig] Failed to get public auth config');\r\n setAuthConfig(null);\r\n }\r\n setIsLoaded(true);\r\n }\r\n\r\n void fetchConfig();\r\n }, [getPublicAuthConfig]);\r\n\r\n return { authConfig, isLoaded };\r\n}\r\n"]}