/** * `useEndpoints` — reactive endpoint list for the active-front picker. * * Fetches `/__nwire/endpoints` on mount and provides a `setActive` helper * that POSTs `/__nwire/endpoints/active` and refreshes the list. * * In a single-endpoint project the list has one entry and the picker is * a no-op — the call still works, the UI just shows one option already * marked active. */ import { ref, onMounted } from "vue"; export const ENDPOINTS_URL = "/__nwire/endpoints"; export const ENDPOINTS_ACTIVE_URL = "/__nwire/endpoints/active"; export interface EndpointInfo { /** Endpoint name — from `endpoint("name")`. */ readonly name: string; /** True when this is the currently active HTTP front. */ readonly active: boolean; /** True when this endpoint registered at least one HTTP handler. */ readonly hasHttp: boolean; } export interface UseEndpointsOptions { /** Inject fetch (tests). Default: global `fetch`. */ readonly fetchFn?: typeof fetch; } export function useEndpoints(options: UseEndpointsOptions = {}) { const doFetch = options.fetchFn ?? ((...a: Parameters) => fetch(...a)); const endpoints = ref([]); const loading = ref(false); const error = ref(null); const switching = ref(false); async function refresh(): Promise { loading.value = true; error.value = null; try { const res = await doFetch(ENDPOINTS_URL); if (!res.ok) { // 501 = route not yet supported (standalone Studio or older host). // Treat as empty — the picker simply won't appear. if (res.status === 501) { endpoints.value = []; return; } error.value = `Failed to load endpoints (${res.status})`; return; } const body = (await res.json()) as { endpoints?: EndpointInfo[] }; endpoints.value = body.endpoints ?? []; } catch (err) { error.value = (err as Error).message; } finally { loading.value = false; } } /** * Switch the active HTTP front. Refreshes the list after a successful switch. * In a single-endpoint project this is a no-op from the server's perspective * (you can only select the one endpoint that is already active). */ async function setActive(name: string): Promise { switching.value = true; error.value = null; try { const res = await doFetch(ENDPOINTS_ACTIVE_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); if (!res.ok) { const body = (await res.json().catch(() => ({}))) as { error?: string }; error.value = body.error ?? `Switch failed (${res.status})`; return; } // Refresh to get the updated active flag. await refresh(); } catch (err) { error.value = (err as Error).message; } finally { switching.value = false; } } onMounted(refresh); return { endpoints, loading, error, switching, refresh, setActive }; }