Skip to content

feat: Add GitHub API Rate Limit Handling for Repository Statistics#1944

Open
Doremon-tech-svg wants to merge 1 commit into
S3DFX-CYBER:mainfrom
Doremon-tech-svg:feat/github-api-rate-limit-handling
Open

feat: Add GitHub API Rate Limit Handling for Repository Statistics#1944
Doremon-tech-svg wants to merge 1 commit into
S3DFX-CYBER:mainfrom
Doremon-tech-svg:feat/github-api-rate-limit-handling

Conversation

@Doremon-tech-svg

@Doremon-tech-svg Doremon-tech-svg commented Jun 23, 2026

Copy link
Copy Markdown

feat: Add GitHub API Rate Limit Handling for Repository Statistics


Contribution Program

  • GSSoC
  • NSoC
  • General Contribution

program: gssoc


Related Issue

Closes #1702


Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor
  • Performance improvement
  • Other

Problem

The app fetched GitHub stats on every request with no protection against rate limiting. When the API quota ran out, all four modes (repo stats, GFI count, issue list, user profile) returned errors with no fallback — breaking the UI entirely for users.

A secondary bug existed in githubAnalyzer.js: GitHub 403 messages were matched as rate limits, which caused auth/permission errors to silently serve stale cache instead of surfacing the real problem.


Solution

Added a stale-cache fallback strategy at both layers:

  • Edge layer (api/github.js) — detects rate limiting via isRateLimited() and returns the last cached value with rateLimit: true rather than failing
  • Client layer (src/js/githubAnalyzer.js) — reads stale localStorage on rate limit, and correctly separates auth 403s from rate limit 429s
  • Tests (tests/github-ratelimit.test.js) — imports the real exported helpers via CJS shims instead of re-implementing them locally

Changes Made

api/github.js

  • isRateLimited(res) — 429 is always a rate limit; 403 only when x-ratelimit-remaining: 0
  • getStale(key) — retrieves cache entry regardless of TTL for fallback use
  • staleResponse(data, headers) — wraps stale payload with { cached: true, rateLimit: true, staleSeconds }
  • Fallback applied across all 4 modes: ?user=, ?gfi=1&issues=1, ?gfi=1, standard repo stats

src/js/githubAnalyzer.js

  • getStaleLocalCache() — reads localStorage ignoring TTL
  • Stale fallback in analyzeGitHubUser() for both data.rateLimit === true and thrown 429 errors
  • GitHub 403 moved to the auth error branch in handleAnalyzerError() — not treated as rate limit

tests/github-ratelimit.test.js (new)

  • Imports isRateLimited, staleResponse, safeCacheSet from tests/helpers/github-test-shim.js
  • Imports getStaleLocalCache from tests/helpers/analyzer-test-shim.js
  • Tests validate the actual shipped code, not local re-implementations

tests/helpers/github-test-shim.js (new) — CJS shim for edge function helpers
tests/helpers/analyzer-test-shim.js (new) — injectable storage shim for getStaleLocalCache


Test Results

✔ isRateLimited: 429 is always a rate limit
✔ isRateLimited: 403 with x-ratelimit-remaining=0 is a rate limit
✔ isRateLimited: 403 without x-ratelimit-remaining is an auth error, not a rate limit
✔ isRateLimited: 403 with remaining > 0 is an auth error, not a rate limit
✔ isRateLimited: 200 OK is never a rate limit
✔ staleResponse: returns a Response with status 200
✔ staleResponse: body has rateLimit=true, cached=true, no ts field
✔ safeCacheSet: evicts the oldest entry when cache is at capacity
✔ getStaleLocalCache: returns data for an existing user ignoring TTL
✔ getStaleLocalCache: returns null for a user not in cache
✔ analyzeGitHubUser: GitHub 403 message triggers auth error, not stale cache
✔ analyzeGitHubUser: rate limit (429) serves stale cache when available

tests 12 | pass 12 | fail 0

Checklist

  • Issue is assigned to me
  • PR links an issue (Closes #1702)
  • Changes are focused and minimal
  • No unnecessary dependencies added
  • Code follows repository architecture (Vanilla JS, Edge-compatible, zero-build)
  • I tested changes locally (node --test tests/github-ratelimit.test.js)
  • I understand the code submitted
  • DCO sign-off included (git commit -s)

@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@Doremon-tech-svg is attempting to deploy a commit to the s3dfx-cyber's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

👋 Thanks for opening a PR, @Doremon-tech-svg!

Your PR has entered the 🚦 PR Review Pipeline.

Standard PR detected — your PR will follow the standard repository review pipeline.


🔄 Review Flow

Stage Reviewer Purpose
Stage 1 🤖 Automation Validation · Duplicate Detection · AI/Slop Checks · Formatting · PR Analysis
Stage 2 👥 Repository Reviewer Code Review · Scope Validation · Quality Check
Stage 3 🔑 Project Admin / Maintainer Final Approval & Merge Decision

The automated PR analysis system will verify issue linkage, PR relevance, and contribution quality.

A pipeline status comment may appear automatically as your PR progresses.


✅ Contributor Checklist

  • Sign commits using git commit -s
  • Link a valid issue (Closes #123)
  • Keep changes focused and relevant
  • Do not include unrelated modifications
  • Ensure workflows/build/tests are passing
  • Read the appropriate contributor guide:

⚠️ Important Notes

  • Low-quality, spammy, or AI-generated PRs may be closed
  • PRs without linked issues may fail automated checks
  • Large unrelated PRs are likely to be rejected
  • Review times may vary depending on mentor/reviewer availability

Happy contributing 🚀

This message is posted automatically and only once.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Validation Issues

Hi @Doremon-tech-svg, your PR requires fixes before review.

Warnings

  • ⚠️ Missing PR template section: description

Please push fixes after updating the PR.

@github-actions github-actions Bot added size/l enhancement New feature or request gssoc26 GirlScript Summer of Code 2026 labels Jun 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

💬 Faster Reviews & Assignments

Hi @Doremon-tech-svg, for faster coordination and smoother communication, consider joining our Discord community:

👉 https://discord.gg/MmZGG2ee

Useful Channels

  • #issue-links-for-assignment → Share issue links for assignment help
  • #pr-links-for-review → Share PR links for mentor/maintainer review

Please avoid spamming channels or repeatedly pinging mentors/maintainers.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

✅ DCO Sign-off Verified

Hi @Doremon-tech-svg 👋

All commits in this PR contain valid Signed-off-by lines.

Thank you for following the DCO requirements 🚀

@github-actions github-actions Bot removed the gssoc26 GirlScript Summer of Code 2026 label Jun 23, 2026
@github-project-automation github-project-automation Bot moved this to Todo in GSSOC 26 Jun 23, 2026
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

⚠️ Invalid Program Classification

Multiple contribution programs were detected in this PR.

Please keep ONLY ONE classification:

  • GSSOC
  • NSOC
  • General Contribution

Detected:

  • gssoc
  • nsoc

After editing your PR body, validation will automatically re-run.

Stage-2 review routing is temporarily paused until this is fixed.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Doremon-tech-svg, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 37 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5ae736ec-b68c-489a-8073-8fdb1014e4ad

📥 Commits

Reviewing files that changed from the base of the PR and between 52eba7e and 651cf56.

📒 Files selected for processing (5)
  • api/github.js
  • src/js/githubAnalyzer.js
  • tests/github-ratelimit.test.js
  • tests/helpers/analyzer-test-shim.js
  • tests/helpers/github-test-shim.js
📝 Walkthrough

Walkthrough

Adds GitHub API rate-limit detection and stale-cache fallback to the Edge Function (api/github.js) across all four request modes (?user=, ?gfi=1&issues=1, ?gfi=1, and standard repo-stats). Mirrors the same pattern in the client-side githubAnalyzer.js using localStorage. Introduces a new test file with shims, mock factories, and eight focused unit tests.

Changes

GitHub Rate Limit Stale-Cache Handling

Layer / File(s) Summary
Edge Function rate-limit utility helpers
api/github.js
Adds three shared helpers: getStale (TTL-ignoring cache read), isRateLimited (detects 429 and 403+x-ratelimit-remaining: 0), and staleResponse (wraps cached payload with cached, rateLimit, and staleSeconds fields).
Edge Function mode-specific rate-limit handling
api/github.js
Wires isRateLimited into all four handler modes. Each mode now attempts to serve stale cached data on rate limiting; falls back to a rate-limit error payload when no stale entry exists. Commits fetch sets lastCommit to '—' on rate limit without failing the overall request. Partial pagination results are tolerated on pages 2–3.
Client-side analyzer stale cache and error handling
src/js/githubAnalyzer.js
Adds getStaleLocalCache (TTL-ignoring localStorage read). handleAnalyzerError now immediately rethrows AbortError and widens rate-limit message matching. analyzeGitHubUser returns stale localStorage data with stale: true when data.rateLimit is set or a rate-limit/429 error is caught in the catch block.
Rate-limit unit tests
tests/github-ratelimit.test.js
New test file with MockResponse/MockHeaders shims, mock response factories, and eight tests covering isRateLimited (four cases), staleResponse field mapping, safeCacheSet FIFO eviction, and three githubAnalyzer scenarios (stale-cache hit, no-cache error, and fresh-cache short-circuit).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐇 When the API says "no more today,"
I dig through my burrow for yesterday's hay.
staleSeconds counted, rateLimit: true set,
The freshest stale data you're ever gonna get!
With 429s caught and the cache falling back,
This rabbit keeps serving — right on track. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding GitHub API rate limit handling for repository statistics, which is the primary focus of the changeset.
Linked Issues check ✅ Passed The PR fully addresses issue #1702's objectives: adds a caching layer with rate limit detection, implements stale-cache fallback across all API modes, maintains periodic refresh via existing TTL, and provides error handling for rate-limited scenarios.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing rate limit handling and stale-cache fallback at server and client layers, with corresponding test coverage; no unrelated modifications detected.
Description check ✅ Passed The pull request description is directly related to the changeset and provides clear context about the GitHub API rate limit handling implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

⚠️ Suspicious Generated Code Detected

This PR contains patterns commonly associated with low-quality AI-generated submissions.

Detected Signals

  • Excessive comments in api/github.js
  • Multiple helper functions added in api/github.js
  • Excessive comments in src/js/githubAnalyzer.js
  • Multiple helper functions added in src/js/githubAnalyzer.js
  • Large insertion in tests/github-ratelimit.test.js (+125)
  • Multiple helper functions added in tests/helpers/github-test-shim.js

A maintainer or mentor should manually review this PR before merge.

This is not an automatic rejection.

@github-actions github-actions Bot added gssoc26 GirlScript Summer of Code 2026 and removed gssoc26 GirlScript Summer of Code 2026 labels Jun 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
api/github.js (1)

315-318: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant lastCommit assignment.

lastCommit is already initialized to '—' at line 298 and is unchanged on this path, so the else if branch is a no-op assignment (matches the SonarCloud S3DFX warning). It can be reduced to a comment if you want to keep the intent documented, otherwise drop the branch.

♻️ Optional simplification
-    } else if (isRateLimited(commitsRes)) {
-      // Commits rate-limited but repo data is fine — use repo data, mark commit as unknown
-      lastCommit = '—';
-    }
+    }
+    // If the commits request was rate-limited, lastCommit stays '—' (set above)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/github.js` around lines 315 - 318, The else if branch checking
isRateLimited(commitsRes) contains a redundant assignment to lastCommit since it
is already initialized to '—' at line 298 and this branch does not change that
value. Remove the else if block entirely, or if you want to keep the intent
documented, replace the assignment with just a comment explaining that commits
are rate-limited but repo data is fine, then remove the lastCommit = '—' line.

Source: Linters/SAST tools

tests/github-ratelimit.test.js (1)

9-42: 📐 Maintainability & Code Quality | 🔵 Trivial

Remove unused test scaffolding: MockResponse class and makeOkRes function.

MockResponse (lines 9-18) and makeOkRes (lines 39-42) are not referenced anywhere in this test file. The tests either create mock responses inline or use makeRateLimitRes() and make429Res() helpers instead. Removing them will simplify the test setup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/github-ratelimit.test.js` around lines 9 - 42, Remove the unused
MockResponse class and the makeOkRes function from the test scaffolding section.
The MockResponse class (which includes the constructor with body and init
parameters, the json and text methods) is not referenced anywhere in the test
file, and the makeOkRes function that creates mock responses with status 200 is
also unused since the tests either create mock responses inline or use the
makeRateLimitRes and make429Res helpers instead. Delete both of these to
simplify the test setup while keeping the actively used helper functions intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/js/githubAnalyzer.js`:
- Around line 167-170: The rate-limit detection in the isRateLimit condition is
incorrectly matching "GitHub 403" as a rate limit error, but the Edge Function
already converts actual rate-limited 403s into 429 responses, so a "GitHub 403"
message indicates an authentication or permission error instead. Remove the
'GitHub 403' check from the isRateLimit condition (keeping only 'rate limit' and
'429' checks), and add a separate handling branch in the handleAnalyzerError
function to detect and properly handle 403 errors as auth/permission failures
rather than rate limits. Also review the similar logic around line 85 to ensure
403 errors are handled distinctly from rate-limit errors and report the correct
error message to users.

In `@tests/github-ratelimit.test.js`:
- Around line 46-256: The test file is re-implementing helper functions
(isRateLimited, staleResponse, safeCacheSet, and getStaleLocalCache) instead of
importing them from their source modules (api/github.js and
src/js/githubAnalyzer.js), which allows implementations to diverge without test
detection. Export these four helper functions from their respective source
files, then remove all local function definitions from the test file and import
them at the top instead. This ensures tests validate the actual shipped behavior
rather than locally re-created versions.

---

Nitpick comments:
In `@api/github.js`:
- Around line 315-318: The else if branch checking isRateLimited(commitsRes)
contains a redundant assignment to lastCommit since it is already initialized to
'—' at line 298 and this branch does not change that value. Remove the else if
block entirely, or if you want to keep the intent documented, replace the
assignment with just a comment explaining that commits are rate-limited but repo
data is fine, then remove the lastCommit = '—' line.

In `@tests/github-ratelimit.test.js`:
- Around line 9-42: Remove the unused MockResponse class and the makeOkRes
function from the test scaffolding section. The MockResponse class (which
includes the constructor with body and init parameters, the json and text
methods) is not referenced anywhere in the test file, and the makeOkRes function
that creates mock responses with status 200 is also unused since the tests
either create mock responses inline or use the makeRateLimitRes and make429Res
helpers instead. Delete both of these to simplify the test setup while keeping
the actively used helper functions intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6e238d3a-de45-4dfb-b6fe-bea96461d986

📥 Commits

Reviewing files that changed from the base of the PR and between 2262547 and 52eba7e.

📒 Files selected for processing (3)
  • api/github.js
  • src/js/githubAnalyzer.js
  • tests/github-ratelimit.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-06-15T18:15:28.688Z
Learnt from: arghya29
Repo: S3DFX-CYBER/GSoC-Org-Finder- PR: 1882
File: src/js/footer.js:33-33
Timestamp: 2026-06-15T18:15:28.688Z
Learning: In this repo’s JavaScript (e.g., footer.js), `globalThis` is intentionally preferred over `window` to keep code environment-agnostic and to satisfy SonarCloud static analysis. The project targets modern browsers (ES2021) with no transpilation, so `globalThis` is fully supported—do not flag `globalThis` usage as a browser compatibility concern or suggest replacing it with `window` during review.

Applied to files:

  • src/js/githubAnalyzer.js
🪛 ast-grep (0.44.0)
src/js/githubAnalyzer.js

[warning] 159-159: Avoid using the initial state variable in setState
Context: setLocalCache(cache)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)


[error] 159-159: React's useState should not be directly called
Context: setLocalCache(cache)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(usestate-direct-usage)

🪛 GitHub Check: SonarCloud Code Analysis
api/github.js

[warning] 317-317: Review this redundant assignment: "lastCommit" already holds the assigned value along all execution paths.

See more on https://sonarcloud.io/project/issues?id=S3DFX-CYBER_GSoC-Org-Finder-&issues=AZ7zEWmQ2oNuUBvr02tv&open=AZ7zEWmQ2oNuUBvr02tv&pullRequest=1944

🔇 Additional comments (2)
api/github.js (1)

19-49: LGTM!

src/js/githubAnalyzer.js (1)

132-162: LGTM!

Comment thread src/js/githubAnalyzer.js Outdated
Comment thread tests/github-ratelimit.test.js Outdated
@github-project-automation github-project-automation Bot moved this from Todo to In progress in GSSOC 26 Jun 23, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 3 files

Confidence score: 2/5

  • In src/js/githubAnalyzer.js (403 catch-path / isRateLimited handling), all GitHub 403s are treated as rate limits, so auth/permission failures can be hidden behind stale-cache fallback and users get the wrong remediation path — split true quota/rate-limit detection from generic 403 permission errors before merging.
  • In api/github.js (?gfi=1 stale fallback), passing { gfi: stale.gfi } into staleResponse drops ts, which makes staleSeconds serialize as null and breaks freshness signaling for clients — include the timestamp field in the stale payload passed to staleResponse.
  • In src/js/githubAnalyzer.js (stale selection logic), localStorage stale data is always preferred without comparing timestamps, so older local data can override fresher server stale data and show users outdated analysis — add an explicit timestamp comparison and only prefer local when it is actually newer.
  • tests/github-ratelimit.test.js currently diverges from runtime contracts (plain object vs Response), has tight real-time assertions, and repeats helper logic, which lowers confidence that regressions will be caught reliably — align test helpers with production staleResponse, mock/freeze time for staleSeconds, and deduplicate helpers before merge.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="tests/github-ratelimit.test.js">

<violation number="1" location="tests/github-ratelimit.test.js:112">
P2: Potentially flaky test: tight staleSeconds window (±1s) uses real Date.now() and may fail under CI load or thread contention</violation>

<violation number="2" location="tests/github-ratelimit.test.js:150">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

Helper functions and constants are duplicated across three separate test cases instead of being extracted into a shared utility, creating a maintenance burden.</violation>
</file>

<file name="src/js/githubAnalyzer.js">

<violation number="1" location="src/js/githubAnalyzer.js:139">
P2: Local stale cache is unconditionally preferred over the server's stale payload despite a comment claiming to "prefer fresher local cache" — no timestamp comparison is performed, so older localStorage data may be served instead of fresher server data.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Browser as Browser Client
    participant LocalStorage as localStorage
    participant EdgeFn as Vercel Edge (api/github.js)
    participant Cache as In-Memory Cache (CACHE)
    participant GitHubAPI as GitHub API

    Note over Browser,GitHubAPI: Rate Limit Stale-Cache Fallback Flow

    Browser->>EdgeFn: GET /api/github?user=username
    EdgeFn->>Cache: get("user__octocat")
    alt Fresh cache hit (unchanged happy path)
        Cache-->>EdgeFn: cached data (within TTL)
        EdgeFn-->>Browser: 200 {cached: true, ...}
    else No fresh cache → fetch from GitHub
        EdgeFn->>GitHubAPI: fetch repos, commits, etc.

        alt GitHub API returns rate limit (429 or 403 with x-ratelimit-remaining=0)
            GitHubAPI-->>EdgeFn: rate-limited response
            EdgeFn->>Cache: getStale("user__octocat")
            alt Stale in-memory cache exists
                Cache-->>EdgeFn: stale data (can be expired)
                EdgeFn->>EdgeFn: staleResponse(data) → strip ts, add rateLimit:true, staleSeconds
                EdgeFn-->>Browser: 200 {cached: true, rateLimit: true, staleSeconds: N, ...}
            else No stale in-memory cache
                EdgeFn-->>Browser: 429 {error: "GitHub API rate limit reached..."}
            end

        else Normal GitHub response (200)
            GitHubAPI-->>EdgeFn: repo data, activity, etc.
            EdgeFn->>Cache: safeCacheSet(key, result with ts)
            EdgeFn-->>Browser: 200 {languages, stars, activity, ...}
        end
    end

    Note over Browser,LocalStorage: Client-side handling in githubAnalyzer.js

    Browser->>Browser: analyzeGitHubUser() receives response/error

    alt Response body has rateLimit: true
        Browser->>LocalStorage: getStaleLocalCache(username)
        alt Stale localStorage exists (ignores TTL)
            LocalStorage-->>Browser: stale user profile
            Browser->>Browser: return {stale: true, ...}
        else No local stale cache
            Browser->>Browser: throw user-friendly error message
        end

    else Network fetch error (429/403 thrown)
        alt Error is rate-limit related
            Browser->>LocalStorage: getStaleLocalCache(username)
            opt Stale localStorage exists
                LocalStorage-->>Browser: stale user profile
                Browser->>Browser: return {stale: true, ...}
            end
            Browser->>Browser: throw rate-limit error if no stale
        else Other error
            Browser->>Browser: handleAnalyzerError → throw
        end

    else Normal successful response (no rateLimit)
        Browser->>LocalStorage: save to localStorage with ts
        Browser->>Browser: return userProfile (no stale flag)
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/js/githubAnalyzer.js Outdated
Comment thread src/js/githubAnalyzer.js
const localStale = getStaleLocalCache(normalizedUsername);
if (localStale) {
console.warn('GitHub rate limit reached — serving stale local cache for', normalizedUsername);
return { ...localStale, stale: true };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Local stale cache is unconditionally preferred over the server's stale payload despite a comment claiming to "prefer fresher local cache" — no timestamp comparison is performed, so older localStorage data may be served instead of fresher server data.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/js/githubAnalyzer.js, line 139:

<comment>Local stale cache is unconditionally preferred over the server's stale payload despite a comment claiming to "prefer fresher local cache" — no timestamp comparison is performed, so older localStorage data may be served instead of fresher server data.</comment>

<file context>
@@ -108,23 +129,54 @@ async function analyzeGitHubUser(username, options = {}) {
+      const localStale = getStaleLocalCache(normalizedUsername);
+      if (localStale) {
+        console.warn('GitHub rate limit reached — serving stale local cache for', normalizedUsername);
+        return { ...localStale, stale: true };
+      }
+      // Server already returned a best-effort stale payload
</file context>

Comment thread api/github.js Outdated
Comment thread tests/github-ratelimit.test.js Outdated
assert.strictEqual(result.cached, true);
assert.strictEqual(result.stars, 42);
assert.ok(!('ts' in result), 'ts should be stripped from stale response');
assert.ok(result.staleSeconds >= 4 && result.staleSeconds <= 6, 'staleSeconds should be ~5');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Potentially flaky test: tight staleSeconds window (±1s) uses real Date.now() and may fail under CI load or thread contention

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/github-ratelimit.test.js, line 112:

<comment>Potentially flaky test: tight staleSeconds window (±1s) uses real Date.now() and may fail under CI load or thread contention</comment>

<file context>
@@ -0,0 +1,256 @@
+    assert.strictEqual(result.cached, true);
+    assert.strictEqual(result.stars, 42);
+    assert.ok(!('ts' in result), 'ts should be stripped from stale response');
+    assert.ok(result.staleSeconds >= 4 && result.staleSeconds <= 6, 'staleSeconds should be ~5');
+});
+
</file context>

Comment thread tests/github-ratelimit.test.js Outdated
Comment thread src/js/githubAnalyzer.js Outdated
Comment thread tests/github-ratelimit.test.js
@@ -0,0 +1,256 @@
// tests/github-ratelimit.test.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: Flag AI Slop and Fabricated Changes

Helper functions and constants are duplicated across three separate test cases instead of being extracted into a shared utility, creating a maintenance burden.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At tests/github-ratelimit.test.js, line 150:

<comment>Helper functions and constants are duplicated across three separate test cases instead of being extracted into a shared utility, creating a maintenance burden.</comment>

<file context>
@@ -0,0 +1,256 @@
+        })
+    };
+
+    function getLocalCache() {
+        try {
+            const raw = fakeStorage[GITHUB_ANALYZER_CACHE_KEY];
</file context>

@Doremon-tech-svg
Doremon-tech-svg force-pushed the feat/github-api-rate-limit-handling branch from 52eba7e to 2e3181f Compare June 23, 2026 06:19
@github-actions github-actions Bot added gssoc26 GirlScript Summer of Code 2026 dco-verified and removed gssoc26 GirlScript Summer of Code 2026 labels Jun 23, 2026
@Doremon-tech-svg
Doremon-tech-svg force-pushed the feat/github-api-rate-limit-handling branch from 2e3181f to e365dbb Compare June 23, 2026 06:20
@github-actions github-actions Bot added gssoc26 GirlScript Summer of Code 2026 and removed gssoc26 GirlScript Summer of Code 2026 labels Jun 23, 2026
@github-actions github-actions Bot added gssoc26 GirlScript Summer of Code 2026 nsoc26 Nexus Spring of Code 2026 and removed missing-program-classification gssoc26 GirlScript Summer of Code 2026 labels Jun 23, 2026
@github-project-automation github-project-automation Bot moved this to Todo in NSOC 26 Jun 23, 2026
- Export isRateLimited, staleResponse, safeCacheSet from api/github.js
- Export getStaleLocalCache from src/js/githubAnalyzer.js
- Fix: 403 without x-ratelimit-remaining=0 is auth error, not rate limit
- Fix: GitHub 403 in analyzeGitHubUser now routes to auth error branch
- Tests import real exported helpers via CJS shims instead of re-implementing
- Add tests/helpers/ with edge-runtime shims for node:test compatibility
- 12 unit tests, all passing

Closes S3DFX-CYBER#1702

Signed-off-by: Doremon-tech-svg <richhariyadivyank1@gmail.com>
@Doremon-tech-svg
Doremon-tech-svg force-pushed the feat/github-api-rate-limit-handling branch from e365dbb to 651cf56 Compare June 23, 2026 06:22
@github-actions github-actions Bot added invalid-program-classification nsoc26 Nexus Spring of Code 2026 gssoc26 GirlScript Summer of Code 2026 and removed gssoc26 GirlScript Summer of Code 2026 nsoc26 Nexus Spring of Code 2026 labels Jun 23, 2026

@deepak0x deepak0x left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The CI check "Validate PR Program Classification" is failing because the unchecked [ ] NSoC line in the template trips the parser.
  • The "TENET Security Review" CI check is failing and you need to review its output to understand and address the flagged security concern.
  • api/github.js:317 has a redundant lastCommit = '—' assignment that duplicates the initialization at line 298, and SonarCloud flagged this redundancy.
  • api/github.js:265-268 has an empty catch { } block without the explanatory comment that documented this behavior in the original code.
  • src/js/githubAnalyzer.js:139 unconditionally prefers local stale cache over server payload without timestamp comparison, so a 3-hour-old localStorage entry can shadow a 5-minute-old server entry.
  • The PR description claims "tests import real exported helpers via CJS shims" but tests/helpers/github-test-shim.js re-implements isRateLimited, staleResponse, and safeCacheSet from scratch, meaning test regressions in the actual code will go undetected.

Fix the CI failures and code redundancy first, then implement the timestamp comparison and update the test helpers to use real imports instead of re-implementing the logic.

@sonarqubecloud

Copy link
Copy Markdown

@CoderOggy78 CoderOggy78 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In progress
Status: Todo

Development

Successfully merging this pull request may close these issues.

feat: Add GitHub API Rate Limit Handling for Repository Statistics

3 participants