Ctrl K
ComunidadGuías…

src/services/githubProfile.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
export interface GitHubProfile {
  id: number
  login: string
  name: string | null
  avatarUrl: string
}

export class GitHubProfileError extends Error {
  constructor(public readonly code: 'invalid_username' | 'not_found' | 'rate_limited' | 'request_failed') {
    super(code)
  }
}

export function normalizeGitHubUsername(username: string) {
  return username.trim()
}

export async function getGitHubProfile(username: string): Promise<GitHubProfile> {
  const normalizedUsername = normalizeGitHubUsername(username)
  if (!/^(?:[a-z\d]|[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,37}[a-z\d])$/i.test(normalizedUsername))
    throw new GitHubProfileError('invalid_username')

  let timeout: ReturnType<typeof setTimeout> | undefined
  const signal = typeof AbortSignal.timeout === 'function'
    ? AbortSignal.timeout(10_000)
    : (() => {
        const controller = new AbortController()
        timeout = setTimeout(() => controller.abort(), 10_000)
        return controller.signal
      })()
  try {
    const response = await fetch(`https://api.github.com/users/${encodeURIComponent(normalizedUsername)}`, {
      headers: {
        accept: 'application/vnd.github+json',
      },
      signal,
    })
    if (response.status === 404)
      throw new GitHubProfileError('not_found')
    if (response.status === 429 || (response.status === 403 && (response.headers.get('x-ratelimit-remaining') === '0' || response.headers.has('retry-after'))))
      throw new GitHubProfileError('rate_limited')
    if (!response.ok)
      throw new GitHubProfileError('request_failed')

    const profile = await response.json() as {
      id?: unknown
      login?: unknown
      name?: unknown
      avatar_url?: unknown
    }
    if (!profile || typeof profile !== 'object' || typeof profile.id !== 'number' || typeof profile.login !== 'string' || typeof profile.avatar_url !== 'string')
      throw new GitHubProfileError('request_failed')

    return {
      id: profile.id,
      login: profile.login,
      name: typeof profile.name === 'string' ? profile.name : null,
      avatarUrl: profile.avatar_url,
    }
  }
  catch (error) {
    if (error instanceof GitHubProfileError)
      throw error
    throw new GitHubProfileError('request_failed')
  }
  finally {
    if (timeout)
      clearTimeout(timeout)
  }
}