@moontra/moonui-pro
Version:
Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components
426 lines (363 loc) • 11.6 kB
text/typescript
import { GitHubRepository, GitHubStats, GitHubActivity, LanguageStats, RateLimitInfo, StarHistory } from "./types"
// Cache management
const cache = new Map<string, { data: any; timestamp: number; expiresAt: number }>()
// Docs mode detection
const isDocsMode = () => {
return typeof window !== "undefined" &&
(window.location.pathname.includes("/docs/") ||
window.location.pathname.includes("/components/"))
}
// Get cache duration based on mode
const getCacheDuration = (defaultDuration: number) => {
// Increase cache duration to 30 minutes in docs mode
return isDocsMode() ? 1800000 : defaultDuration // 30 minutes : default
}
// Language colors
export const LANGUAGE_COLORS: Record<string, string> = {
JavaScript: "#f7df1e",
TypeScript: "#3178c6",
Python: "#3776ab",
Java: "#ed8b00",
"C++": "#00599c",
"C#": "#239120",
Go: "#00add8",
Rust: "#000000",
Swift: "#fa7343",
Kotlin: "#7f52ff",
PHP: "#777bb4",
Ruby: "#cc342d",
HTML: "#e34f26",
CSS: "#1572b6",
Vue: "#4fc08d",
React: "#61dafb",
Shell: "#89e051",
Dart: "#0175c2",
Elixir: "#6e4a7e",
Scala: "#c22d40",
R: "#198ce7",
Julia: "#9558b2",
Lua: "#000080",
Perl: "#39457e",
Haskell: "#5e5086",
Clojure: "#db5855",
Erlang: "#b83998",
Objective_C: "#438eff",
// Add more as needed
}
// API base URL
const API_BASE = "https://api.github.com"
// Helper to make authenticated requests
async function githubFetch(url: string, token?: string): Promise<Response> {
const headers: HeadersInit = {
Accept: "application/vnd.github.v3+json",
}
if (token) {
headers.Authorization = `token ${token}`
}
const response = await fetch(url, { headers })
if (response.status === 403 && response.headers.get("X-RateLimit-Remaining") === "0") {
const resetTime = parseInt(response.headers.get("X-RateLimit-Reset") || "0") * 1000
const resetDate = new Date(resetTime)
throw new Error(`GitHub API rate limit exceeded. Resets at ${resetDate.toLocaleTimeString()}`)
}
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status} ${response.statusText}`)
}
return response
}
// Get rate limit info
export async function getRateLimitInfo(token?: string): Promise<RateLimitInfo> {
const cacheKey = `rate-limit-${token || "public"}`
const cached = cache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.data
}
const response = await githubFetch(`${API_BASE}/rate_limit`, token)
const data = await response.json()
const rateLimitInfo: RateLimitInfo = {
limit: data.rate.limit,
remaining: data.rate.remaining,
reset: data.rate.reset * 1000,
used: data.rate.used,
}
cache.set(cacheKey, {
data: rateLimitInfo,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(60000), // Docs modunda daha uzun cache
})
return rateLimitInfo
}
// Fetch user repositories
export async function fetchUserRepositories(
username: string,
token?: string,
options?: {
sort?: string
per_page?: number
page?: number
}
): Promise<GitHubRepository[]> {
const params = new URLSearchParams({
sort: options?.sort || "updated",
per_page: String(options?.per_page || 100),
page: String(options?.page || 1),
})
const cacheKey = `repos-${username}-${params.toString()}`
const cached = cache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.data
}
const response = await githubFetch(`${API_BASE}/users/${username}/repos?${params}`, token)
const repos = await response.json()
cache.set(cacheKey, {
data: repos,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(300000), // 30 minutes in docs mode
})
return repos
}
// Fetch single repository
export async function fetchRepository(
owner: string,
repo: string,
token?: string
): Promise<GitHubRepository> {
const cacheKey = `repo-${owner}-${repo}`
const cached = cache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.data
}
const response = await githubFetch(`${API_BASE}/repos/${owner}/${repo}`, token)
const repository = await response.json()
cache.set(cacheKey, {
data: repository,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(300000), // 30 minutes in docs mode
})
return repository
}
// Fetch repository contributors count
export async function fetchContributorsCount(
owner: string,
repo: string,
token?: string
): Promise<number> {
const cacheKey = `contributors-${owner}-${repo}`
const cached = cache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.data
}
try {
const response = await githubFetch(
`${API_BASE}/repos/${owner}/${repo}/contributors?per_page=1&anon=true`,
token
)
// Get total count from Link header
const linkHeader = response.headers.get("Link")
if (linkHeader) {
const match = linkHeader.match(/page=(\d+)>; rel="last"/)
if (match) {
const count = parseInt(match[1])
cache.set(cacheKey, {
data: count,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
})
return count
}
}
// If no pagination, count the results
const contributors = await response.json()
const count = contributors.length
cache.set(cacheKey, {
data: count,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
})
return count
} catch (error) {
console.error("Failed to fetch contributors:", error)
return 0
}
}
// Fetch repository star history (limited without token)
export async function fetchStarHistory(
owner: string,
repo: string,
token?: string
): Promise<StarHistory[]> {
const cacheKey = `star-history-${owner}-${repo}`
const cached = cache.get(cacheKey)
if (cached && cached.expiresAt > Date.now()) {
return cached.data
}
try {
// This is a simplified version - for full star history you'd need
// to use the stargazers API with Accept: application/vnd.github.v3.star+json
// header and paginate through all results
const response = await githubFetch(`${API_BASE}/repos/${owner}/${repo}`, token)
const repoData = await response.json()
// For now, return current count as single data point
const history: StarHistory[] = [{
date: new Date().toISOString(),
count: repoData.stargazers_count,
repository: repoData.full_name,
}]
cache.set(cacheKey, {
data: history,
timestamp: Date.now(),
expiresAt: Date.now() + getCacheDuration(3600000), // 30 minutes in docs mode
})
return history
} catch (error) {
console.error("Failed to fetch star history:", error)
return []
}
}
// Calculate repository statistics
export function calculateStats(repositories: GitHubRepository[]): GitHubStats {
const totalStars = repositories.reduce((sum, repo) => sum + repo.stargazers_count, 0)
const totalForks = repositories.reduce((sum, repo) => sum + repo.forks_count, 0)
const totalWatchers = repositories.reduce((sum, repo) => sum + repo.watchers_count, 0)
const totalIssues = repositories.reduce((sum, repo) => sum + repo.open_issues_count, 0)
const avgStarsPerRepo = repositories.length > 0 ? totalStars / repositories.length : 0
const mostStarredRepo = repositories.reduce((max, repo) =>
repo.stargazers_count > (max?.stargazers_count || 0) ? repo : max
, null as GitHubRepository | null)
// Calculate language statistics
const languageMap = new Map<string, number>()
repositories.forEach(repo => {
if (repo.language) {
languageMap.set(repo.language, (languageMap.get(repo.language) || 0) + 1)
}
})
const totalRepos = repositories.length
const languages: LanguageStats[] = Array.from(languageMap.entries())
.map(([language, count]) => ({
language,
count,
percentage: (count / totalRepos) * 100,
color: LANGUAGE_COLORS[language] || "#6b7280",
}))
.sort((a, b) => b.count - a.count)
// Generate recent activity (mock for now)
const recentActivity: GitHubActivity[] = repositories
.slice(0, 5)
.map(repo => ({
type: "star" as const,
repository: repo.full_name,
timestamp: repo.updated_at,
description: `Repository updated`,
}))
return {
totalStars,
totalForks,
totalWatchers,
totalIssues,
avgStarsPerRepo,
mostStarredRepo,
recentActivity,
languages,
}
}
// Format numbers for display
export function formatNumber(num: number): string {
if (num >= 1000000) {
return (num / 1000000).toFixed(1) + "M"
}
if (num >= 1000) {
return (num / 1000).toFixed(1) + "k"
}
return num.toString()
}
// Format date for display
export function formatDate(dateString: string): string {
const date = new Date(dateString)
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
if (diffDays === 0) {
const diffHours = Math.floor(diffMs / (1000 * 60 * 60))
if (diffHours === 0) {
const diffMinutes = Math.floor(diffMs / (1000 * 60))
return `${diffMinutes} minutes ago`
}
return `${diffHours} hours ago`
}
if (diffDays === 1) return "yesterday"
if (diffDays < 7) return `${diffDays} days ago`
if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`
if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
})
}
// Clear cache
export function clearCache(pattern?: string): void {
if (pattern) {
for (const key of cache.keys()) {
if (key.includes(pattern)) {
cache.delete(key)
}
}
} else {
cache.clear()
}
}
// Export data as JSON
export function exportData(data: any, filename: string): void {
const jsonString = JSON.stringify(data, null, 2)
const blob = new Blob([jsonString], { type: "application/json" })
const url = URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
}
// Export data as CSV
export function exportAsCSV(repositories: GitHubRepository[], filename: string): void {
const headers = [
"Name",
"Owner",
"Stars",
"Forks",
"Watchers",
"Issues",
"Language",
"Description",
"URL",
"Created",
"Updated",
]
const rows = repositories.map(repo => [
repo.name,
repo.owner.login,
repo.stargazers_count,
repo.forks_count,
repo.watchers_count,
repo.open_issues_count,
repo.language || "",
repo.description || "",
repo.html_url,
new Date(repo.created_at).toLocaleDateString(),
new Date(repo.updated_at).toLocaleDateString(),
])
const csvContent = [
headers.join(","),
...rows.map(row => row.map(cell => `"${cell}"`).join(",")),
].join("\n")
const blob = new Blob([csvContent], { type: "text/csv" })
const url = URL.createObjectURL(blob)
const link = document.createElement("a")
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
}