import { useEffect, useState } from 'react' import { configApi, type PublicConfig } from '@/api/config' /** * Module-scope cache: the public config endpoint is fetched at most once * per page load. Subsequent hook mounts return the cached value synchronously * (after the initial state update). */ let cached: PublicConfig | null = null let inFlight: Promise | null = null const subscribers = new Set<(c: PublicConfig) => void>() function envFallback(): PublicConfig { // Falls back to build-time flag when the public config endpoint is // unreachable. Defaults to the legacy invite-only behavior so that // a backend hiccup never opens public signup. const selfServe = String(import.meta.env.VITE_SELF_SERVE_ENABLED ?? '').toLowerCase() === 'true' return { self_serve_enabled: selfServe, oauth_providers: [], } } async function loadConfig(): Promise { if (cached) return cached if (inFlight) return inFlight inFlight = configApi .getPublic() .then((c) => { cached = c subscribers.forEach((cb) => cb(c)) return c }) .catch(() => { const fallback = envFallback() cached = fallback subscribers.forEach((cb) => cb(fallback)) return fallback }) .finally(() => { inFlight = null }) return inFlight } /** Test-only: clear the module-scope cache between tests. */ export function __resetAppConfigCache() { cached = null inFlight = null subscribers.clear() } /** Test-only: prime the module-scope cache so hook returns synchronously. */ export function __setAppConfigCache(c: PublicConfig) { cached = c } export interface UseAppConfigResult { self_serve_enabled: boolean oauth_providers: string[] isLoading: boolean } export function useAppConfig(): UseAppConfigResult { const [config, setConfig] = useState(cached) useEffect(() => { if (cached) { setConfig(cached) return } let active = true const handler = (c: PublicConfig) => { if (active) setConfig(c) } subscribers.add(handler) void loadConfig() return () => { active = false subscribers.delete(handler) } }, []) if (config) { return { self_serve_enabled: config.self_serve_enabled, oauth_providers: config.oauth_providers, isLoading: false, } } return { self_serve_enabled: false, oauth_providers: [], isLoading: true, } } export default useAppConfig