@stats-organization/github-readme-stats-core 2.1.4 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/build/api/gist.d.ts.map +1 -1
  2. package/build/api/gist.js +34 -0
  3. package/build/api/index.d.ts.map +1 -1
  4. package/build/api/index.js +18 -0
  5. package/build/api/pin.d.ts.map +1 -1
  6. package/build/api/pin.js +17 -0
  7. package/build/api/top-langs.d.ts.map +1 -1
  8. package/build/api/top-langs.js +36 -4
  9. package/build/api/wakatime.d.ts.map +1 -1
  10. package/build/api/wakatime.js +34 -0
  11. package/build/calculateRank.d.ts +12 -11
  12. package/build/calculateRank.d.ts.map +1 -1
  13. package/build/calculateRank.js +18 -18
  14. package/build/cards/repo.d.ts.map +1 -1
  15. package/build/cards/repo.js +29 -20
  16. package/build/cards/stats.d.ts +4 -2
  17. package/build/cards/stats.d.ts.map +1 -1
  18. package/build/cards/stats.js +21 -8
  19. package/build/cards/top-languages.d.ts.map +1 -1
  20. package/build/cards/top-languages.js +39 -11
  21. package/build/cards/wakatime.d.ts.map +1 -1
  22. package/build/cards/wakatime.js +29 -8
  23. package/build/common/Card.d.ts +9 -3
  24. package/build/common/Card.d.ts.map +1 -1
  25. package/build/common/Card.js +47 -19
  26. package/build/common/color.d.ts +42 -8
  27. package/build/common/color.d.ts.map +1 -1
  28. package/build/common/color.js +62 -13
  29. package/build/common/html.js +1 -1
  30. package/build/common/http.d.ts +13 -4
  31. package/build/common/http.d.ts.map +1 -1
  32. package/build/common/http.js +3 -3
  33. package/build/common/languageColors.json +9 -1
  34. package/build/common/ops.d.ts +30 -29
  35. package/build/common/ops.d.ts.map +1 -1
  36. package/build/common/ops.js +32 -37
  37. package/build/common/render.d.ts +115 -111
  38. package/build/common/render.d.ts.map +1 -1
  39. package/build/common/render.js +138 -87
  40. package/build/common/retryer.d.ts +14 -6
  41. package/build/common/retryer.d.ts.map +1 -1
  42. package/build/common/retryer.js +1 -0
  43. package/build/fetchers/gist.d.ts +6 -21
  44. package/build/fetchers/gist.d.ts.map +1 -1
  45. package/build/fetchers/gist.js +30 -35
  46. package/build/fetchers/repo.js +1 -1
  47. package/build/fetchers/stats.d.ts.map +1 -1
  48. package/build/fetchers/stats.js +0 -1
  49. package/build/fetchers/types.d.ts +126 -0
  50. package/build/fetchers/types.d.ts.map +1 -0
  51. package/build/fetchers/types.js +1 -0
  52. package/package.json +4 -4
  53. package/src/_emoji-name-map.d.ts +10 -0
  54. package/src/api/gist.js +36 -0
  55. package/src/api/index.js +19 -0
  56. package/src/api/pin.js +18 -0
  57. package/src/api/top-langs.js +38 -4
  58. package/src/api/wakatime.js +36 -0
  59. package/src/{calculateRank.js → calculateRank.ts} +29 -19
  60. package/src/cards/repo.js +30 -20
  61. package/src/cards/stats.js +22 -8
  62. package/src/cards/top-languages.js +49 -11
  63. package/src/cards/wakatime.js +33 -8
  64. package/src/common/Card.ts +51 -16
  65. package/src/common/color.ts +85 -22
  66. package/src/common/html.ts +1 -1
  67. package/src/common/http.ts +31 -0
  68. package/src/common/languageColors.json +9 -1
  69. package/src/common/{ops.js → ops.ts} +48 -50
  70. package/src/common/{render.js → render.ts} +208 -96
  71. package/src/common/retryer.ts +18 -10
  72. package/src/fetchers/gist.ts +136 -0
  73. package/src/fetchers/repo.js +1 -1
  74. package/src/fetchers/stats.js +0 -1
  75. package/src/common/http.js +0 -19
  76. package/src/fetchers/gist.js +0 -112
  77. /package/src/fetchers/{types.d.ts → types.ts} +0 -0
@@ -0,0 +1,126 @@
1
+ export interface GistData {
2
+ name: string;
3
+ nameWithOwner: string;
4
+ description: string | null;
5
+ language: string | null;
6
+ starsCount: number;
7
+ forksCount: number;
8
+ }
9
+ export interface RepositoryData {
10
+ name: string;
11
+ nameWithOwner: string;
12
+ isPrivate: boolean;
13
+ isArchived: boolean;
14
+ isTemplate: boolean;
15
+ stargazerCount: number;
16
+ description: string;
17
+ primaryLanguage: {
18
+ color: string;
19
+ id: string;
20
+ name: string;
21
+ };
22
+ forkCount: number;
23
+ starCount: number;
24
+ totalPRsAuthored: number;
25
+ totalPRsCommented: number;
26
+ totalPRsReviewed: number;
27
+ totalIssuesAuthored: number;
28
+ totalIssuesCommented: number;
29
+ }
30
+ export interface StatsData {
31
+ name: string;
32
+ totalPRs: number;
33
+ totalPRsMerged: number;
34
+ mergedPRsPercentage: number;
35
+ totalReviews: number;
36
+ totalCommits: number;
37
+ totalIssues: number;
38
+ totalStars: number;
39
+ totalDiscussionsStarted: number;
40
+ totalDiscussionsAnswered: number;
41
+ contributedTo: number;
42
+ totalPRsAuthored: number;
43
+ totalPRsCommented: number;
44
+ totalPRsReviewed: number;
45
+ totalIssuesAuthored: number;
46
+ totalIssuesCommented: number;
47
+ rank: {
48
+ level: string;
49
+ percentile: number;
50
+ };
51
+ }
52
+ export interface Lang {
53
+ name: string;
54
+ color: string;
55
+ size: number;
56
+ }
57
+ export type TopLangData = Record<string, Lang>;
58
+ export interface WakaTimeData {
59
+ categories: Array<{
60
+ digital: string;
61
+ hours: number;
62
+ minutes: number;
63
+ name: string;
64
+ percent: number;
65
+ text: string;
66
+ total_seconds: number;
67
+ }>;
68
+ daily_average: number;
69
+ daily_average_including_other_language: number;
70
+ days_including_holidays: number;
71
+ days_minus_holidays: number;
72
+ editors: Array<{
73
+ digital: string;
74
+ hours: number;
75
+ minutes: number;
76
+ name: string;
77
+ percent: number;
78
+ text: string;
79
+ total_seconds: number;
80
+ }>;
81
+ holidays: number;
82
+ human_readable_daily_average: string;
83
+ human_readable_daily_average_including_other_language: string;
84
+ human_readable_total: string;
85
+ human_readable_total_including_other_language: string;
86
+ id: string;
87
+ is_already_updating: boolean;
88
+ is_coding_activity_visible: boolean;
89
+ is_including_today: boolean;
90
+ is_other_usage_visible: boolean;
91
+ is_stuck: boolean;
92
+ is_up_to_date: boolean;
93
+ languages: Array<{
94
+ digital: string;
95
+ hours: number;
96
+ minutes: number;
97
+ name: string;
98
+ percent: number;
99
+ text: string;
100
+ total_seconds: number;
101
+ }>;
102
+ operating_systems: Array<{
103
+ digital: string;
104
+ hours: number;
105
+ minutes: number;
106
+ name: string;
107
+ percent: number;
108
+ text: string;
109
+ total_seconds: number;
110
+ }>;
111
+ percent_calculated: number;
112
+ range: string;
113
+ status: string;
114
+ timeout: number;
115
+ total_seconds: number;
116
+ total_seconds_including_other_language: number;
117
+ user_id: string;
118
+ username: string;
119
+ writes_only: boolean;
120
+ }
121
+ export interface WakaTimeLang {
122
+ name: string;
123
+ text: string;
124
+ percent: number;
125
+ }
126
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fetchers/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stats-organization/github-readme-stats-core",
3
- "version": "2.1.4",
3
+ "version": "2.1.5",
4
4
  "type": "module",
5
5
  "homepage": "https://github-stats-extended.vercel.app/frontend",
6
6
  "bugs": {
@@ -71,12 +71,12 @@
71
71
  "@testing-library/jest-dom": "6.9.1",
72
72
  "@uppercod/css-to-object": "1.1.1",
73
73
  "axios-mock-adapter": "2.1.0",
74
- "js-yaml": "4.2.0",
74
+ "js-yaml": "5.2.1",
75
75
  "jsdom": "29.1.1",
76
- "vitest": "4.1.8"
76
+ "vitest": "4.1.10"
77
77
  },
78
78
  "dependencies": {
79
- "axios": "^1.17.0",
79
+ "axios": "^1.18.1",
80
80
  "emoji-name-map": "^2.0.3",
81
81
  "github-username-regex": "^1.0.0"
82
82
  },
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `emoji-name-map` ships no type definitions and exposes no `exports` map, so it
3
+ * cannot be resolved under `nodenext`. Declare the minimal surface used here.
4
+ */
5
+ declare module "emoji-name-map" {
6
+ const emojiNameMap: {
7
+ get(name: string): string | undefined;
8
+ };
9
+ export default emojiNameMap;
10
+ }
package/src/api/gist.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { renderGistCard } from "../cards/gist.js";
2
+ import { findInvalidColor } from "../common/color.js";
2
3
  import {
3
4
  MissingParamError,
4
5
  retrieveSecondaryMessage,
@@ -26,6 +27,23 @@ export default async (
26
27
  },
27
28
  pat = null,
28
29
  ) => {
30
+ const invalidColorInput = findInvalidColor({
31
+ title_color,
32
+ icon_color,
33
+ text_color,
34
+ bg_color,
35
+ border_color,
36
+ });
37
+ if (invalidColorInput) {
38
+ return {
39
+ status: "error - permanent",
40
+ content: renderError({
41
+ message: "Something went wrong",
42
+ secondaryMessage: `Invalid color input for parameter "${invalidColorInput}"`,
43
+ }),
44
+ };
45
+ }
46
+
29
47
  if (locale && !isLocaleAvailable(locale)) {
30
48
  return {
31
49
  status: "error - permanent",
@@ -43,6 +61,24 @@ export default async (
43
61
  };
44
62
  }
45
63
 
64
+ const safePattern = /^[-\w/.,]+$/;
65
+ if (id && !safePattern.test(id)) {
66
+ return {
67
+ status: "error - permanent",
68
+ content: renderError({
69
+ message: "Something went wrong",
70
+ secondaryMessage: "Gist ID contains unsafe characters",
71
+ renderOptions: {
72
+ title_color,
73
+ text_color,
74
+ bg_color,
75
+ border_color,
76
+ theme,
77
+ },
78
+ }),
79
+ };
80
+ }
81
+
46
82
  try {
47
83
  const gistData = await fetchGist(id, pat);
48
84
 
package/src/api/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { renderStatsCard } from "../cards/stats.js";
2
+ import { findInvalidColor } from "../common/color.js";
2
3
  import {
3
4
  MissingParamError,
4
5
  retrieveSecondaryMessage,
@@ -44,6 +45,24 @@ export default async (
44
45
  },
45
46
  pat = null,
46
47
  ) => {
48
+ const invalidColorInput = findInvalidColor({
49
+ title_color,
50
+ ring_color,
51
+ icon_color,
52
+ text_color,
53
+ bg_color,
54
+ border_color,
55
+ });
56
+ if (invalidColorInput) {
57
+ return {
58
+ status: "error - permanent",
59
+ content: renderError({
60
+ message: "Something went wrong",
61
+ secondaryMessage: `Invalid color input for parameter "${invalidColorInput}"`,
62
+ }),
63
+ };
64
+ }
65
+
47
66
  if (locale && !isLocaleAvailable(locale)) {
48
67
  return {
49
68
  status: "error - permanent",
package/src/api/pin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { renderRepoCard } from "../cards/repo.js";
2
+ import { findInvalidColor } from "../common/color.js";
2
3
  import {
3
4
  MissingParamError,
4
5
  retrieveSecondaryMessage,
@@ -34,6 +35,23 @@ export default async (
34
35
  },
35
36
  pat = null,
36
37
  ) => {
38
+ const invalidColorInput = findInvalidColor({
39
+ title_color,
40
+ icon_color,
41
+ text_color,
42
+ bg_color,
43
+ border_color,
44
+ });
45
+ if (invalidColorInput) {
46
+ return {
47
+ status: "error - permanent",
48
+ content: renderError({
49
+ message: "Something went wrong",
50
+ secondaryMessage: `Invalid color input for parameter "${invalidColorInput}"`,
51
+ }),
52
+ };
53
+ }
54
+
37
55
  if (locale && !isLocaleAvailable(locale)) {
38
56
  return {
39
57
  status: "error - permanent",
@@ -1,4 +1,5 @@
1
1
  import { renderTopLanguages } from "../cards/top-languages.js";
2
+ import { findInvalidColor } from "../common/color.js";
2
3
  import {
3
4
  MissingParamError,
4
5
  retrieveSecondaryMessage,
@@ -38,6 +39,23 @@ export default async (
38
39
  },
39
40
  pat = null,
40
41
  ) => {
42
+ const invalidColorInput = findInvalidColor({
43
+ title_color,
44
+ text_color,
45
+ bg_color,
46
+ prog_bar_bg_color,
47
+ border_color,
48
+ });
49
+ if (invalidColorInput) {
50
+ return {
51
+ status: "error - permanent",
52
+ content: renderError({
53
+ message: "Something went wrong",
54
+ secondaryMessage: `Invalid color input for parameter "${invalidColorInput}"`,
55
+ }),
56
+ };
57
+ }
58
+
41
59
  if (locale && !isLocaleAvailable(locale)) {
42
60
  return {
43
61
  status: "error - permanent",
@@ -55,10 +73,27 @@ export default async (
55
73
  };
56
74
  }
57
75
 
76
+ const safePattern = /^[-\w/.,]+$/;
77
+ if (username && !safePattern.test(username)) {
78
+ return {
79
+ status: "error - permanent",
80
+ content: renderError({
81
+ message: "Something went wrong",
82
+ secondaryMessage: "Username contains unsafe characters",
83
+ renderOptions: {
84
+ title_color,
85
+ text_color,
86
+ bg_color,
87
+ border_color,
88
+ theme,
89
+ },
90
+ }),
91
+ };
92
+ }
93
+
58
94
  if (
59
95
  layout !== undefined &&
60
- (typeof layout !== "string" ||
61
- !["compact", "normal", "donut", "donut-vertical", "pie"].includes(layout))
96
+ !["compact", "normal", "donut", "donut-vertical", "pie"].includes(layout)
62
97
  ) {
63
98
  return {
64
99
  status: "error - permanent",
@@ -78,8 +113,7 @@ export default async (
78
113
 
79
114
  if (
80
115
  stats_format !== undefined &&
81
- (typeof stats_format !== "string" ||
82
- !["bytes", "percentages"].includes(stats_format))
116
+ !["bytes", "percentages"].includes(stats_format)
83
117
  ) {
84
118
  return {
85
119
  status: "error - permanent",
@@ -1,4 +1,5 @@
1
1
  import { renderWakatimeCard } from "../cards/wakatime.js";
2
+ import { findInvalidColor } from "../common/color.js";
2
3
  import {
3
4
  MissingParamError,
4
5
  retrieveSecondaryMessage,
@@ -32,6 +33,23 @@ export default async ({
32
33
  display_format,
33
34
  disable_animations,
34
35
  }) => {
36
+ const invalidColorInput = findInvalidColor({
37
+ title_color,
38
+ icon_color,
39
+ text_color,
40
+ bg_color,
41
+ border_color,
42
+ });
43
+ if (invalidColorInput) {
44
+ return {
45
+ status: "error - permanent",
46
+ content: renderError({
47
+ message: "Something went wrong",
48
+ secondaryMessage: `Invalid color input for parameter "${invalidColorInput}"`,
49
+ }),
50
+ };
51
+ }
52
+
35
53
  if (locale && !isLocaleAvailable(locale)) {
36
54
  return {
37
55
  status: "error - permanent",
@@ -49,6 +67,24 @@ export default async ({
49
67
  };
50
68
  }
51
69
 
70
+ const safePattern = /^[-\w/.,]+$/;
71
+ if (username && !safePattern.test(username)) {
72
+ return {
73
+ status: "error - permanent",
74
+ content: renderError({
75
+ message: "Something went wrong",
76
+ secondaryMessage: "Username contains unsafe characters",
77
+ renderOptions: {
78
+ title_color,
79
+ text_color,
80
+ bg_color,
81
+ border_color,
82
+ theme,
83
+ },
84
+ }),
85
+ };
86
+ }
87
+
52
88
  try {
53
89
  const stats = await fetchWakatimeStats({ username, api_domain });
54
90
 
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * Calculates the exponential cdf.
3
3
  *
4
- * @param {number} x The value.
5
- * @returns {number} The exponential cdf.
4
+ * @param x The value.
5
+ * @returns The exponential cdf.
6
6
  */
7
- function exponential_cdf(x) {
7
+ function exponential_cdf(x: number): number {
8
8
  return 1 - 2 ** -x;
9
9
  }
10
10
 
11
11
  /**
12
12
  * Calculates the log normal cdf.
13
13
  *
14
- * @param {number} x The value.
15
- * @returns {number} The log normal cdf.
14
+ * @param x The value.
15
+ * @returns The log normal cdf.
16
16
  */
17
- function log_normal_cdf(x) {
17
+ function log_normal_cdf(x: number): number {
18
18
  // approximation
19
19
  return x / (1 + x);
20
20
  }
@@ -22,16 +22,16 @@ function log_normal_cdf(x) {
22
22
  /**
23
23
  * Calculates the users rank.
24
24
  *
25
- * @param {object} params Parameters on which the user's rank depends.
26
- * @param {boolean} params.all_commits Whether `include_all_commits` was used.
27
- * @param {number} params.commits Number of commits.
28
- * @param {number} params.prs The number of pull requests.
29
- * @param {number} params.issues The number of issues.
30
- * @param {number} params.reviews The number of reviews.
31
- * @param {number} params.repos Total number of repos.
32
- * @param {number} params.stars The number of stars.
33
- * @param {number} params.followers The number of followers.
34
- * @returns {{ level: string, percentile: number }} The users rank.
25
+ * @param params Parameters on which the user's rank depends.
26
+ * @param params.all_commits Whether `include_all_commits` was used.
27
+ * @param params.commits Number of commits.
28
+ * @param params.prs The number of pull requests.
29
+ * @param params.issues The number of issues.
30
+ * @param params.reviews The number of reviews.
31
+ * @param params.repos Total number of repos (accepted for compatibility, unused in the calculation).
32
+ * @param params.stars The number of stars.
33
+ * @param params.followers The number of followers.
34
+ * @returns The users rank.
35
35
  */
36
36
  function calculateRank({
37
37
  all_commits,
@@ -39,11 +39,18 @@ function calculateRank({
39
39
  prs,
40
40
  issues,
41
41
  reviews,
42
- // eslint-disable-next-line no-unused-vars
43
- repos, // unused
44
42
  stars,
45
43
  followers,
46
- }) {
44
+ }: {
45
+ all_commits: boolean;
46
+ commits: number;
47
+ prs: number;
48
+ issues: number;
49
+ reviews: number;
50
+ repos: number;
51
+ stars: number;
52
+ followers: number;
53
+ }): { level: string; percentile: number } {
47
54
  const COMMITS_MEDIAN = all_commits ? 1000 : 250,
48
55
  COMMITS_WEIGHT = 2;
49
56
  const PRS_MEDIAN = 50,
@@ -79,6 +86,9 @@ function calculateRank({
79
86
  TOTAL_WEIGHT;
80
87
 
81
88
  const level = LEVELS[THRESHOLDS.findIndex((t) => rank * 100 <= t)];
89
+ if (level === undefined) {
90
+ throw new Error("Unable to determine rank level");
91
+ }
82
92
 
83
93
  return { level, percentile: rank * 100 };
84
94
  }
package/src/cards/repo.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Card } from "../common/Card.js";
2
2
  import { I18n } from "../common/I18n.js";
3
- import { getCardColors } from "../common/color.js";
3
+ import { getCardColors, isPrefixedHexColor } from "../common/color.js";
4
4
  import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
5
5
  import { encodeHTML } from "../common/html.js";
6
6
  import { icons } from "../common/icons.js";
@@ -32,20 +32,29 @@ const DESCRIPTION_MAX_LINES = 3;
32
32
  * @param {string} textColor The color of the text.
33
33
  * @returns {string} Wrapped repo description SVG object.
34
34
  */
35
- const getBadgeSVG = (label, textColor, xOffset = 0) => `
36
- <g data-testid="badge" class="badge" transform="translate(${320 + xOffset}, -18)">
37
- <rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect>
38
- <text
39
- x="23" y="-5"
40
- alignment-baseline="central"
41
- dominant-baseline="central"
42
- text-anchor="middle"
43
- fill="${textColor}"
44
- >
45
- ${label}
46
- </text>
47
- </g>
48
- `;
35
+ const getBadgeSVG = (label, textColor, xOffset = 0) => {
36
+ if (!isPrefixedHexColor(textColor)) {
37
+ throw new Error(`Invalid text color: "${textColor}"`);
38
+ }
39
+ if (!Number.isFinite(xOffset)) {
40
+ throw new Error(`Invalid xOffset: "${xOffset}"`);
41
+ }
42
+
43
+ return `
44
+ <g data-testid="badge" class="badge" transform="translate(${320 + xOffset}, -18)">
45
+ <rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect>
46
+ <text
47
+ x="23" y="-5"
48
+ alignment-baseline="central"
49
+ dominant-baseline="central"
50
+ text-anchor="middle"
51
+ fill="${textColor}"
52
+ >
53
+ ${encodeHTML(label)}
54
+ </text>
55
+ </g>
56
+ `;
57
+ };
49
58
 
50
59
  /**
51
60
  * @typedef {import("../fetchers/types").RepositoryData} RepositoryData Repository data.
@@ -110,6 +119,7 @@ const renderRepoCard = (repo, options = {}) => {
110
119
  });
111
120
 
112
121
  let repoFilter = encodeURIComponent(buildSearchFilter([nameWithOwner], []));
122
+ const encodedUsername = encodeURIComponent(username);
113
123
  const STATS = {};
114
124
  if (show.includes("prs_authored")) {
115
125
  STATS.prs_authored = {
@@ -117,7 +127,7 @@ const renderRepoCard = (repo, options = {}) => {
117
127
  label: i18n.t("repocard.prs-authored"),
118
128
  value: totalPRsAuthored,
119
129
  id: "prs_authored",
120
- link: `https://github.com/search?q=${repoFilter}author%3A${username}&amp;type=pullrequests`,
130
+ link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&amp;type=pullrequests`,
121
131
  };
122
132
  }
123
133
  if (show.includes("prs_commented")) {
@@ -126,7 +136,7 @@ const renderRepoCard = (repo, options = {}) => {
126
136
  label: i18n.t("repocard.prs-commented"),
127
137
  value: totalPRsCommented,
128
138
  id: "prs_commented",
129
- link: `https://github.com/search?q=${repoFilter}commenter%3A${username}+-author%3A${username}&amp;type=pullrequests`,
139
+ link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&amp;type=pullrequests`,
130
140
  };
131
141
  }
132
142
  if (show.includes("prs_reviewed")) {
@@ -135,7 +145,7 @@ const renderRepoCard = (repo, options = {}) => {
135
145
  label: i18n.t("repocard.prs-reviewed"),
136
146
  value: totalPRsReviewed,
137
147
  id: "prs_reviewed",
138
- link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${username}+-author%3A${username}&amp;type=pullrequests`,
148
+ link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${encodedUsername}+-author%3A${encodedUsername}&amp;type=pullrequests`,
139
149
  };
140
150
  }
141
151
  if (show.includes("issues_authored")) {
@@ -144,7 +154,7 @@ const renderRepoCard = (repo, options = {}) => {
144
154
  label: i18n.t("repocard.issues-authored"),
145
155
  value: totalIssuesAuthored,
146
156
  id: "issues_authored",
147
- link: `https://github.com/search?q=${repoFilter}author%3A${username}&amp;type=issues`,
157
+ link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&amp;type=issues`,
148
158
  };
149
159
  }
150
160
  if (show.includes("issues_commented")) {
@@ -153,7 +163,7 @@ const renderRepoCard = (repo, options = {}) => {
153
163
  label: i18n.t("repocard.issues-commented"),
154
164
  value: totalIssuesCommented,
155
165
  id: "issues_commented",
156
- link: `https://github.com/search?q=${repoFilter}commenter%3A${username}+-author%3A${username}&amp;type=issues`,
166
+ link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&amp;type=issues`,
157
167
  };
158
168
  }
159
169