Documentation Index
Fetch the complete documentation index at: https://mintlify.com/santiagonieto09/portafolio/llms.txt
Use this file to discover all available pages before exploring further.
src/infrastructure/github/github-api.server.ts is the server-only module that owns all GitHub REST API communication. It exports two functions consumed by the rest of the application.
fetchPortfolio(username)
PortfolioSnapshot for the given GitHub
username. The function is designed to always return a valid snapshot — it never
throws and never returns null.
Execution path:
-
Cache check — calls
readSnapshot(). If a fresh snapshot exists (within the 7-day TTL), it is returned immediately with no GitHub API calls. -
Parallel GitHub API calls — fires four requests concurrently via
Promise.all:GET /users/{username}→GitHubUserGET /users/{username}/repos?per_page=100&sort=updated→GitHubRepo[]GET /users/{username}/social_accounts→GitHubSocialAccount[]GET /users/{username}/events/public?per_page=30→GitHubEvent[]
-
Degraded path — if the user call returns
null(GitHub rate-limited or unreachable),readSnapshotOrStale()is tried first. If even a stale snapshot is unavailable,degradedSnapshot(username)is returned as the final fallback. -
Repository enrichment — forked repositories are filtered out. The remaining
repos are passed through
buildRepository()with a maximum concurrency of 3 (viamapLimit) to avoid overwhelming GitHub’s per-token rate limit. -
Snapshot assembly — a
Profile,ActivityItem[](up to 8 entries), andPortfolioStats(including language breakdown fromcomputeLanguages) are built from the API responses. -
Cache write — if the snapshot is not degraded (i.e., all primary data was
fetched successfully),
writeSnapshot()is called and both cache layers are populated. The assembled snapshot is then returned.
invalidatePortfolioCache()
invalidateSnapshot() from the snapshot-cache module. Clears
both the in-memory store and the Cloudflare Cache API entry so the next call to
fetchPortfolio() performs a full re-fetch.
This function is called at the start of every POST /api/public/sync request,
before fetchPortfolio() is invoked, ensuring the response always reflects the
latest data from GitHub.
Internal Helpers
The following functions are not exported. They are documented here for contributors working on the infrastructure layer.get<T>(path, attempts)
500 * 2^attempt). Retries on HTTP
403, 429, and any 5xx status, as well as network-level errors. Returns
null after all attempts are exhausted, or immediately on any other 4xx status.
mapLimit<T, R>(items, limit, fn)
Promise.all. Spawns limit worker coroutines that each pull
from a shared index until all items are processed. Used in fetchPortfolio with
limit = 3 to enrich repositories without saturating the GitHub API.
buildRepository(raw)
GitHubRepo API response into the domain Repository type. Makes
two parallel requests for each repo with attempts = 1 (fast-fail):
GET /repos/{full_name}/languages→ byte counts per languageGET /repos/{full_name}/releases/latest→ latestReleaseInfo
detectTechnologies() on the combined name, description, topics, languages,
and homepage, then maps all fields (stars, forks, license, topics, etc.) to the
domain model.
computeLanguages(repos)
LanguageSlice[] sorted by
descending byte count. Repo-count per language is tracked separately for display.
socialsFrom(user, extra)
SocialLink[] from multiple sources: the GitHub profile’s
blog field, twitter_username, the /social_accounts endpoint, and the profile
email. Each URL is run through safeExternalUrl and matched against a pattern
map to assign a kind (linkedin, x, instagram, facebook, youtube,
blog, or website). Duplicate URLs are removed before the array is returned.
safeExternalUrl(value)
https://). Returns null for any URL
whose parsed protocol is not http: or https:, blocking javascript:, data:,
and similar schemes.
describeEvent(event)
type string to a human-readable Spanish summary. Handled
event types: PushEvent, CreateEvent, ReleaseEvent, WatchEvent, ForkEvent,
PullRequestEvent, IssuesEvent. Any unrecognised type falls back to the type
name with the trailing Event suffix stripped.
degradedSnapshot(username)
PortfolioSnapshot with degraded: true.
All numeric stats are 0, all arrays are empty, and the profile fields are
populated with minimal data derived from the username alone (e.g. avatar URL from
avatars.githubusercontent.com). This allows UI components to render gracefully
without conditional null checks.