/** * @fileoverview API Context for Writenex client * * Provides a shared API client instance across all components using React Context. * This prevents unnecessary recreation of the API client on every hook call. * * @module @writenex/astro/client/context/ApiContext */ import { createContext, type ReactElement, type ReactNode, useContext, useMemo, } from "react"; import { createApiClient } from "../hooks/useApi"; import { createVersionApiClient } from "../hooks/useVersionHistory"; /** * API client type inferred from createApiClient */ type ApiClient = ReturnType; /** * Version API client type inferred from createVersionApiClient */ type VersionApiClient = ReturnType; /** * Context value containing the API client and base URL */ interface ApiContextValue { client: ApiClient; versionClient: VersionApiClient; apiBase: string; } /** * API Context - holds the shared API client instance */ const ApiContext = createContext(null); /** * Props for ApiProvider */ interface ApiProviderProps { /** Base URL for API requests */ apiBase: string; /** Child components */ children: ReactNode; } /** * API Provider component * * Wraps the application and provides a shared API client instance * to all child components via context. * * @example * ```tsx * * * * ``` */ export function ApiProvider({ apiBase, children, }: ApiProviderProps): ReactElement { // Memoize the API clients to prevent recreation on re-renders const value = useMemo( () => ({ client: createApiClient({ apiBase }), versionClient: createVersionApiClient({ apiBase }), apiBase, }), [apiBase] ); return {children}; } /** * Hook to access the shared API client * * Must be used within an ApiProvider. * * @returns The API context value containing client and apiBase * @throws Error if used outside of ApiProvider * * @example * ```tsx * function MyComponent() { * const { client } = useApiContext(); * // Use client.getCollections(), client.getContent(), etc. * } * ``` */ export function useApiContext(): ApiContextValue { const context = useContext(ApiContext); if (!context) { throw new Error("useApiContext must be used within an ApiProvider"); } return context; } /** * Hook to access just the API client * * Convenience wrapper around useApiContext for simpler access to the client. * * @returns The shared API client instance * * @example * ```tsx * function MyComponent() { * const api = useSharedApi(); * const collections = await api.getCollections(); * } * ``` */ export function useSharedApi(): ApiClient { const { client } = useApiContext(); return client; } /** * Hook to access the API base URL from context * * @returns The API base URL string */ export function useApiBase(): string { const { apiBase } = useApiContext(); return apiBase; } /** * Hook to access the shared version API client * * @returns The shared version API client instance * * @example * ```tsx * function MyComponent() { * const versionApi = useSharedVersionApi(); * const versions = await versionApi.listVersions(collection, contentId); * } * ``` */ export function useSharedVersionApi(): VersionApiClient { const { versionClient } = useApiContext(); return versionClient; }