@stats-organization/github-readme-stats-core 2.1.3 → 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.
- package/build/api/gist.d.ts.map +1 -1
- package/build/api/gist.js +34 -1
- package/build/api/index.d.ts.map +1 -1
- package/build/api/index.js +18 -1
- package/build/api/pin.d.ts.map +1 -1
- package/build/api/pin.js +17 -1
- package/build/api/top-langs.d.ts.map +1 -1
- package/build/api/top-langs.js +36 -5
- package/build/api/wakatime.d.ts.map +1 -1
- package/build/api/wakatime.js +34 -1
- package/build/calculateRank.d.ts +12 -11
- package/build/calculateRank.d.ts.map +1 -1
- package/build/calculateRank.js +18 -18
- package/build/cards/gist.d.ts.map +1 -1
- package/build/cards/gist.js +0 -1
- package/build/cards/repo.d.ts.map +1 -1
- package/build/cards/repo.js +29 -21
- package/build/cards/stats.d.ts +4 -2
- package/build/cards/stats.d.ts.map +1 -1
- package/build/cards/stats.js +21 -9
- package/build/cards/top-languages.d.ts.map +1 -1
- package/build/cards/top-languages.js +39 -12
- package/build/cards/wakatime.d.ts.map +1 -1
- package/build/cards/wakatime.js +29 -9
- package/build/common/Card.d.ts +54 -60
- package/build/common/Card.d.ts.map +1 -1
- package/build/common/Card.js +88 -61
- package/build/common/I18n.d.ts +13 -14
- package/build/common/I18n.d.ts.map +1 -1
- package/build/common/I18n.js +13 -12
- package/build/common/color.d.ts +56 -32
- package/build/common/color.d.ts.map +1 -1
- package/build/common/color.js +87 -50
- package/build/common/config.d.ts +18 -2
- package/build/common/config.d.ts.map +1 -1
- package/build/common/config.js +17 -26
- package/build/common/constants.d.ts +2 -2
- package/build/common/constants.d.ts.map +1 -1
- package/build/common/constants.js +5 -3
- package/build/common/error.d.ts +36 -29
- package/build/common/error.d.ts.map +1 -1
- package/build/common/error.js +14 -10
- package/build/common/fmt.d.ts +14 -13
- package/build/common/fmt.d.ts.map +1 -1
- package/build/common/fmt.js +19 -15
- package/build/common/html.d.ts +2 -4
- package/build/common/html.d.ts.map +1 -1
- package/build/common/html.js +2 -6
- package/build/common/http.d.ts +13 -4
- package/build/common/http.d.ts.map +1 -1
- package/build/common/http.js +3 -4
- package/build/common/icons.d.ts +22 -21
- package/build/common/icons.d.ts.map +1 -1
- package/build/common/icons.js +4 -5
- package/build/common/languageColors.json +9 -1
- package/build/common/ops.d.ts +30 -29
- package/build/common/ops.d.ts.map +1 -1
- package/build/common/ops.js +32 -38
- package/build/common/render.d.ts +115 -111
- package/build/common/render.d.ts.map +1 -1
- package/build/common/render.js +138 -88
- package/build/common/retryer.d.ts +24 -13
- package/build/common/retryer.d.ts.map +1 -1
- package/build/common/retryer.js +22 -30
- package/build/fetchers/gist.d.ts +6 -21
- package/build/fetchers/gist.d.ts.map +1 -1
- package/build/fetchers/gist.js +30 -36
- package/build/fetchers/repo.d.ts.map +1 -1
- package/build/fetchers/repo.js +4 -7
- package/build/fetchers/stats.d.ts.map +1 -1
- package/build/fetchers/stats.js +3 -7
- package/build/fetchers/top-languages.d.ts.map +1 -1
- package/build/fetchers/top-languages.js +0 -1
- package/build/fetchers/types.d.ts +126 -0
- package/build/fetchers/types.d.ts.map +1 -0
- package/build/fetchers/types.js +1 -0
- package/build/fetchers/wakatime.d.ts.map +1 -1
- package/build/fetchers/wakatime.js +0 -1
- package/build/translations.d.ts +627 -24
- package/build/translations.d.ts.map +1 -1
- package/build/translations.js +7 -8
- package/package.json +4 -4
- package/src/_emoji-name-map.d.ts +10 -0
- package/src/api/gist.js +36 -2
- package/src/api/index.js +19 -2
- package/src/api/pin.js +18 -2
- package/src/api/top-langs.js +38 -6
- package/src/api/wakatime.js +36 -2
- package/src/{calculateRank.js → calculateRank.ts} +29 -19
- package/src/cards/gist.js +0 -2
- package/src/cards/repo.js +30 -22
- package/src/cards/stats.js +22 -10
- package/src/cards/top-languages.js +49 -13
- package/src/cards/wakatime.js +33 -10
- package/src/common/Card.ts +338 -0
- package/src/common/I18n.ts +49 -0
- package/src/common/color.ts +212 -0
- package/src/common/config.ts +83 -0
- package/src/common/constants.ts +10 -0
- package/src/common/{error.js → error.ts} +20 -14
- package/src/common/{fmt.js → fmt.ts} +27 -19
- package/src/common/{html.js → html.ts} +3 -8
- package/src/common/http.ts +31 -0
- package/src/common/{icons.js → icons.ts} +9 -7
- package/src/common/languageColors.json +9 -1
- package/src/common/{ops.js → ops.ts} +48 -52
- package/src/common/{render.js → render.ts} +208 -98
- package/src/common/retryer.ts +125 -0
- package/src/fetchers/gist.ts +136 -0
- package/src/fetchers/repo.js +4 -8
- package/src/fetchers/stats.js +3 -8
- package/src/fetchers/top-languages.js +0 -2
- package/src/fetchers/{types.d.ts → types.ts} +1 -1
- package/src/fetchers/wakatime.js +0 -2
- package/src/{translations.js → translations.ts} +14 -10
- package/src/common/Card.js +0 -275
- package/src/common/I18n.js +0 -42
- package/src/common/color.js +0 -145
- package/src/common/config.js +0 -78
- package/src/common/constants.js +0 -9
- package/src/common/http.js +0 -21
- package/src/common/retryer.js +0 -104
- package/src/fetchers/gist.js +0 -114
package/build/translations.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
// @ts-check
|
|
2
1
|
import { encodeHTML } from "./common/html.js";
|
|
3
2
|
/**
|
|
4
3
|
* Retrieves stat card labels in the available locales.
|
|
5
4
|
*
|
|
6
|
-
* @param
|
|
7
|
-
* @param
|
|
8
|
-
* @param
|
|
9
|
-
* @returns
|
|
5
|
+
* @param props Function arguments.
|
|
6
|
+
* @param props.name The name of the locale.
|
|
7
|
+
* @param props.apostrophe Whether to use apostrophe or not.
|
|
8
|
+
* @returns The locales object.
|
|
10
9
|
*
|
|
11
10
|
* @see https://www.andiamo.co.uk/resources/iso-language-codes/ for language codes.
|
|
12
11
|
*/
|
|
13
|
-
const statCardLocales = ({ name, apostrophe }) => {
|
|
12
|
+
const statCardLocales = ({ name, apostrophe, }) => {
|
|
14
13
|
const encodedName = encodeHTML(name);
|
|
15
14
|
return {
|
|
16
15
|
"statcard.title": {
|
|
@@ -1131,8 +1130,8 @@ const availableLocales = Object.keys(repoCardLocales["repocard.archived"]);
|
|
|
1131
1130
|
/**
|
|
1132
1131
|
* Checks whether the locale is available or not.
|
|
1133
1132
|
*
|
|
1134
|
-
* @param
|
|
1135
|
-
* @returns
|
|
1133
|
+
* @param locale The locale to check.
|
|
1134
|
+
* @returns Boolean specifying whether the locale is available or not.
|
|
1136
1135
|
*/
|
|
1137
1136
|
const isLocaleAvailable = (locale) => {
|
|
1138
1137
|
return availableLocales.includes(locale.toLowerCase());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stats-organization/github-readme-stats-core",
|
|
3
|
-
"version": "2.1.
|
|
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": "
|
|
74
|
+
"js-yaml": "5.2.1",
|
|
75
75
|
"jsdom": "29.1.1",
|
|
76
|
-
"vitest": "4.1.
|
|
76
|
+
"vitest": "4.1.10"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"axios": "^1.
|
|
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,6 +1,5 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { renderGistCard } from "../cards/gist.js";
|
|
2
|
+
import { findInvalidColor } from "../common/color.js";
|
|
4
3
|
import {
|
|
5
4
|
MissingParamError,
|
|
6
5
|
retrieveSecondaryMessage,
|
|
@@ -28,6 +27,23 @@ export default async (
|
|
|
28
27
|
},
|
|
29
28
|
pat = null,
|
|
30
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
|
+
|
|
31
47
|
if (locale && !isLocaleAvailable(locale)) {
|
|
32
48
|
return {
|
|
33
49
|
status: "error - permanent",
|
|
@@ -45,6 +61,24 @@ export default async (
|
|
|
45
61
|
};
|
|
46
62
|
}
|
|
47
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
|
+
|
|
48
82
|
try {
|
|
49
83
|
const gistData = await fetchGist(id, pat);
|
|
50
84
|
|
package/src/api/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { renderStatsCard } from "../cards/stats.js";
|
|
2
|
+
import { findInvalidColor } from "../common/color.js";
|
|
4
3
|
import {
|
|
5
4
|
MissingParamError,
|
|
6
5
|
retrieveSecondaryMessage,
|
|
@@ -46,6 +45,24 @@ export default async (
|
|
|
46
45
|
},
|
|
47
46
|
pat = null,
|
|
48
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
|
+
|
|
49
66
|
if (locale && !isLocaleAvailable(locale)) {
|
|
50
67
|
return {
|
|
51
68
|
status: "error - permanent",
|
package/src/api/pin.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { renderRepoCard } from "../cards/repo.js";
|
|
2
|
+
import { findInvalidColor } from "../common/color.js";
|
|
4
3
|
import {
|
|
5
4
|
MissingParamError,
|
|
6
5
|
retrieveSecondaryMessage,
|
|
@@ -36,6 +35,23 @@ export default async (
|
|
|
36
35
|
},
|
|
37
36
|
pat = null,
|
|
38
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
|
+
|
|
39
55
|
if (locale && !isLocaleAvailable(locale)) {
|
|
40
56
|
return {
|
|
41
57
|
status: "error - permanent",
|
package/src/api/top-langs.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { renderTopLanguages } from "../cards/top-languages.js";
|
|
2
|
+
import { findInvalidColor } from "../common/color.js";
|
|
4
3
|
import {
|
|
5
4
|
MissingParamError,
|
|
6
5
|
retrieveSecondaryMessage,
|
|
@@ -40,6 +39,23 @@ export default async (
|
|
|
40
39
|
},
|
|
41
40
|
pat = null,
|
|
42
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
|
+
|
|
43
59
|
if (locale && !isLocaleAvailable(locale)) {
|
|
44
60
|
return {
|
|
45
61
|
status: "error - permanent",
|
|
@@ -57,10 +73,27 @@ export default async (
|
|
|
57
73
|
};
|
|
58
74
|
}
|
|
59
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
|
+
|
|
60
94
|
if (
|
|
61
95
|
layout !== undefined &&
|
|
62
|
-
|
|
63
|
-
!["compact", "normal", "donut", "donut-vertical", "pie"].includes(layout))
|
|
96
|
+
!["compact", "normal", "donut", "donut-vertical", "pie"].includes(layout)
|
|
64
97
|
) {
|
|
65
98
|
return {
|
|
66
99
|
status: "error - permanent",
|
|
@@ -80,8 +113,7 @@ export default async (
|
|
|
80
113
|
|
|
81
114
|
if (
|
|
82
115
|
stats_format !== undefined &&
|
|
83
|
-
|
|
84
|
-
!["bytes", "percentages"].includes(stats_format))
|
|
116
|
+
!["bytes", "percentages"].includes(stats_format)
|
|
85
117
|
) {
|
|
86
118
|
return {
|
|
87
119
|
status: "error - permanent",
|
package/src/api/wakatime.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { renderWakatimeCard } from "../cards/wakatime.js";
|
|
2
|
+
import { findInvalidColor } from "../common/color.js";
|
|
4
3
|
import {
|
|
5
4
|
MissingParamError,
|
|
6
5
|
retrieveSecondaryMessage,
|
|
@@ -34,6 +33,23 @@ export default async ({
|
|
|
34
33
|
display_format,
|
|
35
34
|
disable_animations,
|
|
36
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
|
+
|
|
37
53
|
if (locale && !isLocaleAvailable(locale)) {
|
|
38
54
|
return {
|
|
39
55
|
status: "error - permanent",
|
|
@@ -51,6 +67,24 @@ export default async ({
|
|
|
51
67
|
};
|
|
52
68
|
}
|
|
53
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
|
+
|
|
54
88
|
try {
|
|
55
89
|
const stats = await fetchWakatimeStats({ username, api_domain });
|
|
56
90
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Calculates the exponential cdf.
|
|
3
3
|
*
|
|
4
|
-
* @param
|
|
5
|
-
* @returns
|
|
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
|
|
15
|
-
* @returns
|
|
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
|
|
26
|
-
* @param
|
|
27
|
-
* @param
|
|
28
|
-
* @param
|
|
29
|
-
* @param
|
|
30
|
-
* @param
|
|
31
|
-
* @param
|
|
32
|
-
* @param
|
|
33
|
-
* @param
|
|
34
|
-
* @returns
|
|
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/gist.js
CHANGED
package/src/cards/repo.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { Card } from "../common/Card.js";
|
|
4
2
|
import { I18n } from "../common/I18n.js";
|
|
5
|
-
import { getCardColors } from "../common/color.js";
|
|
3
|
+
import { getCardColors, isPrefixedHexColor } from "../common/color.js";
|
|
6
4
|
import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
|
|
7
5
|
import { encodeHTML } from "../common/html.js";
|
|
8
6
|
import { icons } from "../common/icons.js";
|
|
@@ -34,20 +32,29 @@ const DESCRIPTION_MAX_LINES = 3;
|
|
|
34
32
|
* @param {string} textColor The color of the text.
|
|
35
33
|
* @returns {string} Wrapped repo description SVG object.
|
|
36
34
|
*/
|
|
37
|
-
const getBadgeSVG = (label, textColor, xOffset = 0) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
>
|
|
47
|
-
${
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
};
|
|
51
58
|
|
|
52
59
|
/**
|
|
53
60
|
* @typedef {import("../fetchers/types").RepositoryData} RepositoryData Repository data.
|
|
@@ -112,6 +119,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
112
119
|
});
|
|
113
120
|
|
|
114
121
|
let repoFilter = encodeURIComponent(buildSearchFilter([nameWithOwner], []));
|
|
122
|
+
const encodedUsername = encodeURIComponent(username);
|
|
115
123
|
const STATS = {};
|
|
116
124
|
if (show.includes("prs_authored")) {
|
|
117
125
|
STATS.prs_authored = {
|
|
@@ -119,7 +127,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
119
127
|
label: i18n.t("repocard.prs-authored"),
|
|
120
128
|
value: totalPRsAuthored,
|
|
121
129
|
id: "prs_authored",
|
|
122
|
-
link: `https://github.com/search?q=${repoFilter}author%3A${
|
|
130
|
+
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=pullrequests`,
|
|
123
131
|
};
|
|
124
132
|
}
|
|
125
133
|
if (show.includes("prs_commented")) {
|
|
@@ -128,7 +136,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
128
136
|
label: i18n.t("repocard.prs-commented"),
|
|
129
137
|
value: totalPRsCommented,
|
|
130
138
|
id: "prs_commented",
|
|
131
|
-
link: `https://github.com/search?q=${repoFilter}commenter%3A${
|
|
139
|
+
link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
132
140
|
};
|
|
133
141
|
}
|
|
134
142
|
if (show.includes("prs_reviewed")) {
|
|
@@ -137,7 +145,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
137
145
|
label: i18n.t("repocard.prs-reviewed"),
|
|
138
146
|
value: totalPRsReviewed,
|
|
139
147
|
id: "prs_reviewed",
|
|
140
|
-
link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${
|
|
148
|
+
link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
141
149
|
};
|
|
142
150
|
}
|
|
143
151
|
if (show.includes("issues_authored")) {
|
|
@@ -146,7 +154,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
146
154
|
label: i18n.t("repocard.issues-authored"),
|
|
147
155
|
value: totalIssuesAuthored,
|
|
148
156
|
id: "issues_authored",
|
|
149
|
-
link: `https://github.com/search?q=${repoFilter}author%3A${
|
|
157
|
+
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=issues`,
|
|
150
158
|
};
|
|
151
159
|
}
|
|
152
160
|
if (show.includes("issues_commented")) {
|
|
@@ -155,7 +163,7 @@ const renderRepoCard = (repo, options = {}) => {
|
|
|
155
163
|
label: i18n.t("repocard.issues-commented"),
|
|
156
164
|
value: totalIssuesCommented,
|
|
157
165
|
id: "issues_commented",
|
|
158
|
-
link: `https://github.com/search?q=${repoFilter}commenter%3A${
|
|
166
|
+
link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=issues`,
|
|
159
167
|
};
|
|
160
168
|
}
|
|
161
169
|
|
package/src/cards/stats.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { Card } from "../common/Card.js";
|
|
4
2
|
import { I18n } from "../common/I18n.js";
|
|
5
3
|
import { getCardColors } from "../common/color.js";
|
|
6
4
|
import { CustomError } from "../common/error.js";
|
|
7
5
|
import { kFormatter } from "../common/fmt.js";
|
|
6
|
+
import { encodeHTML } from "../common/html.js";
|
|
8
7
|
import { icons, rankIcon } from "../common/icons.js";
|
|
9
8
|
import { buildSearchFilter, clampValue } from "../common/ops.js";
|
|
10
9
|
import { flexLayout, measureText } from "../common/render.js";
|
|
@@ -54,8 +53,10 @@ const LONG_LOCALES = [
|
|
|
54
53
|
/**
|
|
55
54
|
* Create a stats card text item.
|
|
56
55
|
*
|
|
56
|
+
* The caller must ensure that the passed `icon` and `link` are properly sanitized!
|
|
57
|
+
*
|
|
57
58
|
* @param {object} params Object that contains the createTextNode parameters.
|
|
58
|
-
* @param {string} params.icon The icon to display.
|
|
59
|
+
* @param {string} params.icon The sanitized icon to display.
|
|
59
60
|
* @param {string} params.label The label to display.
|
|
60
61
|
* @param {number} params.value The value to display.
|
|
61
62
|
* @param {string} params.id The id of the stat.
|
|
@@ -66,7 +67,7 @@ const LONG_LOCALES = [
|
|
|
66
67
|
* @param {boolean} params.bold Whether to bold the label.
|
|
67
68
|
* @param {string} params.numberFormat The format of numbers on card.
|
|
68
69
|
* @param {number=} params.numberPrecision The precision of numbers on card.
|
|
69
|
-
* @param {string} params.link
|
|
70
|
+
* @param {string} params.link Sanitized url to link to.
|
|
70
71
|
* @param {number} params.labelXOffset horizontal offset for label.
|
|
71
72
|
* @returns {string} The stats card text item SVG object.
|
|
72
73
|
*/
|
|
@@ -85,6 +86,16 @@ const createTextNode = ({
|
|
|
85
86
|
link,
|
|
86
87
|
labelXOffset = 25,
|
|
87
88
|
}) => {
|
|
89
|
+
if (!Number.isFinite(labelXOffset)) {
|
|
90
|
+
throw new Error(`Invalid labelXOffset: "${labelXOffset}"`);
|
|
91
|
+
}
|
|
92
|
+
if (!Number.isFinite(shiftValuePos)) {
|
|
93
|
+
throw new Error(`Invalid shiftValuePos: "${shiftValuePos}"`);
|
|
94
|
+
}
|
|
95
|
+
if (!Number.isFinite(index)) {
|
|
96
|
+
throw new Error(`Invalid index: "${index}"`);
|
|
97
|
+
}
|
|
98
|
+
|
|
88
99
|
const precision =
|
|
89
100
|
typeof numberPrecision === "number" && !isNaN(numberPrecision)
|
|
90
101
|
? clampValue(numberPrecision, 0, 2)
|
|
@@ -111,7 +122,7 @@ const createTextNode = ({
|
|
|
111
122
|
${iconSvg}
|
|
112
123
|
<text class="stat ${
|
|
113
124
|
bold ? " bold" : "not_bold"
|
|
114
|
-
}" ${labelOffset} y="12.5">${label}:</text>
|
|
125
|
+
}" ${labelOffset} y="12.5">${encodeHTML(label)}:</text>
|
|
115
126
|
<text
|
|
116
127
|
class="stat ${bold ? " bold" : "not_bold"}"
|
|
117
128
|
x="${(showIcons ? 140 : 120) + (bold ? 5 : 0) + shiftValuePos}"
|
|
@@ -424,13 +435,14 @@ const renderStatsCard = (
|
|
|
424
435
|
}
|
|
425
436
|
|
|
426
437
|
let repoFilter = encodeURIComponent(buildSearchFilter(repo, owner));
|
|
438
|
+
const encodedUsername = encodeURIComponent(username);
|
|
427
439
|
if (show.includes("prs_authored")) {
|
|
428
440
|
STATS.prs_authored = {
|
|
429
441
|
icon: icons.prs,
|
|
430
442
|
label: i18n.t("statcard.prs-authored"),
|
|
431
443
|
value: totalPRsAuthored,
|
|
432
444
|
id: "prs_authored",
|
|
433
|
-
link: `https://github.com/search?q=${repoFilter}author%3A${
|
|
445
|
+
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=pullrequests`,
|
|
434
446
|
};
|
|
435
447
|
}
|
|
436
448
|
if (show.includes("prs_commented")) {
|
|
@@ -439,7 +451,7 @@ const renderStatsCard = (
|
|
|
439
451
|
label: i18n.t("statcard.prs-commented"),
|
|
440
452
|
value: totalPRsCommented,
|
|
441
453
|
id: "prs_commented",
|
|
442
|
-
link: `https://github.com/search?q=${repoFilter}commenter%3A${
|
|
454
|
+
link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
443
455
|
};
|
|
444
456
|
}
|
|
445
457
|
if (show.includes("prs_reviewed")) {
|
|
@@ -448,7 +460,7 @@ const renderStatsCard = (
|
|
|
448
460
|
label: i18n.t("statcard.prs-reviewed"),
|
|
449
461
|
value: totalPRsReviewed,
|
|
450
462
|
id: "prs_reviewed",
|
|
451
|
-
link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${
|
|
463
|
+
link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${encodedUsername}+-author%3A${encodedUsername}&type=pullrequests`,
|
|
452
464
|
};
|
|
453
465
|
}
|
|
454
466
|
if (show.includes("issues_authored")) {
|
|
@@ -457,7 +469,7 @@ const renderStatsCard = (
|
|
|
457
469
|
label: i18n.t("statcard.issues-authored"),
|
|
458
470
|
value: totalIssuesAuthored,
|
|
459
471
|
id: "issues_authored",
|
|
460
|
-
link: `https://github.com/search?q=${repoFilter}author%3A${
|
|
472
|
+
link: `https://github.com/search?q=${repoFilter}author%3A${encodedUsername}&type=issues`,
|
|
461
473
|
};
|
|
462
474
|
}
|
|
463
475
|
if (show.includes("issues_commented")) {
|
|
@@ -466,7 +478,7 @@ const renderStatsCard = (
|
|
|
466
478
|
label: i18n.t("statcard.issues-commented"),
|
|
467
479
|
value: totalIssuesCommented,
|
|
468
480
|
id: "issues_commented",
|
|
469
|
-
link: `https://github.com/search?q=${repoFilter}commenter%3A${
|
|
481
|
+
link: `https://github.com/search?q=${repoFilter}commenter%3A${encodedUsername}+-author%3A${encodedUsername}&type=issues`,
|
|
470
482
|
};
|
|
471
483
|
}
|
|
472
484
|
|