| 123 | } |
| 124 | |
| 125 | export class GitHubProjectRanker { |
| 126 | private static async enrichRepositoriesWithGraphQL( |
| 127 | username: string, |
| 128 | userToken?: string | null |
| 129 | ): Promise<Map<string, GraphQLRepository>> { |
| 130 | try { |
| 131 | const token = userToken || Settings.GITHUB_TOKEN; |
| 132 | if (!token) { |
| 133 | return new Map(); |
| 134 | } |
| 135 | |
| 136 | const graphqlClient = getGraphQLClient(token); |
| 137 | const isAuthenticatedUser = userToken !== null; |
| 138 | const query = isAuthenticatedUser ? REPO_DETAILS_QUERY_ALL : REPO_DETAILS_QUERY_PUBLIC; |
| 139 | |
| 140 | const result = await graphqlClient<{ |
| 141 | user: { |
| 142 | repositories: { |
| 143 | nodes: GraphQLRepository[]; |
| 144 | }; |
| 145 | } | null; |
| 146 | }>(query, { |
| 147 | username, |
| 148 | }); |
| 149 | |
| 150 | if (!result.user) { |
| 151 | return new Map(); |
| 152 | } |
| 153 | |
| 154 | const repoMap = new Map<string, GraphQLRepository>(); |
| 155 | result.user.repositories.nodes.forEach(repo => { |
| 156 | repoMap.set(repo.name, repo); |
| 157 | }); |
| 158 | |
| 159 | return repoMap; |
| 160 | } catch { |
| 161 | return new Map(); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | private static normalizeHomepageUrl(url: string | null | undefined): string | null { |
| 166 | if (!url) return null; |
| 167 | |
| 168 | const trimmed = url.trim(); |
| 169 | if (!trimmed) return null; |
| 170 | |
| 171 | if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) { |
| 172 | try { |
| 173 | new URL(trimmed); |
| 174 | return trimmed; |
| 175 | } catch { |
| 176 | return null; |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | if (trimmed.startsWith('//')) { |
| 181 | return `https:${trimmed}`; |
| 182 | } |
nothing calls this directly
no outgoing calls
no test coverage detected