1import type { SourceControlProviderAuth } from "@t3tools/contracts";
2import type { GitHubSettings } from "@t3tools/client-runtime/source-control-clients";
3
4export type GitHubDiscoveredAccount = NonNullable<SourceControlProviderAuth["accounts"]>[number];
5
6export interface GitHubHostGroup {
7 readonly host: string;
8 /** gh's active stored login, the one used when Settings pin nothing. */
9 readonly activeAccount: string | null;
10 /** Stored logins that work and can be pinned with `gh auth token --user`. */
11 readonly selectable: ReadonlyArray<string>;
12 /** Logins gh holds but cannot use; shown with their error. */
13 readonly broken: ReadonlyArray<GitHubDiscoveredAccount>;
14 /** Set when GH_TOKEN or a sibling overrides every stored login for this host. */
15 readonly environmentVariable: string | null;
16}
17
18/** Groups gh's logins by host, in the order gh reported the hosts. */
19export function groupGitHubAccounts(
20 accounts: ReadonlyArray<GitHubDiscoveredAccount>,
21): ReadonlyArray<GitHubHostGroup> {
22 const hosts = [...new Set(accounts.map((entry) => entry.host))];
23 return hosts.map((host) => {
24 const entries = accounts.filter((entry) => entry.host === host);
25 const stored = entries.filter((entry) => entry.environmentVariable === undefined);
26 const selectable = stored.filter((entry) => entry.authenticated);
27 return {
28 host,
29 activeAccount: (selectable.find((entry) => entry.active) ?? selectable[0])?.account ?? null,
30 selectable: selectable.map((entry) => entry.account),
31 broken: stored.filter((entry) => !entry.authenticated),
32 environmentVariable:
33 entries.find((entry) => entry.environmentVariable)?.environmentVariable ?? null,
34 };
35 });
36}
37
38/**
39 * The full `hosts` map after one host changes. Hosts left on gh's defaults (enabled,
40 * no pinned account) are dropped so settings only hold real choices.
41 */
42export function nextGitHubHosts(
43 current: GitHubSettings["hosts"],
44 host: string,
45 change: { readonly enabled?: boolean; readonly account?: string | null },
46): GitHubSettings["hosts"] {
47 const previous = current[host];
48 const enabled = change.enabled ?? previous?.enabled ?? true;
49 const account = change.account === undefined ? previous?.account : (change.account ?? undefined);
50 const { [host]: _replaced, ...rest } = current;
51 if (enabled && account === undefined) return rest;
52 return { ...rest, [host]: { enabled, ...(account === undefined ? {} : { account }) } };
53}