import React, { createContext, useContext, ReactNode, useState, useCallback, useMemo } from "react"; interface ApiContextType { baseUrl: string; wsBaseUrl: string; fetchWithHeaders: (url: string, options?: RequestInit) => Promise; } const ApiContext = createContext(undefined); const STORAGE_KEY = "lelab.apiBaseUrl"; const DEFAULT_LOCALHOST = "http://localhost:8000"; const httpToWs = (url: string): string => url.replace(/^http(s?):/, "ws$1:"); const resolveInitialBaseUrl = (): string => { if (typeof window === "undefined") return DEFAULT_LOCALHOST; const fromQuery = new URLSearchParams(window.location.search).get("api"); if (fromQuery) { try { new URL(fromQuery); const clean = fromQuery.replace(/\/$/, ""); window.localStorage.setItem(STORAGE_KEY, clean); return clean; } catch { console.warn("Invalid `api` query param, ignoring:", fromQuery); } } return window.localStorage.getItem(STORAGE_KEY) || DEFAULT_LOCALHOST; }; export const ApiProvider: React.FC<{ children: ReactNode }> = ({ children, }) => { const [baseUrl] = useState(resolveInitialBaseUrl); const wsBaseUrl = httpToWs(baseUrl); const fetchWithHeaders = useCallback(async (url: string, options: RequestInit = {}): Promise => { return fetch(url, { ...options, headers: { "Content-Type": "application/json", ...options.headers, }, }); }, []); const value = useMemo( () => ({ baseUrl, wsBaseUrl, fetchWithHeaders }), [baseUrl, wsBaseUrl, fetchWithHeaders] ); return {children}; }; export const useApi = (): ApiContextType => { const context = useContext(ApiContext); if (context === undefined) { throw new Error("useApi must be used within an ApiProvider"); } return context; };