/** * `useDiscovery` — discovered projects + live health. Posts the known cwds to * `/__nwire/projects/status` on a TanStack poll, merged with the local catalog * so a project shows even when its process is down. */ import { computed } from "vue"; import { useQuery } from "@tanstack/vue-query"; import { loadCatalog, getActiveProjectCwd, type ProjectSnapshot } from "../lib/project-catalog"; const STATUS_URL = "/__nwire/projects/status"; /** Per-project live status from the server (process up? port? pid?). Shape is server-owned. */ export interface ProjectStatus { readonly running?: boolean; readonly port?: number; readonly pid?: number; readonly [k: string]: unknown; } export type StatusMap = Record; /** A discovered project: its catalog snapshot + (best-effort) live status. */ export interface DiscoveredProject { readonly cwd: string; readonly snapshot: ProjectSnapshot; readonly status?: ProjectStatus; readonly active: boolean; } export function useDiscovery(options: { intervalMs?: number } = {}) { const query = useQuery({ queryKey: ["discovery"], queryFn: async (): Promise => { const cwds = Object.keys(loadCatalog()); if (cwds.length === 0) return {}; const res = await fetch(STATUS_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ cwds }), }); if (!res.ok) return {}; return (await res.json()) as StatusMap; }, refetchInterval: options.intervalMs ?? 5_000, placeholderData: (prev) => prev, }); const projects = computed(() => { const catalog = loadCatalog(); const active = getActiveProjectCwd(); const statuses = query.data.value ?? {}; return Object.values(catalog) .map((snapshot) => ({ cwd: snapshot.cwd, snapshot, status: statuses[snapshot.cwd], active: snapshot.cwd === active, })) .sort( (a, b) => Number(b.active) - Number(a.active) || a.snapshot.name.localeCompare(b.snapshot.name), ); }); const runningCount = computed(() => projects.value.filter((p) => p.status?.running).length); return { projects, runningCount, isLoading: query.isLoading, isError: query.isError, refetch: query.refetch, }; }