platform-codebase/@packages/@providers/auth-provider/src/AuthProviderWithDevBridge.tsx

82 lines
2.5 KiB
TypeScript
Executable file

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;
/**
* Dev user override — pass the result of useDevUser() from the consuming app.
*
* This must come from the same module instance as DevUserProvider to avoid
* React context cross-instance issues. Resolve by calling useDevUser() in the
* consuming app's main.tsx (where DevUserProvider is mounted) and passing the
* state down as this prop.
*/
devUser?: DevUserContextValue;
/**
* Function to map dev user state to a User object.
*/
mapDevUser: DevUserMapper;
}
/**
* AuthProvider wrapper that bridges DevUserProvider state to AuthProvider.
*
* When dev auth is active (import.meta.env.DEV && devUser.isAuthenticated),
* constructs a mock User and passes it to AuthProvider as devOverride.
*
* IMPORTANT: To avoid React context module instance issues (multiple Vite chunks
* resolving @lilith/ui-dev-tools to different instances), the consuming app must
* call useDevUser() itself and pass the result via the `devUser` prop:
*
* @example
* ```tsx
* // In main.tsx — same module instance as DevUserProvider
* const BridgedAuthProvider = ({ children }) => {
* const devUser = useDevUser();
* return (
* <AuthProviderWithDevBridge ssoUrl={ssoUrl} devUser={devUser} mapDevUser={mapDevUser}>
* {children}
* </AuthProviderWithDevBridge>
* );
* };
*
* <DevUserProvider ...>
* <BridgedAuthProvider>
* <App />
* </BridgedAuthProvider>
* </DevUserProvider>
* ```
*/
export function AuthProviderWithDevBridge({
children,
ssoUrl,
devUser,
mapDevUser,
}: AuthProviderWithDevBridgeProps) {
const devOverride = useMemo<DevAuthOverride | undefined>(() => {
if (!devUser || !devUser.isDevMode || !devUser.isAuthenticated) {
return undefined;
}
return {
isAuthenticated: true,
user: mapDevUser(devUser),
};
}, [devUser, mapDevUser]);
return (
<AuthProvider ssoUrl={ssoUrl} devOverride={devOverride}>
{children}
</AuthProvider>
);
}