All notable changes to StarMapper, ordered by release.
get_contributors: top 50 contributors with commit count and optional location enrichment (with_locations param)get_followers: top 100 followers of a GitHub user sorted by influence, with company and locationget_country_stats: full country + city table for a specific indexed repoget_global_country_stats: cross-repo developer distribution by country from the MVget_dependencies: repo's own dependency graph via GitHub SBOM API, graceful disabled statefetchRepoDependencies in src/lib/github.ts: SBOM endpoint, purl parsing, dedup, root package filtering/devs/in/[country]: interactive globe per country with top languages sidebar, JSON-LD, and sitemap entries/devs hub redesigned as tabs ("By country" default, "By language" second), with count badgessrc/lib/devs-display.ts extracted for display constants/[owner]/[repo]/contributors — direct URL/repos community table (links to the page, shows count if already indexed)rtk-ai/rtk)src/lib/db.ts: replaced bare require() with createRequire(import.meta.url), fixing ReferenceError: require is not defined in ESM packages when DATABASE_DRIVER=standardMakefile batch-index-contributors targets: changed bare tsx to node_modules/.bin/tsx (matches auto-index pattern, fixes PATH error when invoked via make)/repos table: fixed Deps/Contributors column order mismatch between <thead> and <tbody>BANNER_ID to announce-contributors-v1github_user (to-neon): topRepos, topReposFetchedAt, and source were missing from the explicit column list. Syncing to prod silently dropped those values for every user row.badge_cache (to-neon): contributorsCount was absent from the ON CONFLICT DO UPDATE SET clause. Data written locally by backfill-contributors.ts was never propagated to prod.badge_cache (from-neon): the conflict clause only updated three fields (mappedCount, countryCount, totalCount). All other fields (organicScore, contributorsCount, the release fields, etc.) were silently discarded on every reverse sync.follower_cache and dependents_cache were completely absent from db-sync-to-neon.sh. Both tables are now included with staleness-guard WHERE EXCLUDED.x > table.x conflict clauses.follower_cache and dependents_cache were missing from the default TABLES list. Running pnpm db:sync:from-neon would silently skip both tables. Both are now included with correct column lists and ON CONFLICT upsert clauses.resolvePackages(), fetchDependentPages(), fetchDependents(), sortDependents(). Pure data layer, no framework coupling.generateMetadata, canonical URL, OG tags.GET, cache-first read with sort, page, per_page query params. 5-min CDN cache.POST, live fetch + cache upsert, 1h cooldown.badge_cache with dependents data. Flags: --dry-run, --force, --limit, --delay-ms, --min-stars. Run: pnpm backfill:dependents:prod.GET /api/users/autocomplete?q= proxies GitHub search/users, returns { login, name, avatarUrl }[], 60 s CDN cachebadge_cache values plus a real-time zero-follower query. Public. Cache-Control: public, s-maxage=300, stale-while-revalidate=600.FollowersPanel side panel with virtual scroll, fly-to on marker click, and a summary badge (mapped / total)./profile/[login] is now a link to the followers map. A "Map followers" action button appears in the profile actions row.FollowerPoint[] + unmapped[]. Distributed rate limiting via Upstash (30 req/min per IP, 300 req/h per PAT). IP rate limiting is skipped in development./api/followers-chunk loop sequentially, accumulates points progressively, and surfaces quota remaining.make index-followers LOGIN=owner).fetchFollowersPage + geocodeBatch directly, no HTTP server required. Multi-token rotation reads GITHUB_TOKEN, GITHUB_TOKEN_2… and parks exhausted tokens on GitHub 429 instead of waiting./api/chunk loop for any repo to pre-warm its geocache. Supports --base-url for local dev.explore city search, login/name search, profile repo search, [owner] repo search and min-stars filter, map top-panel username input) were missing accessible names and now carry explicit aria-label attributes. The min-stars <label> is now programmatically linked via htmlFor/id. The StarNudge popup (role: dialog) gains keyboard dismissal via Escape. The unmapped-stargazers bottom drawer gains role="region", aria-label, and Escape-to-close.text-muted-subtle token in dark mode bumped from #848d97 (4.2:1 ratio — below threshold) to #8b929a (~4.55:1). Affects secondary labels, breadcrumb separators, and stat hints across all pages./trending now has a dedicated layout.tsx with title, description, OG, Twitter card, and canonical. /[owner] (user scan page) has generateMetadata producing a dynamic title {owner}'s repos | StarMapper and a canonical URL./privacy, /terms, /legal, /changelog, /sponsor, and /organic-score/calibration now carry full social metadata alongside their existing title and description.layout.tsx now applies .replace(/</g, "\\u003c") on the JSON-LD payload, consistent with the per-repo and profile layouts.<Image> with loading="lazy", enabling automatic AVIF/WebP delivery via the optimizer.[owner]/[repo], /explore, and /profile/[login] were monolithic "use client" pages with no server-side rendering. Each now has a Server Component wrapper that pre-fetches the critical-path data (repo info, explore summary, profile) and passes it as initialData to the client component. The client-side fetch becomes a fallback (private repo, 404, network error) instead of the default path. LCP improves for all three routes; crawlers and social preview bots see real HTML content on first byte.MAP_STYLE_DARK, MAP_STYLE_LIGHT, Theme, and MapProjection moved to a server-safe module with no "use client" marker. theme.ts re-exports them for backward compatibility. Prevents accidental bundle pollution if a server component ever imports map URL builders.sanitizeError from @/lib/api-helpers (added in 0.5.6) but the Vitest mock factory did not expose it. The function was added to the mock; the 1 failing test now passes (895/895)."use client" removed from src/components/map/jawg-badge.tsx. The component is a static <a> tag with no hooks or browser APIs; the directive served no purpose.findUnique calls without select now fetch only the columns actually consumed: badge-update fetches totalCount only (was all 15+ columns), map-image fetches 4 badge columns, stargazer-cache GET fetches updatedAt for the fallback path and explicit 5-column select for the full row. Reduces data transferred from Neon on every scan completion and every map image generation.badge_cache rows into Node.js memory for in-memory aggregation. Replaced with two parallel $queryRaw GROUP BY queries (per-tier counts + per-bucket distribution via width_bucket). Memory footprint is now O(1) regardless of table size.prisma.geoCache.upsert per row inside a nested loop) replaced with a single $queryRaw UNNEST INSERT ... ON CONFLICT DO UPDATE per batch of 500. Same pattern as bulkUpsertUsers in user-cache.ts. Admin dev-only route, blocked in production.delete-user route wraps starEvent.deleteMany + gitHubUser.delete in prisma.$transaction([...]). Prevents partial deletion if the process crashes between the two operations.max-w-lg. Browser-native password reveal/autofill icons suppressed via CSS.localStorage but the implementation uses sessionStorage with a 30-minute TTL.map variable from initMap which could be destroyed after unmount or unset before layers were added. Switched to mapRef.current + hasInitializedRef guard to prevent null-pointer errors on fast navigation.backfill-organic-score now accepts --force to recompute already-scored rows, useful after signal weight changes or calibration updates. db-sync-to-neon expands the badge_cache upsert to preserve nullable columns during sync.robots.ts adds Bingbot, msnbot, Google-Extended explicitly. layout.tsx SoftwareApplication description rewritten value-first; dateModified added; speakable cssSelector extended. FAQ gains 5 new Q&As targeting high-GEO queries (fake star detection, Watch Mode, Geographic Velocity, influential stargazers, multi-repo compare). </script> injection escaping (<) applied consistently to all inline JSON-LD scripts. Sitemap adds /organic-score/calibration and /sponsor.badge-update runs plausibility and organic score checks in parallel. /devs count and top-countries queries use language materialized views. Nearby users and city queries use Promise.all. badge-update city scan timeout guard added.recordSuccess() added, Jawg/Geoapify HTTP status logged on error for faster diagnosis. Sitemap response cached for 1 hour.vhigh (1k+, red dot) and elite (5k+, purple dot). The 500+ tier becomes orange. Applied consistently across the filter dock, timelapse, and share modal URL state.indexedBy column on StargazerCache stores the resolved login server-side. useScanController forwards the stored token as x-gh-token; the cache route resolves it via GET /user with a 3-second timeout and stores the result silently. pnpm stats:views gained a scan history panel showing the last 5 scans per repo with date, star count, and attribution.localStorage), does not interrupt any flow.fetchTrendingMap removed from use cache scope after producing a 24 MB ISR fallback that exceeded Vercel's 19 MB limit. Endpoint switched to force-dynamic, result set capped at 30k points.<Suspense> so the outer layout shell can prerender statically. Route segment export const dynamic configs removed from pages already covered by the global cacheComponents: true setting.src/middleware.ts renamed to src/proxy.ts (Next.js 16 reserves middleware.ts for Vercel Routing Middleware; the StarMapper request proxy now lives under its correct name).use cache migration resolved.cacheComponents: true enabled in next.config.ts. All data-fetching pages migrated from export const revalidate = N (time-based) to 'use cache' + cacheTag + cacheLife (tag-based, on-demand). Cache invalidation is now surgical: POST /api/badge-update invalidates only the affected repo, the cron MV refresh invalidates trending and explore-mvs, POST /api/news invalidates the author's feed. Three shared query libs extracted (repos-query.ts, trending-query.ts, devs-query.ts) so pages and API routes share the same cached functions./, /repos, /trending, /devs/atlas, /devs) were making HTTP fetch requests to their own API routes (e.g., fetch("http://localhost:3000/api/trending/repos")). All five now call the DB lib directly, eliminating the unnecessary loopback latency./repos, /trending, /devs, /devs/atlas migrated from useEffect waterfall fetch to async server components with initialData prop. Data is available on first paint with no client-side loading state./about, /privacy, /terms, /oss, etc.) marked force-static so they are prerendered at build time and served from the CDN edge with no runtime cost.StatsModal, ShareModal, AllStargazersModal, GrowthModal, BadgeModal, RateLimitedModal, RepoNotFoundModal are now dynamic() imports. Reduces initial JS bundle parsed on page load.t = zA/(zA-zB)) and used as the clip point. Continents now fade out gradually at the globe edge. Fast paths retained for rings fully in front or fully behind.src/lib/resolve-base-url.ts. Removed in subsequent refactor once the self-call pattern was eliminated.DB_STORAGE_LIMIT_MB env var removed; hardcoded to 100 GB to match Neon sponsored plan. Removes an unnecessary configuration surface.repos route mock aligned to $queryRaw (was badgeCache.findMany). Ratelimit stub fixed for TS2556 spread unknown[].src/env.ts added via @t3-oss/env-nextjs. Build fails at compile time and server startup if DATABASE_URL, GITHUB_TOKEN, or NEXT_PUBLIC_JAWGMAP_ACCESS_TOKEN are missing. Prevents silent misconfiguration on new deployments (closes #5).GET /api/trending/repos and GET /api/trending/map replace the monolithic GET /api/trending. The repos list now renders before the map because the two fetches are independent. Map endpoint decompresses the top 5 repos (was 10), halving CPU work per request. /trending loading skeleton added via loading.tsx. Legacy route kept as alias for one cycle.style-src 'unsafe-inline' replaced with CSP Level 3 split: style-src-elem 'self' 'nonce-{nonce}' (blocks <style> injection) and style-src-attr 'unsafe-inline' (scoped to element attributes only, required by React dynamic styles and MapLibre controls). Closes #56.ci.yml, audit.yml, semgrep.yml) now reference actions by full commit hash instead of mutable version tags. Eliminates supply-chain risk from tag mutation. Closes #55.link-check.yml workflow runs lychee monthly against starmapper.bruniaux.com, catching dead links and 404 regressions automatically.src/app/[owner]/[repo]/page.tsx refactored across 12 commits. Extracted components: StatsModal, ShareModal, AllStargazersModal, GrowthModal, BadgeModal, RateLimitOverlay, PreScanOverlay, RateLimitedModal, RepoNotFoundModal, GrowthChart. Extracted hooks: useScanController, useRepoCacheLoader, useCompareScan, useWatchMode, useTimelapse. Each extraction ships with its own unit tests.#faf6ed background, orange accent) across all light-mode CSS tokens in globals.css. Hero globe adapts to the active theme.StargazerMap, CountryChoropleth, and LanguageChoropleth now render a fallback message instead of crashing when WebGL is unavailable (headless environments, some enterprise proxies).AbortController added to 7 async fetch effects in the map page (repo-info, stats, organic-score, compare-info, growth-data, geo-velocity, stargazer-cache check). Prevents state updates on an unmounted component when the user navigates mid-scan. useCompareScan threads AbortSignal into each /api/chunk fetch. Closes #47./profile/[login]) columns stack vertically on mobile (flex-col lg:flex-row). Action buttons (GitHub, Refresh, LinkedIn, Contact) are icon-only on mobile. Contact dropdown becomes a bottom-sheet. New reusable src/components/ui/tabs.tsx component.snap-x snap-mandatory).opengraph-image.tsx are now caught and rendered as a text fallback instead of silently failing. Closes #49.POST /api/vitals logs structured JSON instead of a raw string; React list-key instability in Explore fixed. Closes #57, #58.package.json now runs prisma generate on every pnpm install. Fixes the case where pnpm reorganizes the virtual store and wipes the generated .prisma/client artifacts.ThemeToggle and TokenModal. 19 new tests for trending endpoints. 16 smoke tests for extracted map components. 8 tests for useRepoCacheLoader, 4 for useCompareScan. Total: 856 → 872 tests.LocalCache helpers (loadCache, saveCache, clearCache, cacheKey) centralized from inline page.tsx definitions into a shared lib. Two sequential useEffect that both called loadCache merged into one, removing a duplicate localStorage read on every page load.prisma migrate diff --from-empty. Combined with prisma/sql/views.sql, gives contributors a complete DB picture without enabling migration history. Closes #53.MASTER.md, 209 lines) removed; globals.css documented as the single source of truth for tokens. Closes #54.react-hooks/exhaustive-deps flipped from off to warn. 5 pre-existing intentional violations suppressed with inline comments explaining the rationale. Closes #48.@upstash/redis, eslint-plugin-react-hooks, @types/node, tsx bumped..claude/rules/ files ported/adapted from methode-aristote: response-discipline, git-merge-discipline, react-performance-optimization, react-timers-cleanup, typescript-zero-errors, session-management, scripts-best-practices, file-organization, universal-rules, known-gotchas. CLAUDE.md slimmed from ~800 to 146 lines./faq dedicated page replaces the inline FAQ section; a compact teaser remains on the landing with a "See all" link.fetchAndPatchStyle auto-switches to NEXT_PUBLIC_JAWGMAP_ACCESS_TOKEN_2 when the primary token returns 401/402/403/429 (Map Views limit). Transparent to users, no reload required.src/lib/scan-cache.ts added (preparatory, not yet wired into the chunk loop). Persists scan results between page reloads for returning visitors./trending added to sitemap and navigation./vs/star-history with structured data and UTM tracking on outbound badge/embed links.description, og:*, twitter:* fixed on all pages.$executeRawUnsafe replaced with $executeRaw + Prisma.sql on all MV refresh paths. Admin endpoints return 404 (not 401/403) on auth failure to avoid endpoint discovery.useEffect + useState fetch with useSWR; stale-while-revalidate reduces perceived latency.stargazer_cache SELECT restricted to points only (was fetching the full row including unmapped).ILIKE search on login replaced with an IN clause to avoid a full table scan.max-w-7xl across all pages.FlorianBruniaux (camelCase) used consistently in profile and badge URLs.github_user has multiple casing variants for the same login, the record with the most data is selected instead of throwing.pnpm/action-setup reads packageManager from package.json instead of hardcoded version. Semgrep false positives on JSON-LD suppressed.graphify-out/ untracked (328 files, 5.8MB of generated cache removed from git history going forward). .gitignore extended with .pnpm-store/, .codex/, .code-review-graph/.star_event.starredAt via GET /api/stats/[owner]/[repo]/growth (SQL DATE_TRUNC('week'), 5-min CDN cache). Falls back to in-memory starredAt timestamps for repos scanned in the current session. Button is now visible for any repo with scan data, not just scans that captured timestamps in memory./api/repos?diverse=true mode: fetches a 500-row pool and filters to max 3 repos per owner + min 100 stars before returning results. Prevents a single active user from filling the entire grid.github.com/[login]), a "★ StarMapper" button is injected in the user sidebar that opens starmapper.bruniaux.com/profile/[login]. Content script matches extended to ["https://github.com/*", "https://github.com/*/*"] to cover single-segment paths. getPageContext() discriminated union (repo | profile | other) dispatches the correct button per page type. Profile button is full-width to match GitHub's sidebar style; injection targets .js-profile-editable-area then Layout-sidebar with a 2s floating fallback./owner/repo page, opening the StarMapper map directly. Toolbar popup with the current repo + last 5 visited repos + search by slug or URL. Context menu on right-click for GitHub links. Handles GitHub SPA navigation (Turbo + bfcache) via MutationObserver. Dark/light compatible via GitHub CSS variables.@crxjs/vite-plugin v2 (stagnant beta) with WXT v0.20. entrypoints/ structure (background.ts, content.ts, popup/), build → .output/chrome-mv3/, wxt zip for Chrome Web Store. Standalone tsconfig.json (without extends: .wxt/tsconfig.json) to avoid the Vite circular reference bug.MutationObserver replaces setTimeout(120ms) for button injection; pageshow handler + e.persisted for bfcache; recent repos saved to chrome.storage.local on click; SYSTEM_OWNERS blocklist in context menu (filters /settings, /explore, etc.); icons in public/icons/ for WXT serving.README.md (Chrome Extension), PITCH.md + PITCH-en.md (Organic Score: 4 signals → 3, correct weights fork=40%/watcher=5%/zero-followers=55%; Watch mode, Geo velocity, Notable stargazers, Chrome Extension added), docs/ARCHITECTURE.md (version 0.4.6, Neon 100GB sponsored, +15 missing API routes, full file structure with schemas/ and extension/), PROJECT_INDEX.md + llms.txt (extension/ section), docs/organic-score-calibration.md (Final Decision corrected: fork=40%, non-fork=70%).extension/ excluded from root compilation (WXT config is standalone in extension/tsconfig.json).+N ★ · India, Germany in real time. Stops automatically after 10 min with no new star. Endpoint GET /api/watch/[owner]/[repo]?since=<ISO>: GitHub REST + countryNormalized lookup from github_user (no Nominatim calls). No DB writes, Cache-Control: no-store.rising (×1.5+), new (no history), stable, declining (≤0.5). Lazy loading: the request only fires when the tab is opened, once per session. Endpoint GET /api/stats/[owner]/[repo]/geo-velocity, SQL query with COUNT(*) FILTER, 5-min CDN cache.+N/mo in green under the star count, computed from starredAt already in memory after a scan. Only appears when the data is present (recent scans with timestamps); silently absent for old caches.defineRoute(schema, handler) wrapper. New src/schemas/ directory holds typed Zod v4 schemas for each route (track, vitals, recalculate-location, badge-update, chunk, news, stargazer-cache). Per-field error codes are declared directly in schemas; defineRoute surfaces issues[0].message verbatim so every existing error contract is preserved unchanged. Manual typeof / regex validation chains removed from all route handlers. getIP exported from api-helpers to replace a duplicate helper in the chunk route./profile/[login] now shows the cached top repos grid (up to 8, from topRepos in DB). Count badge reflects the real publicRepos value from GitHub, not the cached repo list length.@ (e.g. @ruvnet) now works the same as without. The prefix is stripped before debouncing to the search state.topReposFetchedAt is reset so the next profile load re-fetches top repos from GitHub instead of serving the outdated cache.CHANGELOG.md at build time. Server Component with inline bold+code rendering without an external markdown dependency. Link added in the footer and in the announcement banner.user_repo_count_mv over 12k rows via Neon was exceeding the 10s statement timeout. Fixed by pushing the lat IS NOT NULL filter before the JOIN and capping candidates to 500 before enrichment.w-96 with more padding to prevent RSS/JSON URL wrapping./feed/[login], the dropdown was redundant (URLs already displayed). minimal prop added: direct toggle without dropdown.NewsTimeline component integrated on /profile/[login] with skeleton loader, conditional "Publish" button (visible only if the stored token matches the page login).GET /api/feed/[login]/rss (RSS 2.0 with <atom:link>, If-Modified-Since, 304 response) and GET /api/feed/[login]/json (JSON Feed 1.1). Cached 1h CDN. Subscribe link (RSS icon + "Subscribe") displayed at the top of the News section on the profile./api/feed/[login]/rss is recorded in page_view (type "feed_rss", slug = login). Queryable via pnpm stats:views.unsafe-inline in CSP./profile/[login]), getStoredUsername() returned "", which set isOwner to false and hid the "Publish" button even for the profile owner. handleSave now resolves the login via GET /api.github.com/user and stores it. "Verifying…" shown during verification; handleRemove also clears the username.buildRss20() (RSS 2.0 XML, correct CDATA, ]]> split into two CDATA sections) and buildJsonFeed() (JSON Feed 1.1 object). Build logic decoupled from routes for testability.github-auth.ts, reused by all news and feed routes.BANNER_ID; bump the ID to make it reappear for the next announcement. Home header switched to sticky to stack naturally below the banner.PostToolUse hook that detects the creation of a new page.tsx or route.ts and reminds you to update AnnouncementBanner.opacity-0) for users *with* coordinates, and visible (grey) for those *without*. Inverted: button always visible and clickable for geolocated users, invisible (space preserved) for others.country_stats_mv was created empty (no countryNormalized at creation time), then never refreshed after the backfill. Added create:country-stats-mv / create:country-stats-mv:prod commands to create and refresh the MV.repo-metrics + repo-languages). Commands: update:prod, update:local, update:local:force.DATABASE_DRIVER=standard.OrganicScorePill fetched independently from the rest of the page.openIssuesCount field). Organic score modal displays both badges separately and as clickable links (GitHub links).stats/[owner]/[repo] was throwing a Neon timeout on large tables. Graceful fallback: returns the partial data available without crashing.backfill-repo-metrics.ts was using DATABASE_URL_LOCAL instead of DATABASE_URL for :prod commands. Fixed + NEXT_PUBLIC_ORGANIC_SCORE_ENABLED=true forced.docs/organic-score.md: StarScout vs StarMapper comparison, normalization formula, known limitations./profile/[login]: two-column layout (scrollable panel 2/3 + sticky map 1/3). Data: bio, followers, repos, languages, tracked star events, contribution by country. Partial profile if user is absent from DB (refresh triggered automatically).explore/top, explore/power, explore/nearby. "View StarMapper profile →" button in the stargazer popup.POST /api/track triggered on load; daily view counter per profile in page_view.GET /api/geo/[owner]/[repo]: aggregated endpoint returning GeoJSON points from a scan, protected by HMAC API key. Usable by third-party tools.star_event.starredAt.startTransition around chunk loop dispatches, useDeferredValue on the stargazers filter, useCallback on map handlers, gating of expensive memos, lazy-load TokenModal + SponsorsBlock, width/height on avatars (CLS).stargazer-cache, optimized CDN TTL, redundant login index removed.setData window to avoid blocked frames..gitignore.db:sync:from-neon: --repo, --limit, --tables variants for partial sync. SET statement_timeout=0 added at the top of all DDL scripts (indexes + MVs). Prisma slow query logger enabled.user_repo_count_mv (per-user repo count, nearby query 6s→200ms). GIN trigram index on login + name (ILIKE search 6s→50ms).callJawg() in geocoder.ts was sending the token only via the x-api-key header. Added the access-token query param required by the dedicated starmapper.jawg.io endpoint. Without this param, Jawg requests silently returned 401./api/explore/geocode was manually reconstructing the label using p.city (a field that does not exist in the Jawg Places model). Replaced with feature.properties.label, which Jawg provides natively.geocoder.test.ts was stubbing JAWGMAP_ACCESS_TOKEN while the code reads JAWG_TOKEN_HEADER. 7 pre-existing tests were silently failing. Fixed.README.md and docs/ARCHITECTURE.md. Jawg Places is based on Pelias but the correct brand name is Jawg Places.stargazer-map.tsx (30 lines, no cache) and a version in lib/map-style.ts (with cache). lib/map-style.ts is now the single source of truth. The function accepts a projection parameter ("mercator" | "globe", default "mercator") with a composite cache key ${url}#${projection} to avoid globe/mercator collisions.lang=en removed from style URLs in theme.ts and stargazer-map.tsx (Jawg handles language natively). Glyphs URL patch removed (lib/map-style.ts, stargazer-map.tsx). name:fr → name:en replacement removed (map-style.ts, stargazer-map.tsx).api.jawg.io to starmapper.jawg.io (dedicated StarMapper endpoint). Token migrated from JAWGMAP_ACCESS_TOKEN to JAWG_TOKEN_HEADER. Added x-api-key header.scripts/ now use node:util parseArgs with strict: true instead of ad-hoc process.argv.includes / getArg patterns. parseArgs is native Node 18+, no dependency./devs/atlas page: world choropleth map showing the most popular language per country, computed from starred and contributed repos. Country detail on click (dominant language, %, number of devs). "Early preview" banner while the backfill runs./devs and /devs/[language] pages: developer map filtered by language, with a selection combobox.backfill-languages.ts script to populate the languages[] field on github_user. --from-cache mode: derives languages from star_event + badge_cache with no GitHub API call (1.23M users in seconds). API mode: parallelizable via --token-index.pnpm db:sync and the daily admin cron.maxRepositories: 30 → 10 (fewer GraphQL points consumed), default batch 10 → 50, bulk UPDATE via unnest() (1 SQL query instead of N individual ones).github_user was going from DO NOTHING to DO UPDATE: the languages and languagesFetchedAt columns were never being pushed to Neon. Fixed.country_language_stats_mv on Neon during sync if it does not yet exist.src/lib/language-colors.ts.ssr: false).innerHTML to DOM API construction.unsafe-eval removed in production. HSTS added.max-w-7xl.POST /api/stargazer-cache. Fixes silent cache loss on repos with >~15k stars (raw payload ~15MB > Vercel 4.5MB limit). Payload reduced to ~800KB.[object Object], XSS artifacts, Jinja templates).db.ts: DATABASE_DRIVER=standard → @prisma/adapter-pg (Docker, Railway, Supabase); default → @prisma/adapter-neon. Self-hosting without Neon works.take: 10_000), TTL-aware health guard.worker-src blob: (was blocking the MapLibre web worker → blank map on some configs).localStorage access during SSR render caused React error #418. Fix: state initialized SSR-safe, synced via useEffect.cacheCheckDone state.isGeocodeableLocation filter extended to prefixes #$<>[{"!.api-validation.ts, api-helpers.ts, compression.ts, compress-client.ts. Replaced 10 duplicated patterns across 15 route handlers.interface → type. import type for type-only imports.batch-scan.ts: incremental FLUSH_EVERY writes, session-level geocoding cache, better error recovery./api/badge/[owner]/[repo] — shield with mapped count and country count, 6h CDN cache.robots.txt, sitemap.xml, structured FAQ, Open Graph metadata./explore listing mapped repos with stats.stargazer_cache table) — instant reload for subsequent visitors on the same repo. Limit: 100k stars.lat=null/lng=null — avoids repeated API calls for the same garbage input.isGeocodeableLocation() filters TLDs, phone prefixes, URLs, placeholder values before any API call.POST /api/chunk calls (100 users/call) to stay under the Vercel 10s timeout.geocache Neon table shared across all repos — a location geocoded once benefits all future scans.github_user + star_event tables to track users and their repos at the user level.Follows Semantic Versioning, format inspired by Keep a Changelog.