From 993e846bc79ee5c6816f0f66bb306b5ba8acf9a6 Mon Sep 17 00:00:00 2001 From: jatingow12 Date: Fri, 29 May 2026 00:54:37 +0530 Subject: [PATCH 1/2] feat(i18n): add german translations --- .gitignore | 2 +- app/customize/types.ts | 1 + lib/i18n/badgeLabels.ts | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5557118fc..f67054a02 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ yarn-error.log* # env files — ignore all local secrets but commit the example template .env* !.env.local.example - +.env.local # vercel .vercel diff --git a/app/customize/types.ts b/app/customize/types.ts index d437d5ed0..6c8943ce6 100644 --- a/app/customize/types.ts +++ b/app/customize/types.ts @@ -59,6 +59,7 @@ export const LANGUAGES = [ { value: 'ko', label: 'Korean' }, { value: 'fr', label: 'French' }, { value: 'ja', label: 'Japanese' }, + { value: 'de', label: 'German' }, ] as const satisfies readonly { value: string; label: string }[]; export type Language = (typeof LANGUAGES)[number]['value']; diff --git a/lib/i18n/badgeLabels.ts b/lib/i18n/badgeLabels.ts index fe1633d3d..a0dd1ce07 100644 --- a/lib/i18n/badgeLabels.ts +++ b/lib/i18n/badgeLabels.ts @@ -56,6 +56,13 @@ export const labels: Record = { COMMITS_THIS_MONTH: '今月のコミット数', VS_LAST_MONTH: '先月比', }, + de: { + CURRENT_STREAK: 'AKTUELLE_SERIE', + ANNUAL_SYNC_TOTAL: 'JAHRES_GESAMT', + PEAK_STREAK: 'LÄNGSTE_SERIE', + COMMITS_THIS_MONTH: 'COMMITS DIESEN MONAT', + VS_LAST_MONTH: 'vs. letzten Monat', + }, }; export function getLabels(lang: string = 'en'): BadgeLabels { From 1800cc9a9047136e2d70e8b8e89ccec66135d8a8 Mon Sep 17 00:00:00 2001 From: jatingow12 Date: Thu, 4 Jun 2026 00:51:15 +0530 Subject: [PATCH 2/2] fix: validation error SVG overflow in buildErrorResponse --- .env.local.example | 19 +- .gitattributes | 1 + .github/scripts/check-duplicates.js | 4 +- .../issue-management/assign-handler.js | 2 +- .../scripts/issue-management/claim-handler.js | 11 +- .github/scripts/issue-management/main.js | 4 + .../scripts/issue-management/parse-command.js | 3 + .../issue-management/stale-assignment.js | 4 +- .../issue-management/unclaim-handler.js | 44 + .github/workflows/assign-request-reminder.yml | 4 +- .github/workflows/ci.yml | 1 + .github/workflows/conflict-notifier.yml | 23 +- .github/workflows/issue-management.yml | 3 +- .github/workflows/pr-issue-check.yml | 2 +- .gitignore | 84 +- CONTRIBUTING.md | 229 +- README.md | 130 +- THEMES.md | 331 +- THEME_DEVELOPMENT.md | 475 + __mocks__/server-only.js | 2 + app/(root)/dashboard/[username]/page.test.tsx | 112 +- app/(root)/dashboard/[username]/page.tsx | 174 +- app/(root)/dashboard/error.test.tsx | 54 + app/(root)/dashboard/error.tsx | 2 +- app/(root)/dashboard/loading.test.tsx | 41 + app/(root)/dashboard/loading.tsx | 7 +- app/api/compare/githubFetch.test.ts | 79 + app/api/compare/route.test.ts | 127 + app/api/compare/route.ts | 83 + app/api/compare/tests/comparisonStats.test.ts | 124 + app/api/compare/tests/validation.test.ts | 66 + app/api/github/route.test.ts | 189 +- app/api/github/route.ts | 95 +- app/api/notify/route.test.ts | 281 + app/api/notify/route.ts | 229 + app/api/og/route.test.ts | 38 + app/api/og/route.tsx | 99 +- app/api/stats/route.test.ts | 121 +- app/api/stats/route.ts | 121 +- app/api/streak/png/route.test.ts | 94 + app/api/streak/png/route.ts | 48 + app/api/streak/route.test.ts | 847 +- app/api/streak/route.ts | 359 +- app/api/streak/tests/dateRange.test.ts | 124 + app/api/streak/tests/grace.test.ts | 309 + app/api/streak/tests/languages.test.ts | 106 + app/api/streak/tests/layout.test.ts | 108 + app/api/streak/tests/refresh.test.ts | 100 + app/api/streak/tests/shading.test.ts | 103 + app/api/streak/tests/theme.test.ts | 110 + app/api/streak/tests/views.test.ts | 157 + app/api/student/resume/confirm/route.ts | 90 + app/api/student/resume/tests/confirm.test.ts | 138 + app/api/student/resume/upload/route.ts | 68 + app/api/track-user/route.test.ts | 133 +- app/api/track-user/route.ts | 55 +- app/api/user-details/route.test.ts | 104 + app/api/user-details/route.ts | 49 + app/api/wrapped/route.test.ts | 219 + app/api/wrapped/route.ts | 216 + .../wrapped/tests/statsCalculation.test.ts | 100 + app/api/wrapped/tests/validation.test.ts | 98 + app/api/wrapped/yearBoundary.test.ts | 97 + .../CompareClient.accessibility.test.tsx | 108 + ...CompareClient.mouse-interactivity.test.tsx | 259 + ...pareClient.responsive-breakpoints.test.tsx | 75 + app/compare/CompareClient.test.tsx | 206 + app/compare/CompareClient.tsx | 1145 + .../CompareClient.type-compiler.test.tsx | 68 + app/compare/page.empty-fallback.test.tsx | 38 + app/compare/page.mock-integrations.test.tsx | 46 + app/compare/page.mouse-interactivity.test.tsx | 259 + .../page.responsive-breakpoints.test.tsx | 118 + app/compare/page.theme-contrast.test.tsx | 102 + app/compare/page.tsx | 33 + app/compare/page.type-compiler.test.tsx | 75 + .../CopyRepoButton.accessibility.test.tsx | 55 + .../CopyRepoButton.error-resilience.test.tsx | 88 + .../CopyRepoButton.massive-scaling.test.tsx | 106 + ...RepoButton.responsive-breakpoints.test.tsx | 55 + .../CopyRepoButton.theme-contrast.test.tsx | 55 + ...opyRepoButton.timezone-boundaries.test.tsx | 89 + app/components/CopyRepoButton.tsx | 31 + .../CopyRepoButton.type-compiler.test.tsx | 26 + app/components/CustomizeCTA.test.tsx | 201 +- app/components/CustomizeCTA.tsx | 8 +- app/components/FeatureCard.test.tsx | 17 +- app/components/Footer.test.tsx | 137 +- app/components/Footer.tsx | 219 +- app/components/HeroSection.test.tsx | 81 +- app/components/HeroSection.tsx | 64 +- app/components/Icons.accessibility.test.tsx | 44 + app/components/Icons.empty-fallback.test.tsx | 43 + app/components/Icons.massive-scaling.test.tsx | 91 + .../Icons.mock-integrations.test.tsx | 116 + .../Icons.mouse-interactivity.test.tsx | 79 + .../Icons.responsive-breakpoints.test.tsx | 80 + app/components/Icons.theme-contrast.test.tsx | 39 + .../Icons.timezone-boundaries.test.tsx | 121 + app/components/Icons.tsx | 87 +- app/components/Icons.type-compiler.test.tsx | 45 + .../ScrollRestoration.accessibility.test.tsx | 125 + .../ScrollRestoration.empty-fallback.test.tsx | 62 + ...crollRestoration.error-resilience.test.tsx | 69 + ...ScrollRestoration.massive-scaling.test.tsx | 83 + ...rollRestoration.mock-integrations.test.tsx | 64 + ...estoration.responsive-breakpoints.test.tsx | 75 + app/components/ScrollRestoration.test.tsx | 83 + .../ScrollRestoration.theme-contrast.test.tsx | 81 + ...llRestoration.timezone-boundaries.test.tsx | 79 + app/components/ScrollRestoration.tsx | 28 + .../ScrollRestoration.type-compiler.test.tsx | 49 + app/components/navbar.test.tsx | 155 +- app/components/navbar.tsx | 168 +- .../theme-switch.empty-fallback.test.tsx | 37 + .../theme-switch.error-resilience.test.tsx | 169 + .../theme-switch.massive-scaling.test.tsx | 57 + app/components/theme-switch.test.tsx | 40 + .../theme-switch.timezone-boundaries.test.tsx | 102 + app/components/theme-switch.tsx | 581 + .../theme-switch.type-compiler.test.tsx | 38 + app/contributors/ContributorsClient.tsx | 350 + app/contributors/ContributorsSearch.test.tsx | 75 + app/contributors/ContributorsSearch.tsx | 244 +- .../loading.accessibility.test.tsx | 70 + .../loading.empty-fallback.test.tsx | 87 + .../loading.error-resilience.test.tsx | 133 + .../loading.mock-integrations.test.tsx | 120 + .../loading.responsive-breakpoints.test.tsx | 53 + app/contributors/loading.test.tsx | 48 + .../loading.theme-contrast.test.tsx | 90 + .../loading.timezone-boundaries.test.tsx | 91 + app/contributors/loading.tsx | 15 +- .../loading.type-compiler.test.tsx | 103 + app/contributors/page.accessibility.test.tsx | 95 + app/contributors/page.empty-fallback.test.tsx | 142 + .../page.error-resilience.test.tsx | 114 + .../page.massive-scaling.test.tsx | 243 + .../page.mock-integrations.test.tsx | 95 + .../page.mouse-interactivity.test.tsx | 93 + .../page.responsive-breakpoints.test.tsx | 179 + app/contributors/page.theme-contrast.test.tsx | 206 + .../page.timezone-boundaries.test.tsx | 122 + app/contributors/page.tsx | 251 +- app/contributors/page.type-compiler.test.tsx | 100 + .../components/AdvancedSettingsPanel.test.tsx | 47 + .../components/AdvancedSettingsPanel.tsx | 242 + .../components/ControlsPanel.test.tsx | 122 + app/customize/components/ControlsPanel.tsx | 256 +- app/customize/components/ExportPanel.test.tsx | 88 +- app/customize/components/ExportPanel.tsx | 98 +- .../components/SectionLabel.test.tsx | 33 + app/customize/components/SectionLabel.tsx | 2 +- .../components/ThemeQuickPresets.css | 119 + .../components/ThemeQuickPresets.test.tsx | 161 + .../components/ThemeQuickPresets.tsx | 78 +- .../components/ThemeSelector.test.tsx | 193 + app/customize/components/ThemeSelector.tsx | 28 +- app/customize/page.test.tsx | 169 + app/customize/page.tsx | 467 +- app/customize/types.test.ts | 13 +- app/customize/types.ts | 43 +- app/customize/utils.test.ts | 161 +- app/customize/utils.ts | 94 +- app/documentation/code-block.test.tsx | 81 + app/documentation/page.tsx | 28 +- app/favicon.ico | Bin 4286 -> 0 bytes app/globals.css | 147 + app/icon.png | Bin 247530 -> 0 bytes app/layout.tsx | 26 +- app/not-found.test.tsx | 52 + app/not-found.tsx | 40 +- app/page.test.tsx | 261 +- app/page.tsx | 1049 +- app/template.accessibility.test.tsx | 204 + app/template.empty-fallback.test.tsx | 58 + app/template.mock-integrations.test.tsx | 130 + app/template.responsive-breakpoints.test.tsx | 173 + app/template.test.tsx | 105 + app/template.theme-contrast.test.tsx | 128 + app/template.timezone-boundaries.test.tsx | 100 + app/template.type-compiler.test.tsx | 44 + combine.py | 99 - components/AnimatedCursor.tsx | 103 + components/BrandParticles.test.tsx | 97 + components/BrandParticles.tsx | 40 +- components/CherryBlossom.test.tsx | 70 + .../DiscordButton.accessibility.test.tsx | 59 + .../DiscordButton.error-resilience.test.tsx | 64 + .../DiscordButton.mock-integrations.test.tsx | 48 + components/DiscordButton.test.tsx | 111 + ...DiscordButton.timezone-boundaries.test.tsx | 47 + components/DiscordButton.tsx | 137 + .../DiscordButton.type-compiler.test.tsx | 48 + .../FeatureCards.accessibility.test.tsx | 105 + .../FeatureCards.massive-scaling.test.tsx | 74 + .../FeatureCards.mock-integrations.test.tsx | 87 + .../FeatureCards.mouse-interactivity.test.tsx | 87 + ...atureCards.responsive-breakpoints.test.tsx | 98 + components/FeatureCards.test.tsx | 107 + .../FeatureCards.theme-contrast.test.tsx | 96 + .../FeatureCards.timezone-boundaries.test.tsx | 89 + components/FeatureCards.tsx | 466 + components/InteractiveViewer.test.tsx | 603 + components/InteractiveViewer.tsx | 458 + components/Leaderboard.accessibility.test.tsx | 95 + .../Leaderboard.error-resilience.test.tsx | 66 + .../Leaderboard.massive-scaling.test.tsx | 136 + .../Leaderboard.mock-integrations.test.tsx | 100 + .../Leaderboard.mouse-interactivity.test.tsx | 106 + ...eaderboard.responsive-breakpoints.test.tsx | 119 + components/Leaderboard.test.tsx | 153 + .../Leaderboard.theme-contrast.test.tsx | 83 + components/Leaderboard.tsx | 252 + components/Leaderboard.type-compiler.test.tsx | 76 + components/ReturnToTop.test.tsx | 123 + components/ReturnToTop.tsx | 80 +- .../ShareButtons.accessibility.test.tsx | 68 + .../ShareButtons.error-resilience.test.tsx | 41 + .../ShareButtons.massive-scaling.test.tsx | 53 + .../ShareButtons.mock-integrations.test.tsx | 69 + .../ShareButtons.mouse-interactivity.test.tsx | 50 + ...areButtons.responsive-breakpoints.test.tsx | 61 + .../ShareButtons.theme-contrast.test.tsx | 81 + .../ShareButtons.timezone-boundaries.test.tsx | 57 + components/ShareButtons.tsx | 36 + .../ShareButtons.type-compiler.test.tsx | 45 + components/WallOfLove.accessibility.test.tsx | 71 + components/WallOfLove.empty-fallback.test.tsx | 59 + .../WallOfLove.massive-scaling.test.tsx | 216 + components/WallOfLove.test.tsx | 88 + components/WallOfLove.tsx | 690 + .../commitpulse-logo.accessibility.test.tsx | 49 + components/commitpulse-logo.test.tsx | 47 + components/dashboard/AIInsights.test.tsx | 15 +- components/dashboard/AIInsights.tsx | 6 +- .../dashboard/AIInsightsSkeleton.test.tsx | 43 + components/dashboard/AIInsightsSkeleton.tsx | 30 + .../Achievements.empty-fallback.test.tsx | 64 + components/dashboard/Achievements.test.tsx | 80 +- components/dashboard/Achievements.tsx | 80 +- .../dashboard/AchievementsSkeleton.test.tsx | 27 + components/dashboard/AchievementsSkeleton.tsx | 6 +- .../dashboard/ActivityLandscape.test.tsx | 73 + components/dashboard/ActivityLandscape.tsx | 257 +- components/dashboard/CommitClock.test.tsx | 2 + components/dashboard/CommitClock.tsx | 394 +- .../dashboard/ComparisonStatsCard.test.tsx | 805 + components/dashboard/ComparisonStatsCard.tsx | 154 + components/dashboard/DashboardClient.test.tsx | 594 + components/dashboard/DashboardClient.tsx | 1172 + components/dashboard/GithubWrapped.test.tsx | 96 + components/dashboard/GithubWrapped.tsx | 16 +- .../dashboard/GrowthTrendChart.test.tsx | 220 + components/dashboard/GrowthTrendChart.tsx | 292 + components/dashboard/Heatmap.tsx | 154 +- .../dashboard/HistoricalTrendView.test.tsx | 93 + ...icalTrendView.timezone-boundaries.test.tsx | 237 + components/dashboard/HistoricalTrendView.tsx | 417 + components/dashboard/PopularRepos.tsx | 112 + components/dashboard/ProfileCard.tsx | 33 +- ...mizerModal.responsive-breakpoints.test.tsx | 113 + .../dashboard/ProfileOptimizerModal.test.tsx | 81 + .../dashboard/ProfileOptimizerModal.tsx | 571 + components/dashboard/RadarChart.test.tsx | 363 + components/dashboard/RadarChart.tsx | 245 + components/dashboard/RefreshButton.test.tsx | 90 + components/dashboard/RefreshButton.tsx | 10 +- components/dashboard/RepositoryGraph.test.tsx | 163 + components/dashboard/RepositoryGraph.tsx | 283 + .../ResumePreviewForm.accessibility.test.tsx | 79 + .../ResumePreviewForm.empty-fallback.test.tsx | 125 + ...ResumePreviewForm.massive-scaling.test.tsx | 130 + ...sumePreviewForm.mock-integrations.test.tsx | 172 + .../dashboard/ResumePreviewForm.test.tsx | 149 + .../ResumePreviewForm.theme-contrast.test.tsx | 111 + ...mePreviewForm.timezone-boundaries.test.tsx | 337 + components/dashboard/ResumePreviewForm.tsx | 413 + .../ResumePreviewForm.type-compiler.test.tsx | 95 + ...sumeProfileSection.empty-fallback.test.tsx | 62 + ...meProfileSection.error-resilience.test.tsx | 247 + ...umeProfileSection.massive-scaling.test.tsx | 148 + ...eProfileSection.mock-integrations.test.tsx | 99 + .../dashboard/ResumeProfileSection.test.tsx | 121 + ...rofileSection.timezone-boundaries.test.tsx | 99 + components/dashboard/ResumeProfileSection.tsx | 82 + ...esumeProfileSection.type-compiler.test.tsx | 52 + .../ResumeUpload.accessibility.test.tsx | 122 + .../ResumeUpload.error-resilience.test.tsx | 140 + .../ResumeUpload.mock-integrations.test.tsx | 185 + ...sumeUpload.responsive-breakpoints.test.tsx | 67 + components/dashboard/ResumeUpload.test.tsx | 143 + components/dashboard/ResumeUpload.tsx | 187 + components/dashboard/ShareSheet.test.tsx | 466 +- components/dashboard/ShareSheet.tsx | 703 +- components/dashboard/StatsCard.test.tsx | 15 +- components/dashboard/StatsCard.tsx | 11 +- .../StatsCard.utc-disclaimer.test.tsx | 41 + components/dashboard/StatsCardSkeleton.tsx | 2 +- components/dashboard/ThemeSelector.test.tsx | 87 + ...sualizationTooltip.empty-fallback.test.tsx | 102 + ...alizationTooltip.error-resilience.test.tsx | 206 + .../dashboard/VisualizationTooltip.test.tsx | 76 + components/dashboard/VisualizationTooltip.tsx | 34 + .../StatsCardSkeleton.test.tsx.snap | 2 +- components/dashboard/heatmapUtils.test.ts | 33 + .../tooltipUtils.empty-fallback.test.ts | 32 + components/dashboard/tooltipUtils.test.ts | 51 + components/dashboard/tooltipUtils.ts | 53 + eslint.config.mjs | 6 +- files.txt | 20513 ---------------- hooks/useDebounce.test.ts | 104 + hooks/useDebounce.ts | 17 + hooks/useGlowEffect.test.ts | 66 + hooks/useRecentSearches.test.ts | 94 + hooks/useRecentSearches.ts | 34 +- hooks/useShareActions.test.ts | 237 + hooks/useShareActions.ts | 296 +- lib/cache.test.ts | 651 +- lib/cache.ts | 401 + lib/calculate.test.ts | 1980 +- lib/calculate.ts | 198 +- lib/export3d.test.ts | 86 + lib/github.rotation.test.ts | 114 + lib/github.test.ts | 1760 +- lib/github.ts | 1448 +- lib/i18n/badgeLabels.test.ts | 85 + lib/i18n/badgeLabels.ts | 40 +- lib/i18n/languages/en.test.ts | 36 + lib/i18n/languages/fr.test.ts | 65 + lib/i18n/languages/ja.test.ts | 65 + lib/mongodb.test.ts | 116 +- lib/mongodb.ts | 45 +- lib/rate-limit.test.ts | 183 +- lib/rate-limit.ts | 228 +- lib/resume-parser.empty-fallback.test.ts | 125 + lib/resume-parser.error-resilience.test.ts | 108 + lib/resume-parser.massive-scaling.test.ts | 73 + lib/resume-parser.mock-integrations.test.ts | 122 + lib/resume-parser.mouse-interactivity.test.ts | 80 + ...sume-parser.responsive-breakpoints.test.ts | 61 + lib/resume-parser.test.ts | 114 + lib/resume-parser.theme-contrast.test.ts | 48 + lib/resume-parser.timezone-boundaries.test.ts | 80 + lib/resume-parser.ts | 158 + lib/resume-parser.type-compiler.test.ts | 35 + lib/svg/animations.test.ts | 56 +- lib/svg/animations.ts | 72 +- lib/svg/constants.accessibility.test.ts | 50 + lib/svg/constants.empty-fallback.test.ts | 45 + lib/svg/constants.error-resilience.test.ts | 44 + lib/svg/constants.massive-scaling.test.ts | 48 + lib/svg/constants.mock-integrations.test.ts | 44 + lib/svg/constants.mouse-interactivity.test.ts | 43 + .../constants.responsive-breakpoints.test.ts | 32 + lib/svg/constants.test.ts | 66 + lib/svg/constants.theme-contrast.test.ts | 46 + lib/svg/constants.timezone-boundaries.test.ts | 27 + lib/svg/constants.ts | 17 + lib/svg/constants.type-compiler.test.ts | 48 + lib/svg/fonts.test.ts | 24 + lib/svg/fonts.ts | 51 + lib/svg/generator.additional.test.ts | 764 + lib/svg/generator.buildTowerPaths.test.ts | 130 + lib/svg/generator.escapeXML.test.ts | 31 + lib/svg/generator.getSizeScale.test.ts | 27 + lib/svg/generator.opacity.test.ts | 231 + lib/svg/generator.test.ts | 1305 +- lib/svg/generator.truncateUsername.test.ts | 25 + lib/svg/generator.ts | 2498 +- lib/svg/generatorConstants.test.ts | 189 + lib/svg/generatorConstants.ts | 6 +- lib/svg/glacier.test.ts | 124 + lib/svg/languageColors.test.ts | 33 + lib/svg/layout.test.ts | 218 +- lib/svg/layout.ts | 101 +- lib/svg/layoutConstants.test.ts | 30 + lib/svg/layoutConstants.ts | 5 + lib/svg/lumos.test.ts | 147 + lib/svg/nord.test.ts | 69 + lib/svg/sanitizer.test.ts | 118 + lib/svg/sanitizer.ts | 83 +- lib/svg/themes.test.ts | 162 + lib/svg/themes.ts | 45 +- lib/svg/themes/dark.test.ts | 41 + lib/svg/themes/forest.test.ts | 97 + lib/svg/themes/ocean.test.ts | 62 + lib/svg/themes/rose.test.ts | 97 + lib/svg/themes/sunset.test.ts | 55 + lib/svg/themes/synthwave.test.ts | 55 + lib/validate-user.error-resilience.test.ts | 85 + lib/validations.githubParamsSchema.test.ts | 82 + lib/validations.notifyGetSchema.test.ts | 62 + lib/validations.notifyPostSchema.test.ts | 84 + lib/validations.ogParamsSchema.test.ts | 92 + lib/validations.streakParamsSchema.test.ts | 189 + lib/validations.test.ts | 1285 +- lib/validations.ts | 522 +- middleware.test.ts | 235 + middleware.ts | 16 +- models/Notification.accessibility.test.ts | 42 + models/Notification.error-resilience.test.ts | 101 + models/Notification.massive-scaling.test.ts | 71 + models/Notification.mock-integrations.test.ts | 131 + ...otification.responsive-breakpoints.test.ts | 104 + models/Notification.theme-contrast.test.ts | 113 + .../Notification.timezone-boundaries.test.ts | 264 + models/Notification.ts | 62 + models/Notification.type-compiler.test.ts | 64 + models/StudentProfile.accessibility.test.ts | 59 + models/StudentProfile.empty-fallback.test.ts | 43 + models/StudentProfile.massive-scaling.test.ts | 92 + .../StudentProfile.mock-integrations.test.ts | 80 + ...StudentProfile.mouse-interactivity.test.ts | 56 + ...dentProfile.responsive-breakpoints.test.ts | 58 + models/StudentProfile.test.ts | 107 + models/StudentProfile.theme-contrast.test.ts | 123 + ...StudentProfile.timezone-boundaries.test.ts | 284 + models/StudentProfile.ts | 87 + models/StudentProfile.type-compiler.test.ts | 126 + models/User.test.ts | 428 +- models/User.ts | 11 +- next.config.ts | 3 +- package-lock.json | 3309 +-- package.json | 33 +- raw.svg | 1403 ++ sanitized.svg | 1395 ++ scripts/benchmark-svg.test.ts | 114 + scripts/benchmark-svg.ts | 5 +- .../background-refresh.accessibility.test.ts | 37 + ...ackground-refresh.error-resilience.test.ts | 107 + ...background-refresh.massive-scaling.test.ts | 737 + ...ckground-refresh.mock-integrations.test.ts | 93 + ...ground-refresh.mouse-interactivity.test.ts | 87 + ...und-refresh.responsive-breakpoints.test.ts | 129 + .../background-refresh.theme-contrast.test.ts | 106 + ...ground-refresh.timezone-boundaries.test.ts | 112 + services/github/background-refresh.ts | 80 + .../background-refresh.type-compiler.test.ts | 46 + .../quota-monitor.accessibility.test.ts | 62 + .../quota-monitor.empty-fallback.test.ts | 72 + .../quota-monitor.error-resilience.test.ts | 123 + .../quota-monitor.massive-scaling.test.ts | 80 + ...ota-monitor.responsive-breakpoints.test.ts | 63 + services/github/quota-monitor.test.ts | 59 + .../quota-monitor.theme-contrast.test.ts | 49 + services/github/quota-monitor.ts | 97 + .../quota-monitor.type-compiler.test.ts | 39 + .../refresh-policy.accessibility.test.ts | 45 + .../refresh-policy.empty-fallback.test.ts | 89 + .../refresh-policy.error-resilience.test.ts | 44 + .../refresh-policy.massive-scaling.test.ts | 95 + .../refresh-policy.mock-integrations.test.ts | 80 + ...refresh-policy.mouse-interactivity.test.ts | 53 + ...resh-policy.responsive-breakpoints.test.ts | 50 + services/github/refresh-policy.test.ts | 62 + .../refresh-policy.theme-contrast.test.ts | 115 + services/github/refresh-policy.ts | 115 + ...refresh-rate-limiter.accessibility.test.ts | 112 + ...efresh-rate-limiter.empty-fallback.test.ts | 94 + ...resh-rate-limiter.error-resilience.test.ts | 81 + ...fresh-rate-limiter.massive-scaling.test.ts | 157 + ...esh-rate-limiter.mock-integrations.test.ts | 79 + ...h-rate-limiter.mouse-interactivity.test.ts | 109 + ...ate-limiter.responsive-breakpoints.test.ts | 75 + services/github/refresh-rate-limiter.test.ts | 78 + ...efresh-rate-limiter.theme-contrast.test.ts | 116 + ...h-rate-limiter.timezone-boundaries.test.ts | 85 + services/github/refresh-rate-limiter.ts | 104 + .../validate-user.accessibility.test.ts | 40 + .../validate-user.empty-fallback.test.ts | 63 + .../validate-user.mock-integrations.test.ts | 67 + .../validate-user.mouse-interactivity.test.ts | 52 + ...lidate-user.responsive-breakpoints.test.ts | 57 + services/github/validate-user.test.ts | 64 + .../validate-user.timezone-boundaries.test.ts | 232 + services/github/validate-user.ts | 65 + .../validate-user.type-compiler.test.ts | 39 + ...rack-user-protection.accessibility.test.ts | 40 + ...ack-user-protection.empty-fallback.test.ts | 66 + ...k-user-protection.error-resilience.test.ts | 56 + ...ck-user-protection.massive-scaling.test.ts | 67 + ...-user-protection.mock-integrations.test.ts | 60 + ...ser-protection.mouse-interactivity.test.ts | 57 + ...ack-user-protection.theme-contrast.test.ts | 38 + ...ser-protection.timezone-boundaries.test.ts | 85 + services/security/track-user-protection.ts | 98 + ...rack-user-protection.type-compiler.test.ts | 44 + types/dashboard.ts | 22 +- types/index.test-d.ts | 62 + types/index.ts | 141 +- types/network.test.ts | 38 + types/network.ts | 26 + types/student.test.ts | 108 + types/student.ts | 52 + utils/cacheControl.test.ts | 24 + utils/cacheControl.ts | 25 + utils/dashboardPeriod.test.ts | 50 + utils/dashboardPeriod.ts | 243 + utils/dateRange.test.ts | 113 + utils/dateRange.ts | 63 + utils/getClientIp.test.ts | 126 + utils/getClientIp.ts | 125 + utils/time.test.ts | 212 + utils/tracking.test.ts | 146 + utils/tracking.ts | 22 +- utils/trustedProxy.test.ts | 33 + utils/trustedProxy.ts | 124 + utils/urls.test.ts | 69 + utils/urls.ts | 28 + vercel-ignore.sh | 5 + vitest.config.ts | 10 +- vitest.setup.ts | 1 + 513 files changed, 70691 insertions(+), 26332 deletions(-) create mode 100644 .gitattributes create mode 100644 .github/scripts/issue-management/unclaim-handler.js create mode 100644 THEME_DEVELOPMENT.md create mode 100644 __mocks__/server-only.js create mode 100644 app/(root)/dashboard/error.test.tsx create mode 100644 app/(root)/dashboard/loading.test.tsx create mode 100644 app/api/compare/githubFetch.test.ts create mode 100644 app/api/compare/route.test.ts create mode 100644 app/api/compare/route.ts create mode 100644 app/api/compare/tests/comparisonStats.test.ts create mode 100644 app/api/compare/tests/validation.test.ts create mode 100644 app/api/notify/route.test.ts create mode 100644 app/api/notify/route.ts create mode 100644 app/api/streak/png/route.test.ts create mode 100644 app/api/streak/png/route.ts create mode 100644 app/api/streak/tests/dateRange.test.ts create mode 100644 app/api/streak/tests/grace.test.ts create mode 100644 app/api/streak/tests/languages.test.ts create mode 100644 app/api/streak/tests/layout.test.ts create mode 100644 app/api/streak/tests/refresh.test.ts create mode 100644 app/api/streak/tests/shading.test.ts create mode 100644 app/api/streak/tests/theme.test.ts create mode 100644 app/api/streak/tests/views.test.ts create mode 100644 app/api/student/resume/confirm/route.ts create mode 100644 app/api/student/resume/tests/confirm.test.ts create mode 100644 app/api/student/resume/upload/route.ts create mode 100644 app/api/user-details/route.test.ts create mode 100644 app/api/user-details/route.ts create mode 100644 app/api/wrapped/route.test.ts create mode 100644 app/api/wrapped/route.ts create mode 100644 app/api/wrapped/tests/statsCalculation.test.ts create mode 100644 app/api/wrapped/tests/validation.test.ts create mode 100644 app/api/wrapped/yearBoundary.test.ts create mode 100644 app/compare/CompareClient.accessibility.test.tsx create mode 100644 app/compare/CompareClient.mouse-interactivity.test.tsx create mode 100644 app/compare/CompareClient.responsive-breakpoints.test.tsx create mode 100644 app/compare/CompareClient.test.tsx create mode 100644 app/compare/CompareClient.tsx create mode 100644 app/compare/CompareClient.type-compiler.test.tsx create mode 100644 app/compare/page.empty-fallback.test.tsx create mode 100644 app/compare/page.mock-integrations.test.tsx create mode 100644 app/compare/page.mouse-interactivity.test.tsx create mode 100644 app/compare/page.responsive-breakpoints.test.tsx create mode 100644 app/compare/page.theme-contrast.test.tsx create mode 100644 app/compare/page.tsx create mode 100644 app/compare/page.type-compiler.test.tsx create mode 100644 app/components/CopyRepoButton.accessibility.test.tsx create mode 100644 app/components/CopyRepoButton.error-resilience.test.tsx create mode 100644 app/components/CopyRepoButton.massive-scaling.test.tsx create mode 100644 app/components/CopyRepoButton.responsive-breakpoints.test.tsx create mode 100644 app/components/CopyRepoButton.theme-contrast.test.tsx create mode 100644 app/components/CopyRepoButton.timezone-boundaries.test.tsx create mode 100644 app/components/CopyRepoButton.tsx create mode 100644 app/components/CopyRepoButton.type-compiler.test.tsx create mode 100644 app/components/Icons.accessibility.test.tsx create mode 100644 app/components/Icons.empty-fallback.test.tsx create mode 100644 app/components/Icons.massive-scaling.test.tsx create mode 100644 app/components/Icons.mock-integrations.test.tsx create mode 100644 app/components/Icons.mouse-interactivity.test.tsx create mode 100644 app/components/Icons.responsive-breakpoints.test.tsx create mode 100644 app/components/Icons.theme-contrast.test.tsx create mode 100644 app/components/Icons.timezone-boundaries.test.tsx create mode 100644 app/components/Icons.type-compiler.test.tsx create mode 100644 app/components/ScrollRestoration.accessibility.test.tsx create mode 100644 app/components/ScrollRestoration.empty-fallback.test.tsx create mode 100644 app/components/ScrollRestoration.error-resilience.test.tsx create mode 100644 app/components/ScrollRestoration.massive-scaling.test.tsx create mode 100644 app/components/ScrollRestoration.mock-integrations.test.tsx create mode 100644 app/components/ScrollRestoration.responsive-breakpoints.test.tsx create mode 100644 app/components/ScrollRestoration.test.tsx create mode 100644 app/components/ScrollRestoration.theme-contrast.test.tsx create mode 100644 app/components/ScrollRestoration.timezone-boundaries.test.tsx create mode 100644 app/components/ScrollRestoration.tsx create mode 100644 app/components/ScrollRestoration.type-compiler.test.tsx create mode 100644 app/components/theme-switch.empty-fallback.test.tsx create mode 100644 app/components/theme-switch.error-resilience.test.tsx create mode 100644 app/components/theme-switch.massive-scaling.test.tsx create mode 100644 app/components/theme-switch.test.tsx create mode 100644 app/components/theme-switch.timezone-boundaries.test.tsx create mode 100644 app/components/theme-switch.tsx create mode 100644 app/components/theme-switch.type-compiler.test.tsx create mode 100644 app/contributors/ContributorsClient.tsx create mode 100644 app/contributors/ContributorsSearch.test.tsx create mode 100644 app/contributors/loading.accessibility.test.tsx create mode 100644 app/contributors/loading.empty-fallback.test.tsx create mode 100644 app/contributors/loading.error-resilience.test.tsx create mode 100644 app/contributors/loading.mock-integrations.test.tsx create mode 100644 app/contributors/loading.responsive-breakpoints.test.tsx create mode 100644 app/contributors/loading.test.tsx create mode 100644 app/contributors/loading.theme-contrast.test.tsx create mode 100644 app/contributors/loading.timezone-boundaries.test.tsx create mode 100644 app/contributors/loading.type-compiler.test.tsx create mode 100644 app/contributors/page.accessibility.test.tsx create mode 100644 app/contributors/page.empty-fallback.test.tsx create mode 100644 app/contributors/page.error-resilience.test.tsx create mode 100644 app/contributors/page.massive-scaling.test.tsx create mode 100644 app/contributors/page.mock-integrations.test.tsx create mode 100644 app/contributors/page.mouse-interactivity.test.tsx create mode 100644 app/contributors/page.responsive-breakpoints.test.tsx create mode 100644 app/contributors/page.theme-contrast.test.tsx create mode 100644 app/contributors/page.timezone-boundaries.test.tsx create mode 100644 app/contributors/page.type-compiler.test.tsx create mode 100644 app/customize/components/AdvancedSettingsPanel.test.tsx create mode 100644 app/customize/components/AdvancedSettingsPanel.tsx create mode 100644 app/customize/components/ControlsPanel.test.tsx create mode 100644 app/customize/components/SectionLabel.test.tsx create mode 100644 app/customize/components/ThemeQuickPresets.css create mode 100644 app/customize/components/ThemeQuickPresets.test.tsx create mode 100644 app/customize/components/ThemeSelector.test.tsx create mode 100644 app/customize/page.test.tsx create mode 100644 app/documentation/code-block.test.tsx delete mode 100644 app/favicon.ico delete mode 100644 app/icon.png create mode 100644 app/not-found.test.tsx create mode 100644 app/template.accessibility.test.tsx create mode 100644 app/template.empty-fallback.test.tsx create mode 100644 app/template.mock-integrations.test.tsx create mode 100644 app/template.responsive-breakpoints.test.tsx create mode 100644 app/template.test.tsx create mode 100644 app/template.theme-contrast.test.tsx create mode 100644 app/template.timezone-boundaries.test.tsx create mode 100644 app/template.type-compiler.test.tsx delete mode 100644 combine.py create mode 100644 components/AnimatedCursor.tsx create mode 100644 components/BrandParticles.test.tsx create mode 100644 components/CherryBlossom.test.tsx create mode 100644 components/DiscordButton.accessibility.test.tsx create mode 100644 components/DiscordButton.error-resilience.test.tsx create mode 100644 components/DiscordButton.mock-integrations.test.tsx create mode 100644 components/DiscordButton.test.tsx create mode 100644 components/DiscordButton.timezone-boundaries.test.tsx create mode 100644 components/DiscordButton.tsx create mode 100644 components/DiscordButton.type-compiler.test.tsx create mode 100644 components/FeatureCards.accessibility.test.tsx create mode 100644 components/FeatureCards.massive-scaling.test.tsx create mode 100644 components/FeatureCards.mock-integrations.test.tsx create mode 100644 components/FeatureCards.mouse-interactivity.test.tsx create mode 100644 components/FeatureCards.responsive-breakpoints.test.tsx create mode 100644 components/FeatureCards.test.tsx create mode 100644 components/FeatureCards.theme-contrast.test.tsx create mode 100644 components/FeatureCards.timezone-boundaries.test.tsx create mode 100644 components/FeatureCards.tsx create mode 100644 components/InteractiveViewer.test.tsx create mode 100644 components/InteractiveViewer.tsx create mode 100644 components/Leaderboard.accessibility.test.tsx create mode 100644 components/Leaderboard.error-resilience.test.tsx create mode 100644 components/Leaderboard.massive-scaling.test.tsx create mode 100644 components/Leaderboard.mock-integrations.test.tsx create mode 100644 components/Leaderboard.mouse-interactivity.test.tsx create mode 100644 components/Leaderboard.responsive-breakpoints.test.tsx create mode 100644 components/Leaderboard.test.tsx create mode 100644 components/Leaderboard.theme-contrast.test.tsx create mode 100644 components/Leaderboard.tsx create mode 100644 components/Leaderboard.type-compiler.test.tsx create mode 100644 components/ReturnToTop.test.tsx create mode 100644 components/ShareButtons.accessibility.test.tsx create mode 100644 components/ShareButtons.error-resilience.test.tsx create mode 100644 components/ShareButtons.massive-scaling.test.tsx create mode 100644 components/ShareButtons.mock-integrations.test.tsx create mode 100644 components/ShareButtons.mouse-interactivity.test.tsx create mode 100644 components/ShareButtons.responsive-breakpoints.test.tsx create mode 100644 components/ShareButtons.theme-contrast.test.tsx create mode 100644 components/ShareButtons.timezone-boundaries.test.tsx create mode 100644 components/ShareButtons.tsx create mode 100644 components/ShareButtons.type-compiler.test.tsx create mode 100644 components/WallOfLove.accessibility.test.tsx create mode 100644 components/WallOfLove.empty-fallback.test.tsx create mode 100644 components/WallOfLove.massive-scaling.test.tsx create mode 100644 components/WallOfLove.test.tsx create mode 100644 components/WallOfLove.tsx create mode 100644 components/commitpulse-logo.accessibility.test.tsx create mode 100644 components/commitpulse-logo.test.tsx create mode 100644 components/dashboard/AIInsightsSkeleton.test.tsx create mode 100644 components/dashboard/AIInsightsSkeleton.tsx create mode 100644 components/dashboard/Achievements.empty-fallback.test.tsx create mode 100644 components/dashboard/AchievementsSkeleton.test.tsx create mode 100644 components/dashboard/ActivityLandscape.test.tsx create mode 100644 components/dashboard/ComparisonStatsCard.test.tsx create mode 100644 components/dashboard/ComparisonStatsCard.tsx create mode 100644 components/dashboard/DashboardClient.test.tsx create mode 100644 components/dashboard/DashboardClient.tsx create mode 100644 components/dashboard/GithubWrapped.test.tsx create mode 100644 components/dashboard/GrowthTrendChart.test.tsx create mode 100644 components/dashboard/GrowthTrendChart.tsx create mode 100644 components/dashboard/HistoricalTrendView.test.tsx create mode 100644 components/dashboard/HistoricalTrendView.timezone-boundaries.test.tsx create mode 100644 components/dashboard/HistoricalTrendView.tsx create mode 100644 components/dashboard/PopularRepos.tsx create mode 100644 components/dashboard/ProfileOptimizerModal.responsive-breakpoints.test.tsx create mode 100644 components/dashboard/ProfileOptimizerModal.test.tsx create mode 100644 components/dashboard/ProfileOptimizerModal.tsx create mode 100644 components/dashboard/RadarChart.test.tsx create mode 100644 components/dashboard/RadarChart.tsx create mode 100644 components/dashboard/RefreshButton.test.tsx create mode 100644 components/dashboard/RepositoryGraph.test.tsx create mode 100644 components/dashboard/RepositoryGraph.tsx create mode 100644 components/dashboard/ResumePreviewForm.accessibility.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.empty-fallback.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.massive-scaling.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.mock-integrations.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.theme-contrast.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.timezone-boundaries.test.tsx create mode 100644 components/dashboard/ResumePreviewForm.tsx create mode 100644 components/dashboard/ResumePreviewForm.type-compiler.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.empty-fallback.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.error-resilience.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.massive-scaling.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.mock-integrations.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.timezone-boundaries.test.tsx create mode 100644 components/dashboard/ResumeProfileSection.tsx create mode 100644 components/dashboard/ResumeProfileSection.type-compiler.test.tsx create mode 100644 components/dashboard/ResumeUpload.accessibility.test.tsx create mode 100644 components/dashboard/ResumeUpload.error-resilience.test.tsx create mode 100644 components/dashboard/ResumeUpload.mock-integrations.test.tsx create mode 100644 components/dashboard/ResumeUpload.responsive-breakpoints.test.tsx create mode 100644 components/dashboard/ResumeUpload.test.tsx create mode 100644 components/dashboard/ResumeUpload.tsx create mode 100644 components/dashboard/StatsCard.utc-disclaimer.test.tsx create mode 100644 components/dashboard/ThemeSelector.test.tsx create mode 100644 components/dashboard/VisualizationTooltip.empty-fallback.test.tsx create mode 100644 components/dashboard/VisualizationTooltip.error-resilience.test.tsx create mode 100644 components/dashboard/VisualizationTooltip.test.tsx create mode 100644 components/dashboard/VisualizationTooltip.tsx create mode 100644 components/dashboard/heatmapUtils.test.ts create mode 100644 components/dashboard/tooltipUtils.empty-fallback.test.ts create mode 100644 components/dashboard/tooltipUtils.test.ts create mode 100644 components/dashboard/tooltipUtils.ts delete mode 100644 files.txt create mode 100644 hooks/useDebounce.test.ts create mode 100644 hooks/useDebounce.ts create mode 100644 hooks/useGlowEffect.test.ts create mode 100644 hooks/useShareActions.test.ts create mode 100644 lib/github.rotation.test.ts create mode 100644 lib/i18n/badgeLabels.test.ts create mode 100644 lib/i18n/languages/en.test.ts create mode 100644 lib/i18n/languages/fr.test.ts create mode 100644 lib/i18n/languages/ja.test.ts create mode 100644 lib/resume-parser.empty-fallback.test.ts create mode 100644 lib/resume-parser.error-resilience.test.ts create mode 100644 lib/resume-parser.massive-scaling.test.ts create mode 100644 lib/resume-parser.mock-integrations.test.ts create mode 100644 lib/resume-parser.mouse-interactivity.test.ts create mode 100644 lib/resume-parser.responsive-breakpoints.test.ts create mode 100644 lib/resume-parser.test.ts create mode 100644 lib/resume-parser.theme-contrast.test.ts create mode 100644 lib/resume-parser.timezone-boundaries.test.ts create mode 100644 lib/resume-parser.ts create mode 100644 lib/resume-parser.type-compiler.test.ts create mode 100644 lib/svg/constants.accessibility.test.ts create mode 100644 lib/svg/constants.empty-fallback.test.ts create mode 100644 lib/svg/constants.error-resilience.test.ts create mode 100644 lib/svg/constants.massive-scaling.test.ts create mode 100644 lib/svg/constants.mock-integrations.test.ts create mode 100644 lib/svg/constants.mouse-interactivity.test.ts create mode 100644 lib/svg/constants.responsive-breakpoints.test.ts create mode 100644 lib/svg/constants.test.ts create mode 100644 lib/svg/constants.theme-contrast.test.ts create mode 100644 lib/svg/constants.timezone-boundaries.test.ts create mode 100644 lib/svg/constants.ts create mode 100644 lib/svg/constants.type-compiler.test.ts create mode 100644 lib/svg/fonts.test.ts create mode 100644 lib/svg/fonts.ts create mode 100644 lib/svg/generator.additional.test.ts create mode 100644 lib/svg/generator.buildTowerPaths.test.ts create mode 100644 lib/svg/generator.escapeXML.test.ts create mode 100644 lib/svg/generator.getSizeScale.test.ts create mode 100644 lib/svg/generator.opacity.test.ts create mode 100644 lib/svg/generator.truncateUsername.test.ts create mode 100644 lib/svg/generatorConstants.test.ts create mode 100644 lib/svg/glacier.test.ts create mode 100644 lib/svg/languageColors.test.ts create mode 100644 lib/svg/layoutConstants.test.ts create mode 100644 lib/svg/lumos.test.ts create mode 100644 lib/svg/nord.test.ts create mode 100644 lib/svg/themes.test.ts create mode 100644 lib/svg/themes/dark.test.ts create mode 100644 lib/svg/themes/forest.test.ts create mode 100644 lib/svg/themes/ocean.test.ts create mode 100644 lib/svg/themes/rose.test.ts create mode 100644 lib/svg/themes/sunset.test.ts create mode 100644 lib/svg/themes/synthwave.test.ts create mode 100644 lib/validate-user.error-resilience.test.ts create mode 100644 lib/validations.githubParamsSchema.test.ts create mode 100644 lib/validations.notifyGetSchema.test.ts create mode 100644 lib/validations.notifyPostSchema.test.ts create mode 100644 lib/validations.ogParamsSchema.test.ts create mode 100644 lib/validations.streakParamsSchema.test.ts create mode 100644 middleware.test.ts create mode 100644 models/Notification.accessibility.test.ts create mode 100644 models/Notification.error-resilience.test.ts create mode 100644 models/Notification.massive-scaling.test.ts create mode 100644 models/Notification.mock-integrations.test.ts create mode 100644 models/Notification.responsive-breakpoints.test.ts create mode 100644 models/Notification.theme-contrast.test.ts create mode 100644 models/Notification.timezone-boundaries.test.ts create mode 100644 models/Notification.ts create mode 100644 models/Notification.type-compiler.test.ts create mode 100644 models/StudentProfile.accessibility.test.ts create mode 100644 models/StudentProfile.empty-fallback.test.ts create mode 100644 models/StudentProfile.massive-scaling.test.ts create mode 100644 models/StudentProfile.mock-integrations.test.ts create mode 100644 models/StudentProfile.mouse-interactivity.test.ts create mode 100644 models/StudentProfile.responsive-breakpoints.test.ts create mode 100644 models/StudentProfile.test.ts create mode 100644 models/StudentProfile.theme-contrast.test.ts create mode 100644 models/StudentProfile.timezone-boundaries.test.ts create mode 100644 models/StudentProfile.ts create mode 100644 models/StudentProfile.type-compiler.test.ts create mode 100644 raw.svg create mode 100644 sanitized.svg create mode 100644 scripts/benchmark-svg.test.ts create mode 100644 services/github/background-refresh.accessibility.test.ts create mode 100644 services/github/background-refresh.error-resilience.test.ts create mode 100644 services/github/background-refresh.massive-scaling.test.ts create mode 100644 services/github/background-refresh.mock-integrations.test.ts create mode 100644 services/github/background-refresh.mouse-interactivity.test.ts create mode 100644 services/github/background-refresh.responsive-breakpoints.test.ts create mode 100644 services/github/background-refresh.theme-contrast.test.ts create mode 100644 services/github/background-refresh.timezone-boundaries.test.ts create mode 100644 services/github/background-refresh.ts create mode 100644 services/github/background-refresh.type-compiler.test.ts create mode 100644 services/github/quota-monitor.accessibility.test.ts create mode 100644 services/github/quota-monitor.empty-fallback.test.ts create mode 100644 services/github/quota-monitor.error-resilience.test.ts create mode 100644 services/github/quota-monitor.massive-scaling.test.ts create mode 100644 services/github/quota-monitor.responsive-breakpoints.test.ts create mode 100644 services/github/quota-monitor.test.ts create mode 100644 services/github/quota-monitor.theme-contrast.test.ts create mode 100644 services/github/quota-monitor.ts create mode 100644 services/github/quota-monitor.type-compiler.test.ts create mode 100644 services/github/refresh-policy.accessibility.test.ts create mode 100644 services/github/refresh-policy.empty-fallback.test.ts create mode 100644 services/github/refresh-policy.error-resilience.test.ts create mode 100644 services/github/refresh-policy.massive-scaling.test.ts create mode 100644 services/github/refresh-policy.mock-integrations.test.ts create mode 100644 services/github/refresh-policy.mouse-interactivity.test.ts create mode 100644 services/github/refresh-policy.responsive-breakpoints.test.ts create mode 100644 services/github/refresh-policy.test.ts create mode 100644 services/github/refresh-policy.theme-contrast.test.ts create mode 100644 services/github/refresh-policy.ts create mode 100644 services/github/refresh-rate-limiter.accessibility.test.ts create mode 100644 services/github/refresh-rate-limiter.empty-fallback.test.ts create mode 100644 services/github/refresh-rate-limiter.error-resilience.test.ts create mode 100644 services/github/refresh-rate-limiter.massive-scaling.test.ts create mode 100644 services/github/refresh-rate-limiter.mock-integrations.test.ts create mode 100644 services/github/refresh-rate-limiter.mouse-interactivity.test.ts create mode 100644 services/github/refresh-rate-limiter.responsive-breakpoints.test.ts create mode 100644 services/github/refresh-rate-limiter.test.ts create mode 100644 services/github/refresh-rate-limiter.theme-contrast.test.ts create mode 100644 services/github/refresh-rate-limiter.timezone-boundaries.test.ts create mode 100644 services/github/refresh-rate-limiter.ts create mode 100644 services/github/validate-user.accessibility.test.ts create mode 100644 services/github/validate-user.empty-fallback.test.ts create mode 100644 services/github/validate-user.mock-integrations.test.ts create mode 100644 services/github/validate-user.mouse-interactivity.test.ts create mode 100644 services/github/validate-user.responsive-breakpoints.test.ts create mode 100644 services/github/validate-user.test.ts create mode 100644 services/github/validate-user.timezone-boundaries.test.ts create mode 100644 services/github/validate-user.ts create mode 100644 services/github/validate-user.type-compiler.test.ts create mode 100644 services/security/track-user-protection.accessibility.test.ts create mode 100644 services/security/track-user-protection.empty-fallback.test.ts create mode 100644 services/security/track-user-protection.error-resilience.test.ts create mode 100644 services/security/track-user-protection.massive-scaling.test.ts create mode 100644 services/security/track-user-protection.mock-integrations.test.ts create mode 100644 services/security/track-user-protection.mouse-interactivity.test.ts create mode 100644 services/security/track-user-protection.theme-contrast.test.ts create mode 100644 services/security/track-user-protection.timezone-boundaries.test.ts create mode 100644 services/security/track-user-protection.ts create mode 100644 services/security/track-user-protection.type-compiler.test.ts create mode 100644 types/index.test-d.ts create mode 100644 types/network.test.ts create mode 100644 types/network.ts create mode 100644 types/student.test.ts create mode 100644 types/student.ts create mode 100644 utils/cacheControl.test.ts create mode 100644 utils/cacheControl.ts create mode 100644 utils/dashboardPeriod.test.ts create mode 100644 utils/dashboardPeriod.ts create mode 100644 utils/dateRange.test.ts create mode 100644 utils/dateRange.ts create mode 100644 utils/getClientIp.test.ts create mode 100644 utils/getClientIp.ts create mode 100644 utils/trustedProxy.test.ts create mode 100644 utils/trustedProxy.ts create mode 100644 utils/urls.test.ts create mode 100644 utils/urls.ts create mode 100644 vitest.setup.ts diff --git a/.env.local.example b/.env.local.example index e786d4feb..c7a6bf7db 100644 --- a/.env.local.example +++ b/.env.local.example @@ -20,8 +20,7 @@ # Generate new token (classic) # Required scope: read:user # ------------------------------------------------------------ -GITHUB_TOKEN=ghp_your_personal_access_token_here - +GITHUB_TOKEN= # ------------------------------------------------------------ # REQUIRED — Public site URL @@ -48,3 +47,19 @@ NEXT_PUBLIC_SITE_URL=http://localhost:3000 # 4. Replace with your DB user's password # ------------------------------------------------------------ # MONGODB_URI=mongodb+srv://:@cluster.mongodb.net/commitpulse?retryWrites=true&w=majority + +# ------------------------------------------------------------ +# OPTIONAL — Upstash Redis / Vercel KV (Distributed Rate Limiting) +# ------------------------------------------------------------ +# Enables distributed rate limiting across all serverless instances. +# Without these, rate limiting is per-instance (each Vercel function +# gets its own counter), which allows N× the intended limit under load. +# +# How to get one (free): +# 1. Create a free Upstash Redis database at https://console.upstash.com +# 2. Copy the REST API URL and token +# 3. If using Vercel KV, link it to your project in the Vercel dashboard +# — it sets KV_REST_API_URL and KV_REST_API_TOKEN automatically. +# ------------------------------------------------------------ +# KV_REST_API_URL=https://.upstash.io +# KV_REST_API_TOKEN= diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..94f480de9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.github/scripts/check-duplicates.js b/.github/scripts/check-duplicates.js index 7783491d4..884cf3e91 100644 --- a/.github/scripts/check-duplicates.js +++ b/.github/scripts/check-duplicates.js @@ -18,13 +18,13 @@ function cosineSimilarity(vecA, vecB) { async function run() { const geminiApiKey = process.env.GEMINI_API_KEY; - const githubToken = process.env.GITHUB_TOKEN; + const githubToken = process.env.GITHUB_PAT || process.env.GITHUB_TOKEN; if (!geminiApiKey) { throw new Error('❌ Missing GEMINI_API_KEY environment variable.'); } if (!githubToken) { - throw new Error('❌ Missing GITHUB_TOKEN environment variable.'); + throw new Error('❌ Missing GITHUB_PAT or GITHUB_TOKEN environment variable.'); } const octokit = github.getOctokit(githubToken); diff --git a/.github/scripts/issue-management/assign-handler.js b/.github/scripts/issue-management/assign-handler.js index 575dfd41c..160d85266 100644 --- a/.github/scripts/issue-management/assign-handler.js +++ b/.github/scripts/issue-management/assign-handler.js @@ -10,7 +10,7 @@ async function findExistingAssignments(github, owner, repo, username, currentIss return issues.filter((issue) => !issue.pull_request && issue.number !== currentIssueNumber); } -const MAX_ASSIGNED_ISSUES = 3; +const MAX_ASSIGNED_ISSUES = 5; async function handleAssign({ github, context, username, hasWriteAccess }) { const { owner, repo } = context.repo; diff --git a/.github/scripts/issue-management/claim-handler.js b/.github/scripts/issue-management/claim-handler.js index 08996afec..5d15efcb1 100644 --- a/.github/scripts/issue-management/claim-handler.js +++ b/.github/scripts/issue-management/claim-handler.js @@ -10,7 +10,7 @@ async function findExistingAssignments(github, owner, repo, username, currentIss return issues.filter((issue) => !issue.pull_request && issue.number !== currentIssueNumber); } -const MAX_ASSIGNED_ISSUES = 3; +const MAX_ASSIGNED_ISSUES = 5; async function handleClaim({ github, context }) { const { owner, repo } = context.repo; @@ -30,9 +30,10 @@ async function handleClaim({ github, context }) { const issueAuthor = context.payload.issue.user.login; - const isAuthorJhasourav07 = issueAuthor.toLowerCase() === 'jhasourav07'; + const MAINTAINERS = ['jhasourav07', 'aamod007', 'souravjhahind']; + const isOpenedByMaintainer = MAINTAINERS.includes(issueAuthor.toLowerCase()); - if (!isAuthorJhasourav07 && commenter.toLowerCase() !== issueAuthor.toLowerCase()) { + if (!isOpenedByMaintainer && commenter.toLowerCase() !== issueAuthor.toLowerCase()) { await github.rest.issues.createComment({ owner, repo, @@ -73,7 +74,7 @@ async function handleClaim({ github, context }) { owner, repo, issue_number: issueNumber, - body: `❌ You already have **${existingIssues.length}/${MAX_ASSIGNED_ISSUES}** active assigned issues (the maximum allowed).\nPlease complete or unassign one of your current issues before claiming another.\n\n${issueList}`, + body: `❌ You already have **${existingIssues.length}/${MAX_ASSIGNED_ISSUES}** active assigned issues (the maximum allowed).\nPlease complete or \`/unclaim\` one of your current issues before claiming another.\n\n${issueList}`, }); return; } @@ -89,7 +90,7 @@ async function handleClaim({ github, context }) { owner, repo, issue_number: issueNumber, - body: `🎉 **Assigned!** Welcome to the project, @${commenter}.\n\n⏳ **Reminder:** You have **3 days** to submit a Pull Request. After 3 days of inactivity, you will be automatically unassigned to give others a chance (as per our GSSoC anti-hoarding policy).\n\n> 💡 Please read [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md) if you haven't already.\n\nHappy coding! 🚀`, + body: `🎉 **Assigned!** Welcome to the project, @${commenter}.\n\n⏳ **Reminder:** You have **2 days** to submit a Pull Request. After 2 days of inactivity, you will be automatically unassigned to give others a chance (as per our GSSoC anti-hoarding policy).\n\n> 💡 Please read [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md) if you haven't already.\n\nHappy coding! 🚀`, }); } diff --git a/.github/scripts/issue-management/main.js b/.github/scripts/issue-management/main.js index 5485b28de..407cb8fe2 100644 --- a/.github/scripts/issue-management/main.js +++ b/.github/scripts/issue-management/main.js @@ -4,6 +4,7 @@ const { handleAssign } = require('./assign-handler'); const { handleUnassign } = require('./unassign-handler'); const { handleAddLabel } = require('./addlabel-handler'); const { handleClaim } = require('./claim-handler'); +const { handleUnclaim } = require('./unclaim-handler'); module.exports = async ({ github, context, core }) => { const commentBody = context.payload.comment?.body; @@ -48,6 +49,9 @@ module.exports = async ({ github, context, core }) => { case 'claim': await handleClaim({ github, context }); break; + case 'unclaim': + await handleUnclaim({ github, context }); + break; case 'ping': await github.rest.issues.createComment({ owner, diff --git a/.github/scripts/issue-management/parse-command.js b/.github/scripts/issue-management/parse-command.js index cc72ad3b7..f29fd8c50 100644 --- a/.github/scripts/issue-management/parse-command.js +++ b/.github/scripts/issue-management/parse-command.js @@ -15,6 +15,9 @@ function parseCommand(commentBody) { const claimMatch = line.match(/^\/claim\s*$/i); if (claimMatch) return { command: 'claim' }; + const unclaimMatch = line.match(/^\/unclaim\s*$/i); + if (unclaimMatch) return { command: 'unclaim' }; + const pingMatch = line.match(/^\/ping\s*$/i); if (pingMatch) return { command: 'ping' }; diff --git a/.github/scripts/issue-management/stale-assignment.js b/.github/scripts/issue-management/stale-assignment.js index a4ab6e4a9..33ddae7aa 100644 --- a/.github/scripts/issue-management/stale-assignment.js +++ b/.github/scripts/issue-management/stale-assignment.js @@ -1,6 +1,6 @@ async function handleStaleAssignments({ github, context, core }) { const { owner, repo } = context.repo; - const THREE_DAYS_MS = 3 * 24 * 60 * 60 * 1000; + const TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1000; const now = new Date(); console.log(`Starting stale assignment check for ${owner}/${repo}`); @@ -27,7 +27,7 @@ async function handleStaleAssignments({ github, context, core }) { const updatedAt = new Date(issue.updated_at); const timeSinceUpdate = now.getTime() - updatedAt.getTime(); - if (timeSinceUpdate > THREE_DAYS_MS) { + if (timeSinceUpdate > TWO_DAYS_MS) { console.log( `Issue #${issue.number} has been inactive since ${issue.updated_at}. Removing assignees.` ); diff --git a/.github/scripts/issue-management/unclaim-handler.js b/.github/scripts/issue-management/unclaim-handler.js new file mode 100644 index 000000000..0d39d2c68 --- /dev/null +++ b/.github/scripts/issue-management/unclaim-handler.js @@ -0,0 +1,44 @@ +async function handleUnclaim({ github, context }) { + const { owner, repo } = context.repo; + const issueNumber = context.payload.issue.number; + const commenter = context.payload.comment.user.login; + const issueState = context.payload.issue.state; + + if (issueState === 'closed') { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issueNumber, + body: `❌ Commands cannot be used on closed issues.`, + }); + return; + } + + const currentAssignees = context.payload.issue.assignees.map((a) => a.login.toLowerCase()); + + if (!currentAssignees.includes(commenter.toLowerCase())) { + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issueNumber, + body: `ℹ️ @${commenter}, you are not currently assigned to this issue, so there's nothing to unclaim.`, + }); + return; + } + + await github.rest.issues.removeAssignees({ + owner, + repo, + issue_number: issueNumber, + assignees: [commenter], + }); + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: issueNumber, + body: `✅ Successfully unclaimed this issue for @${commenter}.\n\n> 🔓 The issue is now open for others to claim.`, + }); +} + +module.exports = { handleUnclaim }; diff --git a/.github/workflows/assign-request-reminder.yml b/.github/workflows/assign-request-reminder.yml index 688f379a6..0f0c6ddb1 100644 --- a/.github/workflows/assign-request-reminder.yml +++ b/.github/workflows/assign-request-reminder.yml @@ -42,7 +42,7 @@ jobs: contains(github.event.comment.body, 'can i be assigned') || contains(github.event.comment.body, 'assign me to this') || contains(github.event.comment.body, 'assign me this') || - contains(github.event.comment.body, 'i will work on this') || + contains(github.event.comment.body, 'work on this') || contains(github.event.comment.body, 'i''ll work on this') || contains(github.event.comment.body, 'ill work on this') || contains(github.event.comment.body, 'i can work on this') || @@ -93,7 +93,7 @@ jobs: ``, `## 📋 A Few Things to Know`, ``, - `- You can hold a **maximum of 3 open issues** at a time.`, + `- You can hold a **maximum of 5 open issues** at a time.`, `- If there's **no activity for 3 days**, the assignment will automatically expire so others can pick it up.`, `- Make sure to read our **[CONTRIBUTING.md](https://github.com/${owner}/${repo}/blob/main/CONTRIBUTING.md)** before you start — it covers code style, commit conventions, and the PR checklist.`, ``, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db8226bd8..d3cb92f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: branches: [main, dev] pull_request: branches: [main, dev] + merge_group: # Run CI on the merged commit before it lands on main jobs: quality: diff --git a/.github/workflows/conflict-notifier.yml b/.github/workflows/conflict-notifier.yml index f46e3e421..cec63e94a 100644 --- a/.github/workflows/conflict-notifier.yml +++ b/.github/workflows/conflict-notifier.yml @@ -2,7 +2,12 @@ name: Merge Conflict Notifier on: schedule: - - cron: '0 * * * *' + - cron: '*/30 * * * *' # Hourly/half-hourly fallback + push: + branches: + - main + pull_request_target: + types: [synchronize, reopened, edited] workflow_dispatch: permissions: @@ -74,22 +79,8 @@ jobs: issue_number: pr.number, labels: [label], }); - } - - // Check if we've already posted a conflict comment to avoid spam - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - per_page: 100, - }); - - const alreadyCommented = comments.some(c => - c.user?.login === 'github-actions[bot]' && - c.body?.includes('merge conflicts with the main branch') - ); - if (!alreadyCommented) { + // Post notification comment ONLY ONCE (when the label is first applied) await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/issue-management.yml b/.github/workflows/issue-management.yml index c9aacf091..5f0a44866 100644 --- a/.github/workflows/issue-management.yml +++ b/.github/workflows/issue-management.yml @@ -22,7 +22,8 @@ jobs: contains(github.event.comment.body, '/assign') || contains(github.event.comment.body, '/unassign') || contains(github.event.comment.body, '/addlabel') || - contains(github.event.comment.body, '/claim') + contains(github.event.comment.body, '/claim') || + contains(github.event.comment.body, '/unclaim') ) steps: - name: Checkout Repository diff --git a/.github/workflows/pr-issue-check.yml b/.github/workflows/pr-issue-check.yml index 8c136df18..15ed4de11 100644 --- a/.github/workflows/pr-issue-check.yml +++ b/.github/workflows/pr-issue-check.yml @@ -182,7 +182,7 @@ jobs: 2. **Wait for confirmation** — The bot will confirm your assignment with a ✅ reply. 3. **Then open your PR** — Link the issue with \`Fixes #${issueNumber}\` in your description. - > 💡 You can be assigned to up to **3** open issues at a time. Check your current assignments before claiming a new one. + > 💡 You can be assigned to up to **5** open issues at a time. Check your current assignments before claiming a new one. We look forward to your contribution once you're assigned! 🚀`, }); diff --git a/.gitignore b/.gitignore index f67054a02..ca096d8e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,42 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files — ignore all local secrets but commit the example template -.env* -!.env.local.example -.env.local -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files — ignore all local secrets but commit the example template +.env* +!.env.local.example +.env.local +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 25df2ad0c..eee1a628b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ - [The Standard We Hold](#-the-standard-we-hold) - [Local Setup](#-local-setup) +- [Testing & Previewing Your Changes](#-testing--previewing-your-changes) - [What to Contribute](#-what-to-contribute) - [Automated Issue Management & Claiming](#-automated-issue-management--claiming) - [Branch & Commit Conventions](#-branch--commit-conventions) @@ -88,6 +89,200 @@ http://localhost:3000/api/streak?user=YOUR_GITHUB_USERNAME --- +## 🧪 Testing & Previewing Your Changes + +Before opening a PR, every contributor is expected to verify their changes locally across three areas: **visual SVG output**, **unit tests**, and **branch coverage**. This section explains how to do all three. + +### 1. Visual Browser Preview + +With your dev server running (`npm run dev`), open your browser and visit these URLs to preview the SVG output directly — **open them as raw URLs, not embedded in an `` tag or Markdown preview**: + +**Standard badge — valid username:** + +``` +http://localhost:3000/api/streak?user=YOUR_GITHUB_USERNAME +``` + +**Monthly view:** + +``` +http://localhost:3000/api/streak?user=YOUR_GITHUB_USERNAME&view=monthly +``` + +**Custom theme — test your color changes:** + +``` +http://localhost:3000/api/streak?user=YOUR_GITHUB_USERNAME&theme=YOUR_THEME_NAME +``` + +**Invalid username — must render a styled SVG error card, not raw JSON:** + +``` +http://localhost:3000/api/streak?user=vivek%20Sangani +``` + +**Non-existent username — must render the ghost-city not-found badge:** + +``` +http://localhost:3000/api/streak?user=xyzabc999notreallll +``` + +**Useful DevTools tricks:** + +- **Inspect SVG structure:** Open DevTools (`Cmd+Option+I` / `F12`) → Elements panel → expand the `` node to inspect every path, rect, and text element +- **Force a fresh fetch:** Add `&refresh=true` to bypass the cache: `http://localhost:3000/api/streak?user=YOUR_GITHUB_USERNAME&refresh=true` +- **Test different params live:** `?theme=obsidian`, `?size=large`, `?grace=2`, `?autoTheme=true` + +> **⚠️ Browser XML error warning:** If your browser shows an `EntityRef: expecting ';'` error instead of rendering the SVG, it means an unescaped `&` character exists somewhere in the SVG output. All `&` characters inside SVG `