#!/usr/bin/env ts-node
// github-info.ts — query GitHub's public API
// Usage: ts-node github-info.ts <username> [<username2> ...]
interface GHUser {
login: string
name: string | null
bio: string | null
public_repos: number
followers: number
following: number
location: string | null
blog: string
created_at: string
}
type Result<T> =
| { ok: true; value: T }
| { ok: false; error: string }
async function fetchGH<T>(path: string): Promise<Result<T>> {
try {
const res = await fetch(`https://api.github.com${path}`, {
headers: { "Accept": "application/vnd.github.v3+json" }
})
if (res.status === 404) return { ok: false, error: "Not found" }
if (!res.ok) return { ok: false, error: `HTTP ${res.status}` }
return { ok: true, value: await res.json() as T }
} catch (e) {
return { ok: false, error: e instanceof Error ? e.message : "Network error" }
}
}
function formatUser(u: GHUser): string {
const lines: string[] = [
`── @${u.login} ${u.name ? `(${u.name})` : ""}`,
u.bio ? ` ${u.bio}` : "",
u.location ? ` 📍 ${u.location}` : "",
u.blog ? ` 🔗 ${u.blog}` : "",
` repos: ${u.public_repos} followers: ${u.followers}`,
]
return lines.filter(Boolean).join("\n")
}
async function main(): Promise<void> {
const usernames = process.argv.slice(2)
if (usernames.length === 0) {
console.log("Usage: ts-node github-info.ts <username> [username2] ...")
process.exit(1)
}
const results = await Promise.all(
usernames.map(async (username): Promise<[string, Result<GHUser>]> => [
username,
await fetchGH<GHUser>(`/users/${username}`),
])
)
for (const [username, result] of results) {
if (result.ok) {
console.log(formatUser(result.value))
} else {
console.error(`✗ ${username}: ${result.error}`)
}
console.log()
}
}
main().catch(err => {
console.error(err)
process.exit(1)
})