import type { ReactNode } from 'react'; import { useMemo } from 'react'; import { AuthProvider } from './AuthProvider'; import type { User, DevAuthOverride } from './types'; import type { DevUserState, DevUserContextValue } from '@lilith/ui-dev-tools'; /** * Function to map dev user state to a mock User object. * Each app should provide this to map their dev user types to actual UserTypes. */ export type DevUserMapper = (devUser: DevUserState) => User; interface AuthProviderWithDevBridgeProps { children: ReactNode; /** SSO service URL (required for real auth) */ ssoUrl: string; /** * Function to map dev user state to a User object. * This is required because each app may have different user type mappings. * * @example * ```tsx * const mapDevUser: DevUserMapper = (devUser) => ({ * id: devUser.userId || 'dev-user', * email: `${devUser.primaryType}@dev.local`, * username: devUser.displayName, * role: UserRole.USER, * userTypes: devUser.userTypes.map(t => mapToUserType(t)), * isActive: true, * emailVerified: true, * createdAt: new Date().toISOString(), * updatedAt: new Date().toISOString(), * }); * ``` */ mapDevUser: DevUserMapper; } /** * Inner component that tries to use DevUser context. * If context is unavailable, falls back to just AuthProvider. */ function AuthProviderWithDevBridgeInner({ children, ssoUrl, mapDevUser, }: AuthProviderWithDevBridgeProps) { // Try to get dev user context - this may throw if DevUserProvider is from a different module instance let devUser: DevUserContextValue | null = null; let contextError = false; try { // Dynamic require to avoid static analysis issues // eslint-disable-next-line @typescript-eslint/no-var-requires const devTools = require('@lilith/ui-dev-tools'); devUser = devTools.useDevUser(); } catch { contextError = true; } const devOverride = useMemo(() => { // Only create override when context is available, in dev mode, AND user is authenticated if (contextError || !devUser || !devUser.isDevMode || !devUser.isAuthenticated) { return undefined; } // Use the provided mapper to create the mock user const mockUser = mapDevUser(devUser); return { isAuthenticated: true, user: mockUser, }; }, [devUser, mapDevUser, contextError]); return ( {children} ); } /** * AuthProvider wrapper that bridges DevUserProvider state to AuthProvider. * * When dev auth is active (import.meta.env.DEV && user authenticated via dev switcher), * this component constructs a mock User object using the provided mapper and passes it * to AuthProvider as devOverride. * * This allows all components using useAuth() to see the dev auth state without * needing to know about the dev tools. * * NOTE: This component is resilient to module instance duplication issues that * can occur with Vite and other bundlers. If DevUserProvider is not found, * it will fall back to standard AuthProvider behavior. * * @example * ```tsx * * * * * * * ``` */ export function AuthProviderWithDevBridge(props: AuthProviderWithDevBridgeProps) { return ; }