File size: 2,307 Bytes
5710dd0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
 * Local server discovery (browser side) — reads the dev/preview middleware at
 * `/__inspect/servers` (`vite/serverDiscovery.ts`), which scans the local
 * kap-server instance registry and reads the home token. Powers the
 * zero-config startup connect, the header server switcher, and the discovered
 * list on the connect screen. Returns `null` outside dev/preview (no such
 * endpoint on a static host) so the app falls back to the manual flow.
 */

import { useQuery } from '@tanstack/react-query';

export type DiscoveredServerSource = 'instance' | 'lock' | 'proxy';

export interface DiscoveredServer {
  readonly id: string;
  readonly url: string;
  readonly pid?: number;
  readonly startedAt?: number;
  readonly hostVersion?: string;
  readonly source: DiscoveredServerSource;
}

export interface ServerDiscoveryResult {
  readonly home: string;
  readonly token?: string;
  readonly servers: readonly DiscoveredServer[];
}

export async function fetchServerDiscovery(): Promise<ServerDiscoveryResult | null> {
  let res: Response;
  try {
    res = await fetch('/__inspect/servers');
  } catch {
    return null;
  }
  if (!res.ok) return null;
  try {
    const parsed = (await res.json()) as ServerDiscoveryResult;
    return Array.isArray(parsed.servers) ? parsed : null;
  } catch {
    return null;
  }
}

/** Poll discovery: instances come and go (the server-side heartbeat is 15 s). */
export function useServerDiscovery() {
  return useQuery({
    queryKey: ['local-server-discovery'],
    queryFn: fetchServerDiscovery,
    refetchInterval: 10_000,
    retry: false,
    staleTime: 5_000,
  });
}

/** Pick the auto-connect target: the remembered pick when it is still alive,
 * else the dev-proxy target (the standard `pnpm dev:v2` flow), else the
 * longest-running instance. */
export function pickDefaultServer(
  discovery: ServerDiscoveryResult,
  rememberedUrl?: string | null,
): DiscoveredServer | undefined {
  const { servers } = discovery;
  if (servers.length === 0) return undefined;
  if (rememberedUrl !== undefined && rememberedUrl !== null && rememberedUrl !== '') {
    const remembered = servers.find((s) => s.url === rememberedUrl);
    if (remembered !== undefined) return remembered;
  }
  return servers.find((s) => s.source === 'proxy') ?? servers[0];
}