@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.
- package/README.md +720 -8
- package/dist/atoms.cjs +3565 -0
- package/dist/atoms.cjs.map +1 -0
- package/dist/atoms.d.cts +255 -0
- package/dist/atoms.d.ts +255 -0
- package/dist/atoms.js +3530 -0
- package/dist/atoms.js.map +1 -0
- package/dist/components.cjs +5397 -0
- package/dist/components.cjs.map +1 -0
- package/dist/components.d.cts +362 -0
- package/dist/components.d.ts +362 -0
- package/dist/components.js +5344 -0
- package/dist/components.js.map +1 -0
- package/dist/forms.cjs +3928 -0
- package/dist/forms.cjs.map +1 -0
- package/dist/forms.d.cts +135 -0
- package/dist/forms.d.ts +135 -0
- package/dist/forms.js +3903 -0
- package/dist/forms.js.map +1 -0
- package/dist/hooks.cjs +74 -0
- package/dist/hooks.cjs.map +1 -0
- package/dist/hooks.d.cts +138 -0
- package/dist/hooks.d.ts +138 -0
- package/dist/hooks.js +70 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.cjs +6030 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +248 -0
- package/dist/index.d.ts +248 -0
- package/dist/index.js +5952 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.cjs +139 -0
- package/dist/lib.cjs.map +1 -0
- package/dist/lib.d.cts +111 -0
- package/dist/lib.d.ts +111 -0
- package/dist/lib.js +128 -0
- package/dist/lib.js.map +1 -0
- package/dist/navigation.cjs +56 -0
- package/dist/navigation.cjs.map +1 -0
- package/dist/navigation.d.cts +65 -0
- package/dist/navigation.d.ts +65 -0
- package/dist/navigation.js +51 -0
- package/dist/navigation.js.map +1 -0
- package/dist/styles.css +839 -0
- package/dist/types.cjs +4 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +17 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -28
- package/index.cjs +0 -10
- package/index.d.ts +0 -6
- package/index.js +0 -5
- 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"]}
|
package/dist/hooks.d.cts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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"]}
|