Skip to content

refactor!: rewrite and format code base#229

Merged
vn7n24fzkq merged 2 commits intomainfrom
refactor
Jan 10, 2026
Merged

refactor!: rewrite and format code base#229
vn7n24fzkq merged 2 commits intomainfrom
refactor

Conversation

@vn7n24fzkq
Copy link
Owner

@vn7n24fzkq vn7n24fzkq commented Jan 10, 2026

This pull request introduces several improvements and refactors across the codebase, focusing on enhanced GitHub token management, improved analytics, better cache control, and streamlined development workflows. The most significant changes include replacing the GitHub token rotation logic, standardizing cache headers and analytics tracking for API endpoints, updating the development environment setup, and modernizing dependencies and linting configurations.

API Endpoint Improvements:

  • Replaced the legacy changToNextGitHubToken function with a new getGitHubToken utility, improving how API endpoints manage and rotate GitHub tokens for rate-limiting and multi-token setups. All relevant API card endpoints now use this new approach. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]
  • Added analytics tracking (sendAnalytics) and standardized cache headers (CONST_CACHE_CONTROL) to all card API endpoints for better observability and cache consistency. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11]

Development Workflow and Tooling:

  • Introduced a devbox.json configuration for reproducible local development environments, ensuring consistent Node.js and Python versions and pre-commit hooks.
  • Updated the README.md to document the new Devbox-based development workflow and added instructions for local testing and running the Vercel API locally.
  • Added a test:local script for local card generation and a shell script (scripts/cleanup-vercel.sh) for cleaning up Vercel preview deployments. [1] [2]

Dependency and Linting Updates:

  • Upgraded ESLint and Prettier dependencies, updated the ESLint configuration to use the latest parser and plugins, and improved lint script patterns for broader file coverage. [1] [2] [3]
  • Removed unused dependencies (childprocess, moment) and updated TypeScript and related dev dependencies to latest versions. [1] [2]

CI/CD Enhancements:

  • Added a new release-action job to the GitHub Actions workflow for building, packaging, committing, and releasing the distribution on demand.

These changes collectively improve maintainability, reliability, and developer experience for the project.

Summary by CodeRabbit

  • New Features

    • Analytics tracking for card requests; automated release workflow; Devbox-based local dev environment and local test runner; script to clean up preview deployments.
  • Improvements

    • More reliable GitHub token usage and rotation across APIs; centralized cache-control behavior; improved date handling for charts; expanded test coverage for cards and analytics.
  • Chores

    • Dependency/tooling upgrades and package version bump; updated lint/test configs and project scripts.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR centralizes GitHub token resolution into a pure getGitHubToken(index) function, threads token arguments through API endpoints, card generators, and GitHub API callers, adds analytics event emission, centralizes Cache-Control, updates dev/test tooling (Devbox, ESLint, Jest, TS), and removes moment.js.

Changes

Cohort / File(s) Summary
Token Management
api/utils/github-token-updater.ts
Replaced changToNextGitHubToken with getGitHubToken(index): string; returns resolved token from GITHUB_TOKEN_{index} (fallback to GITHUB_TOKEN at index 0); no longer mutates env.
API Endpoints
api/cards/*.ts
api/cards/most-commit-language.ts, api/cards/productive-time.ts, api/cards/profile-details.ts, api/cards/repos-per-language.ts, api/cards/stats.ts
Initialize/rotate token via getGitHubToken, pass token into card SVG generators, send analytics on success (sendAnalytics), and use CONST_CACHE_CONTROL for Cache-Control header.
Card Libraries
src/cards/*-card.ts
src/cards/profile-details-card.ts, src/cards/most-commit-language-card.ts, src/cards/repos-per-language-card.ts, src/cards/stats-card.ts, src/cards/productive-time-card.ts
Added token parameter to public card creation and SVG functions; threaded token into internal data-fetch calls.
GitHub API Calls
src/github-api/*.ts
src/github-api/profile-details.ts, .../commits-per-language.ts, .../contributions-by-year.ts, .../productive-time.ts, .../repos-per-language.ts
All exported functions now accept token and use it for GraphQL/HTTP fetches instead of reading process.env.GITHUB_TOKEN.
Analytics
src/utils/analytics.ts, tests/utils/analytics.test.ts
New sendAnalytics(eventName, params, headers) exported; builds GA4 Measurement Protocol payload, sends only when enabled/on Vercel; tests added to validate behavior and error handling.
Cache Constant
src/const/cache.ts
Added exported CONST_CACHE_CONTROL constant for centralized Cache-Control header value.
App Integration
src/app.ts
Threaded GITHUB_TOKEN into all card creation calls and added analytics invocation for profile card.
Dev & Tooling
devbox.json, .eslintrc.json, jest.config.js, tsconfig.json, tsconfig.test.json, package.json, vercel.json
Added Devbox config; updated ESLint/Jest/TypeScript configs to use tsconfig.test.json; bumped deps and scripts (added test:local); formatted vercel.json and added functions memory/duration.
Tests
tests/cards/all-cards.test.ts, tests/github-api/*.test.ts
Added integration tests for card generation; updated unit tests to pass token param to GitHub API functions; added analytics tests.
Misc / Scripts / Docs
.github/workflows/manual-release.yml, README.md, scripts/cleanup-vercel.sh, src/templates/profile-details-card.ts, .eslintrc.json
Added release-action job; switched README local flow to Devbox; added vercel cleanup script; replaced moment with d3.timeFormat in profile template; ESLint config formatting/path tweak.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API as Cards API
  participant TokenSvc as getGitHubToken
  participant GitHub
  participant Analytics as GA (sendAnalytics)

  Client->>API: GET /api/cards/<card>?username=...
  API->>TokenSvc: getGitHubToken(0)
  TokenSvc-->>API: token
  API->>GitHub: fetch data (with token)
  alt GitHub returns 401/403
    API->>TokenSvc: getGitHubToken(nextIndex)
    TokenSvc-->>API: rotated token
    API->>GitHub: fetch data (with rotated token)
  end
  API->>API: render SVG (card generator with token)
  API->>Analytics: sendAnalytics(event, params, headers)
  API-->>Client: 200 + SVG (Cache-Control: CONST_CACHE_CONTROL)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A token threaded through each little chart,
No env mutation — logic set apart.
Analytics whispers in measured tone,
Devbox hums softly, tools newly grown,
A rabbit hops, and tests all pass — back home.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'refactor!: rewrite and format code base' is overly broad and vague, failing to capture the main changes (token management, analytics, dev tooling, CI/CD). Provide a more specific title that highlights the primary change, such as 'refactor!: improve token management with getGitHubToken and add analytics tracking' or focus on the most impactful change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d4eb87b and b0d0018.

⛔ Files ignored due to path filters (2)
  • dist/index.js is excluded by !**/dist/**
  • dist/index.js.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (14)
  • .github/workflows/manual-release.yml
  • api/cards/most-commit-language.ts
  • api/cards/productive-time.ts
  • api/cards/profile-details.ts
  • api/cards/repos-per-language.ts
  • api/cards/stats.ts
  • api/utils/github-token-updater.ts
  • package.json
  • scripts/cleanup-vercel.sh
  • src/app.ts
  • src/cards/profile-details-card.ts
  • src/templates/profile-details-card.ts
  • src/utils/analytics.ts
  • tests/utils/analytics.test.ts

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
api/cards/productive-time.ts (1)

44-47: Don’t console.log(err)—can leak tokens/headers from HTTP client errors.
Line 45: axios errors often include request config/headers; logging the full object risks leaking Authorization/token into logs. Log err.message and maybe err.response?.status instead.

Safer logging
 } catch (err: any) {
-        console.log(err);
+        console.log(err?.message, err?.response?.status);
         res.send(getErrorMsgCard(err.message, theme));
 }
src/cards/productive-time-card.ts (1)

53-85: Guard against non-finite utcOffset to prevent invalid indexing.
In CLI usage, utcOffset can be NaN, which makes afterOffset NaN and breaks chartData[...] += 1.

Proposed fix
 const getProductiveTimeData = async function (
     username: string,
     utcOffset: number,
     token: string
 ): Promise<Array<number>> {
+    const safeUtcOffset = Number.isFinite(utcOffset) ? utcOffset : 0;
     const until = new Date();
     const since = new Date();
     since.setFullYear(since.getFullYear() - 1);
     const productiveTime = await getProductiveTime(username, until.toISOString(), since.toISOString(), token);
@@
     for (const time of productiveTime.productiveDate) {
         const hour = new Date(time).getUTCHours(); // We use UTC+0 here
-        const afterOffset = adjustOffset(Number(hour) + Number(utcOffset), roundRobin); // Add offset to hour
+        const afterOffset = adjustOffset(Number(hour) + Number(safeUtcOffset), roundRobin); // Add offset to hour
src/cards/stats-card.ts (1)

43-79: Defensive guard for contributionYears[0] in Vercel labeling.
If profileDetails.contributionYears is unexpectedly empty, ${profileDetails.contributionYears[0]} Commits: will produce undefined Commits: (or worse if later refactors assume a string).

Proposed tweak
-            : {
+            : {
                   index: 1,
                   icon: Icon.COMMIT,
-                  name: `${profileDetails.contributionYears[0]} Commits:`,
+                  name: `${profileDetails.contributionYears[0] ?? 'Last year'} Commits:`,
                   value: `${abbreviateNumber(totalCommitContributions, 1)}`
               },
api/cards/most-commit-language.ts (1)

31-50: Fix token rotation starting point (don’t skip GITHUB_TOKEN_0 / avoid !).
Initializing with process.env.GITHUB_TOKEN! both (a) bypasses GITHUB_TOKEN_0 if present and (b) can pass undefined downstream until a 401/403 happens. Use getGitHubToken(0) as the single source of truth.

Proposed patch
-        let token = process.env.GITHUB_TOKEN!;
-        let tokenIndex = 0;
+        let tokenIndex = 0;
+        let token = getGitHubToken(tokenIndex);
         while (true) {
             try {
                 const cardSVG = await getCommitsLanguageSVGWithThemeName(username, theme, excludeArr, token);
                 await sendAnalytics('most-commit-language-card', {username, theme});
                 res.setHeader('Content-Type', 'image/svg+xml');
                 res.setHeader('Cache-Control', CONST_CACHE_CONTROL);
                 res.send(cardSVG);
                 return;
             } catch (err: any) {
                 console.log(err.message);
                 // We update github token and try again, until getNextGitHubToken throw an Error
-                if (err.response && (err.response.status === 403 || err.response.status === 401)) {
+                const status = err?.response?.status;
+                if (status === 403 || status === 401) {
                     tokenIndex += 1;
                     token = getGitHubToken(tokenIndex);
                 } else {
                     throw err;
                 }
             }
         }
🤖 Fix all issues with AI agents
In @.github/workflows/manual-release.yml:
- Line 64: Update the GitHub Action reference for the release step from
softprops/action-gh-release@v1 to softprops/action-gh-release@v2 by changing the
uses line (uses: softprops/action-gh-release@v1 → uses:
softprops/action-gh-release@v2); after updating, run or validate the workflow to
ensure any v2 input/behavior changes are handled (adjust inputs or token
permissions in the release step if the v2 action requires them).

In @api/cards/productive-time.ts:
- Around line 23-40: The token is incorrectly initialized from
process.env.GITHUB_TOKEN!, which ignores multi-token setups; change the
initialization to call getGitHubToken(0) and use that variable everywhere in the
loop (replace the current token = process.env.GITHUB_TOKEN! with token =
getGitHubToken(0)), so getProductiveTimeSVGWithThemeName(username, theme,
Number(utcOffset), token) and the retry logic that calls
getGitHubToken(tokenIndex) work correctly.

In @api/cards/repos-per-language.ts:
- Around line 31-47: The code initializes token from process.env.GITHUB_TOKEN
which ignores numbered tokens and can be undefined; change the initialization to
use getGitHubToken(0) (or getGitHubToken(tokenIndex)) and keep tokenIndex in
sync so the retry loop rotates tokens correctly (update the token assignment
before the loop or set let tokenIndex = 0; let token =
getGitHubToken(tokenIndex); and continue to call getGitHubToken(tokenIndex) when
incrementing tokenIndex in the catch).

In @api/utils/github-token-updater.ts:
- Around line 1-11: Validate the incoming index in getGitHubToken (ensure it's a
non-negative integer) and determine which environment variable was actually used
before logging; replace the unconditional console.log(`Using ${tokenName}`) with
logic that sets a usedTokenName (either tokenName or 'GITHUB_TOKEN' when
fallback occurs) and log that, and throw a clear error if index is invalid or no
token is found; reference the getGitHubToken function, tokenName variable, the
fallback to process.env.GITHUB_TOKEN, and the console.log call to locate and
update the code.

In @package.json:
- Line 10: The npm script "lint" in package.json was widened from "src/**/*.ts"
to "**/*.ts", which now includes tests/; either revert the "lint" value back to
"src/**/*.ts" (targeting only source) or add "tests/" to .eslintignore so test
files are excluded—update the "lint" entry or .eslintignore accordingly and run
the linter to confirm only intended files are scanned.
- Around line 40-41: package.json was updated to major versions of TypeScript
ESLint packages ("@typescript-eslint/eslint-plugin" and
"@typescript-eslint/parser") which can be incompatible with current
ESLint/Prettier and project configs; verify compatibility by ensuring the ESLint
core and Prettier versions match required peer ranges, update ESLint and
Prettier configs (including rules like comma-dangle and formatting defaults) to
align with ESLint 8 and Prettier 3, then run npm run format-check and npm run
lint, fix any reported rule violations or formatting diffs, and pin or adjust
package versions if necessary to preserve CI formatting/linting behavior.

In @scripts/cleanup-vercel.sh:
- Around line 1-49: The script currently risks deleting the whole project by
calling vercel remove $PROJECT_NAME; instead, enable strict shell options (set
-euo pipefail), call vercel ls --json (omit the project name argument or ensure
correct target flags like --meta target=staging / --environment staging), parse
the JSON into deployment IDs/URLs using jq from the deployments variable, then
loop over each id and call vercel remove <deployment-id> --yes (not the project
name) to delete only individual staging/preview deployments; update prompts and
error handling accordingly.

In @src/app.ts:
- Around line 2-3: Add a single validated constant for GITHUB_TOKEN at module
init and fail fast if missing instead of using process.env.GITHUB_TOKEN! inline;
e.g., read once into a const (e.g., GITHUB_TOKEN) and if falsy log an
error/throw or process.exit(1), then replace all occurrences of
process.env.GITHUB_TOKEN! (including where Authorization headers are built and
where sendAnalytics or createProfileDetailsCard or other functions consume the
token) to use that validated const so you never emit "Authorization: bearer
undefined".

In @src/cards/profile-details-card.ts:
- Around line 63-79: getProfileDetailsData currently uses
profileDetails.contributionYears.slice(0, 1) which assumes years are
newest-first and can break when the array is empty; instead ensure you pick the
most recent year safely by checking profileDetails.contributionYears.length > 0
and selecting the max/newest year (e.g., sort descending or use
Array.prototype.reduce to find the largest year or use slice(-1) on a copy)
before calling getContributionByYear; apply the same guard/selection fix to the
other contribution-year loop (the later block that also iterates
contributionYears) so you never pass undefined to getContributionByYear and
always compute the intended latest-year contributions.

In @src/templates/profile-details-card.ts:
- Around line 52-54: The aggregation uses formatter/formatDate and then calls
new Date(formatDate), which is unreliable for "YYYY-MM" strings; replace the new
Date(formatDate) usage in the contributionsData loop (the code that computes
date.getTime() for grouping) with a deterministic parse such as using
d3.timeParse('%Y-%m')(formatDate) or by appending a day component (e.g., treat
month start by parsing formatDate + '-01') so the grouped timestamps are
consistent across environments.

In @src/utils/analytics.ts:
- Around line 7-37: sendAnalytics is currently including raw params.username in
the GA payload and sending engagement_time_msec and session_id as strings;
update sendAnalytics to compute clientId via getClientId(params.username) but
remove username from the event params before building payload (do not send PII),
and ensure engagement_time_msec and session_id are numeric values (e.g., 100 and
1) in the payload.events[0].params; modify the payload construction in
sendAnalytics (and avoid mutating the original params object by creating a
shallow copy without username) so GA receives pseudonymous client_id and numeric
fields.
🧹 Nitpick comments (12)
.github/workflows/manual-release.yml (1)

42-42: Consider adding fetch-depth: 0 for tag operations.

The release-web job specifies fetch-depth: 0 (line 28) but this job doesn't. Since you're creating releases with tags and generating release notes, a shallow clone may cause issues with tag creation or note generation that relies on commit history.

Suggested fix
       - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
README.md (1)

180-210: Prefer npx vercel (and clarify token expectations) to avoid global install + reduce friction.
Lines 206-210: instead of npm i -g vercel, consider documenting npx vercel dev (or devbox shell + local install), and clarify what token type/scopes are expected for GITHUB_TOKEN in local testing.

api/cards/repos-per-language.ts (1)

24-28: Avoid type assertion + skip empty exclude entries.
Line 24: prefer const excludeArr: string[] = []; and filter out empty values so exclude= doesn’t add '' to the exclude list.

Possible tweak
-    const excludeArr = <string[]>[];
-    exclude.split(',').forEach(function (val) {
-        const translatedLanguage = translateLanguage(val);
-        excludeArr.push(translatedLanguage.toLowerCase());
-    });
+    const excludeArr: string[] = [];
+    exclude
+        .split(',')
+        .map((v) => v.trim())
+        .filter(Boolean)
+        .forEach((val) => {
+            const translatedLanguage = translateLanguage(val);
+            excludeArr.push(translatedLanguage.toLowerCase());
+        });
tsconfig.test.json (1)

1-16: Looks good; consider excluding generated dirs if present (dist/build/coverage).
This config is fine for test/lint typechecking; adding common generated folders to exclude can reduce noise if they exist in-repo.

api/cards/profile-details.ts (1)

19-19: Consider explicit validation for GITHUB_TOKEN.

The non-null assertion (!) assumes GITHUB_TOKEN is always present. While the outer try-catch would handle missing tokens, an explicit check with a clear error message would improve developer experience.

♻️ Optional: Add explicit validation
-    let token = process.env.GITHUB_TOKEN!;
+    const initialToken = process.env.GITHUB_TOKEN;
+    if (!initialToken) {
+        throw new Error('GITHUB_TOKEN environment variable is required');
+    }
+    let token = initialToken;
api/cards/stats.ts (1)

19-26: Consistent implementation with other card endpoints.

The token management, analytics tracking, and cache control implementation follows the same pattern as profile-details.ts, which ensures consistency across the codebase. The same optional refactor regarding explicit GITHUB_TOKEN validation applies here as well.

src/github-api/repos-per-language.ts (1)

33-61: Tighten types around fetcher / pagination to avoid implicit-any drift.
Right now variables: any, cursor = null, and nodes = [] make it easy for subtle response-shape regressions to slip through.

Proposed refactor
-const fetcher = (token: string, variables: any) => {
+type ReposPerLanguageVars = {login: string; endCursor: string | null};
+type ReposPerLanguageNode = {primaryLanguage: {name: string; color: string} | null};
+
+const fetcher = (token: string, variables: ReposPerLanguageVars) => {
     // contain private repo need token permission
     return request(
         {
             Authorization: `bearer ${token}`
         },
         {
             query: `
@@
             variables
         }
     );
 };
@@
 export async function getRepoLanguages(
     username: string,
     exclude: Array<string>,
     token: string
 ): Promise<RepoLanguages> {
     let hasNextPage = true;
-    let cursor = null;
+    let cursor: string | null = null;
     const repoLanguages = new RepoLanguages();
-    const nodes = [];
+    const nodes: ReposPerLanguageNode[] = [];

Also applies to: 64-78

src/utils/analytics.ts (1)

39-48: Good: hard guard + timeout + error swallowing keeps analytics from breaking card generation.
One small hardening: ensure logs can’t accidentally include secrets (e.g., avoid dumping raw Axios error objects/config).

src/cards/repos-per-language-card.ts (1)

6-13: Token propagation looks consistent; consider typing langData explicitly.

Proposed refactor
 const getRepoLanguageData = async function (username: string, exclude: Array<string>, token: string) {
     const repoLanguages = await getRepoLanguages(username, exclude, token);
-    let langData = [];
+    let langData: {name: string; value: number; color: string}[] = [];

Also applies to: 15-24, 31-33

src/app.ts (1)

126-133: Same token validation should apply to main() too (local runs).
Otherwise local generation can silently degrade into unauthenticated/failed GitHub API calls.

src/cards/most-commit-language-card.ts (1)

6-24: Token threading is consistent; consider typing langData explicitly (avoid implicit any[]).

Proposed refactor
 const getCommitsLanguageData = async function (
     username: string,
     exclude: Array<string>,
     token: string
 ): Promise<{name: string; value: number; color: string}[]> {
     const commitLanguages: CommitLanguages = await getCommitLanguage(username, exclude, token);
-    let langData = [];
+    let langData: {name: string; value: number; color: string}[] = [];

Also applies to: 51-57

api/cards/most-commit-language.ts (1)

24-28: Avoid pushing empty/whitespace exclude entries; drop the cast-style init.
exclude="" currently yields [''] and may create a no-op/odd exclude item.

Proposed patch
-    const excludeArr = <string[]>[];
-    exclude.split(',').forEach(function (val) {
-        const translatedLanguage = translateLanguage(val);
-        excludeArr.push(translatedLanguage.toLowerCase());
-    });
+    const excludeArr: string[] = exclude
+        .split(',')
+        .map((val) => translateLanguage(val.trim()).toLowerCase())
+        .filter(Boolean);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32f6b1f and d4eb87b.

⛔ Files ignored due to path filters (5)
  • dist/index.js is excluded by !**/dist/**
  • dist/index.js.map is excluded by !**/dist/**, !**/*.map
  • dist/licenses.txt is excluded by !**/dist/**
  • dist/sourcemap-register.js is excluded by !**/dist/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (36)
  • .eslintrc.json
  • .github/workflows/manual-release.yml
  • README.md
  • api/cards/most-commit-language.ts
  • api/cards/productive-time.ts
  • api/cards/profile-details.ts
  • api/cards/repos-per-language.ts
  • api/cards/stats.ts
  • api/utils/github-token-updater.ts
  • devbox.json
  • jest.config.js
  • package.json
  • scripts/cleanup-vercel.sh
  • src/app.ts
  • src/cards/most-commit-language-card.ts
  • src/cards/productive-time-card.ts
  • src/cards/profile-details-card.ts
  • src/cards/repos-per-language-card.ts
  • src/cards/stats-card.ts
  • src/const/cache.ts
  • src/github-api/commits-per-language.ts
  • src/github-api/contributions-by-year.ts
  • src/github-api/productive-time.ts
  • src/github-api/profile-details.ts
  • src/github-api/repos-per-language.ts
  • src/templates/profile-details-card.ts
  • src/utils/analytics.ts
  • tests/cards/all-cards.test.ts
  • tests/github-api/commits-per-language.test.ts
  • tests/github-api/contributions-by-year.test.ts
  • tests/github-api/profile-details.test.ts
  • tests/github-api/repos-per-language.test.ts
  • tests/utils/analytics.test.ts
  • tsconfig.json
  • tsconfig.test.json
  • vercel.json
🧰 Additional context used
🧬 Code graph analysis (17)
tests/github-api/profile-details.test.ts (1)
src/github-api/profile-details.ts (1)
  • getProfileDetails (92-127)
tests/github-api/repos-per-language.test.ts (1)
src/github-api/repos-per-language.ts (1)
  • getRepoLanguages (64-99)
tests/github-api/commits-per-language.test.ts (1)
src/github-api/commits-per-language.ts (1)
  • getCommitLanguage (64-97)
.github/workflows/manual-release.yml (1)
src/utils/file-writer.ts (1)
  • isInGithubAction (28-54)
tests/utils/analytics.test.ts (1)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
tests/github-api/contributions-by-year.test.ts (1)
src/github-api/contributions-by-year.ts (1)
  • getContributionByYear (41-66)
src/cards/most-commit-language-card.ts (1)
src/github-api/commits-per-language.ts (2)
  • CommitLanguages (15-31)
  • getCommitLanguage (64-97)
api/utils/github-token-updater.ts (1)
src/utils/file-writer.ts (1)
  • isInGithubAction (28-54)
api/cards/repos-per-language.ts (4)
src/utils/translator.ts (1)
  • translateLanguage (1-273)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/const/cache.ts (1)
  • CONST_CACHE_CONTROL (1-1)
api/utils/github-token-updater.ts (1)
  • getGitHubToken (1-11)
src/cards/profile-details-card.ts (3)
src/const/theme.ts (1)
  • ThemeMap (1-1)
src/github-api/profile-details.ts (2)
  • ProfileDetails (3-25)
  • getProfileDetails (92-127)
src/github-api/contributions-by-year.ts (1)
  • getContributionByYear (41-66)
api/cards/productive-time.ts (3)
src/cards/productive-time-card.ts (1)
  • getProductiveTimeSVGWithThemeName (15-24)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/const/cache.ts (1)
  • CONST_CACHE_CONTROL (1-1)
tests/cards/all-cards.test.ts (6)
src/utils/file-writer.ts (1)
  • writeSVG (14-18)
src/cards/profile-details-card.ts (1)
  • createProfileDetailsCard (9-23)
src/cards/repos-per-language-card.ts (1)
  • createReposPerLanguageCard (6-13)
src/cards/most-commit-language-card.ts (1)
  • createCommitsPerLanguageCard (6-13)
src/cards/stats-card.ts (1)
  • createStatsCard (9-16)
src/cards/productive-time-card.ts (1)
  • createProductiveTimeCard (6-13)
api/cards/profile-details.ts (3)
src/cards/profile-details-card.ts (1)
  • getProfileDetailsSVGWithThemeName (24-33)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/const/cache.ts (1)
  • CONST_CACHE_CONTROL (1-1)
api/cards/stats.ts (5)
src/cards/stats-card.ts (1)
  • getStatsSVGWithThemeName (18-22)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/const/cache.ts (1)
  • CONST_CACHE_CONTROL (1-1)
api/utils/github-token-updater.ts (1)
  • getGitHubToken (1-11)
src/templates/stats-card.ts (1)
  • createStatsCard (5-61)
api/cards/most-commit-language.ts (4)
src/cards/most-commit-language-card.ts (1)
  • getCommitsLanguageSVGWithThemeName (15-24)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/const/cache.ts (1)
  • CONST_CACHE_CONTROL (1-1)
api/utils/github-token-updater.ts (1)
  • getGitHubToken (1-11)
src/cards/stats-card.ts (2)
src/github-api/profile-details.ts (1)
  • getProfileDetails (92-127)
src/github-api/contributions-by-year.ts (1)
  • getContributionByYear (41-66)
src/app.ts (6)
src/cards/profile-details-card.ts (1)
  • createProfileDetailsCard (9-23)
src/utils/analytics.ts (1)
  • sendAnalytics (14-49)
src/cards/repos-per-language-card.ts (1)
  • createReposPerLanguageCard (6-13)
src/cards/most-commit-language-card.ts (1)
  • createCommitsPerLanguageCard (6-13)
src/cards/stats-card.ts (1)
  • createStatsCard (9-16)
src/cards/productive-time-card.ts (1)
  • createProductiveTimeCard (6-13)
🪛 actionlint (1.7.10)
.github/workflows/manual-release.yml

64-64: the runner of "softprops/action-gh-release@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🔇 Additional comments (40)
.github/workflows/manual-release.yml (1)

36-61: LGTM on job structure and build pipeline.

The job setup follows good practices: appropriate permissions scoping, consistent git config with the other job, and graceful handling of "no changes" scenario with the || echo fallback.

tsconfig.json (1)

11-17: LGTM!

Excluding scripts/**/*.ts from the main compilation is appropriate for separating build-time utilities from the application codebase, especially with the introduction of a separate test configuration.

devbox.json (1)

1-19: LGTM!

The Devbox configuration properly defines a reproducible development environment with appropriate package versions and convenient shell scripts.

src/const/cache.ts (1)

1-1: LGTM!

The cache control strategy is well-suited for profile card data: 4-hour client cache, 24-hour CDN cache, and 7-day stale-while-revalidate provide a good balance between freshness and performance. Centralizing this constant improves maintainability.

package.json (2)

13-13: LGTM!

The test:local script provides a convenient way to test card generation locally, aligning with the Devbox development workflow.


54-55: Ensure TypeScript 5.4.5 build succeeds.

Run npm run build and npm test to confirm the upgrade doesn't introduce type errors or break existing functionality. The codebase uses strict mode and noImplicitAny, which should catch any compatibility issues.

tests/github-api/commits-per-language.test.ts (1)

87-87: LGTM! Token parameter correctly added to test calls.

The test calls have been properly updated to include the token parameter, aligning with the refactored function signature. Using a hardcoded 'token' string is appropriate for these unit tests since the HTTP requests are mocked.

Also applies to: 99-99, 104-104

.eslintrc.json (2)

7-14: LGTM! Improved formatting.

The multi-line formatting of the plugins and extends arrays improves readability.


19-19: tsconfig.test.json is properly configured for ESLint.

Verification confirms the configuration meets all requirements:

  • ✓ Extends the base tsconfig.json
  • ✓ Includes both source and test files (src/**/*, api/**/*, tests/**/*, scripts/**/*)
  • ✓ The rootDir override to "." and intentional inclusion of test/api/scripts directories properly override the base config's exclusions, which is appropriate for ESLint's linting scope
src/github-api/profile-details.ts (1)

92-95: LGTM! Explicit token passing improves testability.

The function signature has been correctly updated to accept an explicit token parameter, which is then passed to the fetcher. This refactoring removes the environment variable dependency and aligns with the broader token management improvements across the codebase.

tests/github-api/repos-per-language.test.ts (1)

120-120: LGTM! Token parameter correctly added to test calls.

The test calls have been properly updated to include the token parameter, aligning with the refactored function signature. The tests continue to properly exercise success, error, and language exclusion scenarios with appropriate mocked responses.

Also applies to: 132-132, 137-137

src/github-api/contributions-by-year.ts (1)

41-52: LGTM! Explicit token passing improves testability.

The function signature has been correctly updated to accept an explicit token parameter, which is then passed to the fetcher. This refactoring removes the environment variable dependency and maintains consistency with other API modules in the codebase.

tests/github-api/contributions-by-year.test.ts (1)

37-37: LGTM! Test updates align with the new API signature.

Both test cases correctly pass the token parameter to match the updated getContributionByYear function signature.

Also applies to: 47-47

tests/github-api/profile-details.test.ts (1)

73-73: LGTM! Test updates align with the new API signature.

Both test cases correctly pass the token parameter to match the updated getProfileDetails function signature.

Also applies to: 108-108

src/github-api/productive-time.ts (1)

72-92: LGTM! Token parameter correctly threaded through the data-fetching layer.

The function signature update and token propagation to both GraphQL fetchers are consistent with the PR's token management refactor.

vercel.json (2)

6-11: LGTM! Formatting improvements enhance readability.

The multi-line formatting for rewrites, headers, and redirects makes the configuration more maintainable without changing functionality.

Also applies to: 31-38, 55-58


12-17: Verify that resource limits are adequate for the card generation workload.

The new function configuration sets memory to 128MB and maxDuration to 10 seconds for all API endpoints. Card generation involves multiple GraphQL queries, data processing, and SVG rendering for 30+ themes per request. Ensure these limits are sufficient to handle typical and peak workloads without timeout or memory issues.

Consider monitoring these metrics in production or testing with representative workloads to confirm the limits are appropriate.

tests/cards/all-cards.test.ts (3)

1-21: LGTM! Clean test setup with proper dependency mocking.

The imports and mock setup follow Jest best practices by isolating the card creation functions from their dependencies.


53-55: Verify the productiveDate mock data type.

The mock returns productiveDate: [new Date().toISOString()] (an array of strings), but the ProfuctiveTime class defines productiveDate: Date[] (an array of Date objects). Ensure the card generation code handles whichever type is actually returned by the real implementation.

Consider updating the mock to return Date objects for consistency:

🔧 Suggested fix
         (getProductiveTime as jest.Mock).mockResolvedValue({
-            productiveDate: [new Date().toISOString()]
+            productiveDate: [new Date()]
         });

58-103: LGTM! Comprehensive integration test coverage for all card types.

The test suite verifies that each card creation function properly generates and writes SVG output. The consistent structure and appropriate assertions make the tests maintainable and effective.

tests/utils/analytics.test.ts (4)

1-13: LGTM!

The test setup correctly uses jest.resetModules() to ensure each test gets a fresh module instance with isolated environment variables. The pattern of saving and restoring process.env prevents test pollution.


15-36: LGTM!

These tests correctly verify that analytics are skipped when environment guards fail. The use of dynamic require() after setting environment variables is the correct pattern for testing module-level code that reads from process.env.


38-66: LGTM!

The test comprehensively verifies the GA4 payload structure, including the measurement ID in the URL, event structure, and parameter propagation. The use of flexible matchers (expect.any, expect.objectContaining) appropriately handles computed values like the hashed client_id.


68-84: LGTM!

The error handling test correctly verifies that analytics errors are caught and logged without breaking the main flow. Proper mock restoration ensures no test pollution.

src/github-api/commits-per-language.ts (2)

64-68: LGTM!

The function signature correctly adds the token parameter to support the new token management strategy. This breaking change aligns with the PR's refactoring objective and enables caller-controlled authentication.


71-73: LGTM!

The implementation correctly passes the provided token to the fetcher, removing the implicit dependency on environment variables. This change supports the PR's goal of explicit token management.

jest.config.js (2)

8-10: LGTM!

The updated ts-jest configuration uses the modern array format, enabling test-specific TypeScript compiler options via tsconfig.test.json. This is the recommended approach for configuring ts-jest with custom settings.


16-16: LGTM!

Excluding the scripts/** directory from coverage collection is appropriate, as utility scripts typically don't require the same test coverage as application code.

api/cards/profile-details.ts (4)

2-5: LGTM!

The updated imports correctly reflect the new token management utility (getGitHubToken), analytics tracking (sendAnalytics), and centralized cache control constant (CONST_CACHE_CONTROL).


23-24: LGTM!

The token is correctly threaded through to the card generation function, and analytics are properly awaited to ensure the event is sent before the Lambda function freezes.


26-26: LGTM!

Using the centralized CONST_CACHE_CONTROL constant ensures consistent cache behavior across all card endpoints and makes cache policy updates easier.


32-34: LGTM!

The token rotation logic correctly increments the index and retrieves the next token using getGitHubToken(). This enables multi-token setups for handling rate limits.

api/cards/stats.ts (2)

2-5: LGTM!

The updated imports mirror the pattern in profile-details.ts, providing consistent token management, analytics, and cache control across card endpoints.


32-34: LGTM!

The token rotation logic matches the pattern used in other card endpoints, enabling rate limit handling through multi-token rotation.

src/cards/productive-time-card.ts (2)

6-13: Token threading here looks consistent with the rest of the PR.


15-24: Good: token forwarded through the themed SVG entrypoint.

src/cards/stats-card.ts (1)

9-22: Token propagation into getProfileDetails / getContributionByYear is consistent across Vercel/non-Vercel paths.

Also applies to: 33-55

api/cards/most-commit-language.ts (1)

10-23: Input validation looks good (string checks for query params).
This keeps the handler from accidentally accepting array/querystring edge-cases.

src/cards/profile-details-card.ts (2)

9-33: Token threading in public APIs is consistent and clean.
Signatures and downstream calls line up with the new token-aware GitHub API functions.


45-61: Formatting-only change here; logic unchanged.
No concerns.

@vn7n24fzkq
Copy link
Owner Author

Updated title and added docstrings to improve coverage.

@vn7n24fzkq vn7n24fzkq merged commit f092803 into main Jan 10, 2026
5 checks passed
@vn7n24fzkq vn7n24fzkq deleted the refactor branch January 10, 2026 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant