@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
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { themes } from "../themes/index.js";
|
|
2
|
+
|
|
3
|
+
/** Matches a 3-, 4-, 6-, or 8-digit hex color with no leading `#`. */
|
|
4
|
+
const HEX_COLOR =
|
|
5
|
+
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{3})$/;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a value is a bare hex color, i.e. hex digits with no `#` prefix
|
|
9
|
+
* (`"f00"`, `"ffffff"`). This is the form user-supplied color params and
|
|
10
|
+
* gradient stops arrive in.
|
|
11
|
+
*
|
|
12
|
+
* @param value Value to check.
|
|
13
|
+
* @returns True if the value is a bare hex color.
|
|
14
|
+
*/
|
|
15
|
+
const isBareHexColor = (value: unknown): boolean => {
|
|
16
|
+
return typeof value === "string" && HEX_COLOR.test(value);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Checks if a value is a `#`-prefixed hex color (`"#f00"`, `"#ffffff"`). This
|
|
21
|
+
* is the form colors take once resolved by {@link getCardColors}, i.e. right
|
|
22
|
+
* before they are written into the SVG.
|
|
23
|
+
*
|
|
24
|
+
* @param value Value to check.
|
|
25
|
+
* @returns True if the value is a `#`-prefixed hex color.
|
|
26
|
+
*/
|
|
27
|
+
const isPrefixedHexColor = (value: unknown): boolean => {
|
|
28
|
+
return (
|
|
29
|
+
typeof value === "string" &&
|
|
30
|
+
value.startsWith("#") &&
|
|
31
|
+
HEX_COLOR.test(value.slice(1))
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if the given parts form a valid gradient: a finite numeric angle
|
|
37
|
+
* followed by at least two bare-hex color stops, e.g. `["90", "f00", "0f0"]`.
|
|
38
|
+
* The angle is written into the SVG `gradientTransform="rotate(...)"`.
|
|
39
|
+
*
|
|
40
|
+
* @param parts Gradient parts: `[angle, ...stops]`.
|
|
41
|
+
* @returns True if the parts form a valid gradient.
|
|
42
|
+
*/
|
|
43
|
+
const isValidGradient = (parts: Array<string>): boolean => {
|
|
44
|
+
const [angle, ...stops] = parts;
|
|
45
|
+
return (
|
|
46
|
+
stops.length >= 2 &&
|
|
47
|
+
angle !== undefined &&
|
|
48
|
+
angle.trim() !== "" &&
|
|
49
|
+
Number.isFinite(Number(angle)) &&
|
|
50
|
+
stops.every(isBareHexColor)
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Checks if a string is a valid input for a color or gradient.
|
|
56
|
+
*
|
|
57
|
+
* @param color String to check, may be null or undefined.
|
|
58
|
+
* @returns True if the given string is a valid input.
|
|
59
|
+
*/
|
|
60
|
+
const isValidColorInput = (color: string | null | undefined): boolean => {
|
|
61
|
+
if (color === null || color === undefined) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return isValidGradient(color.split(",")) || isBareHexColor(color);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Iterates over a collection of colors inputs and verifies that each is a valid color or gradient.
|
|
69
|
+
*
|
|
70
|
+
* @param colors Object whose values are checked as valid color inputs.
|
|
71
|
+
* @return The first key where the associated input value is not valid. null if all inputs are valid.
|
|
72
|
+
*/
|
|
73
|
+
const findInvalidColor = (
|
|
74
|
+
colors: Record<string, string | null | undefined>,
|
|
75
|
+
): string | null => {
|
|
76
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
77
|
+
if (!isValidColorInput(value)) {
|
|
78
|
+
return key;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Retrieves a gradient if color has more than one valid hex codes else a single color.
|
|
86
|
+
*
|
|
87
|
+
* @param color The color to parse.
|
|
88
|
+
* @param fallbackColor The fallback color.
|
|
89
|
+
* @returns The gradient or color.
|
|
90
|
+
*/
|
|
91
|
+
const fallbackColor = (
|
|
92
|
+
color: string | undefined,
|
|
93
|
+
fallbackColor: string | Array<string>,
|
|
94
|
+
): string | Array<string> => {
|
|
95
|
+
const colors = color ? color.split(",") : [];
|
|
96
|
+
if (colors.length > 1 && isValidGradient(colors)) {
|
|
97
|
+
return colors;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (color !== undefined && isBareHexColor(color)) {
|
|
101
|
+
return `#${color}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return fallbackColor;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Object containing card colors.
|
|
109
|
+
*/
|
|
110
|
+
interface CardColors {
|
|
111
|
+
titleColor: string;
|
|
112
|
+
iconColor: string;
|
|
113
|
+
textColor: string;
|
|
114
|
+
bgColor: string | Array<string>;
|
|
115
|
+
borderColor: string;
|
|
116
|
+
ringColor: string;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Returns theme based colors with proper overrides and defaults.
|
|
121
|
+
*
|
|
122
|
+
* @param props Function arguments.
|
|
123
|
+
* @param props.title_color Card title color.
|
|
124
|
+
* @param props.text_color Card text color.
|
|
125
|
+
* @param props.icon_color Card icon color.
|
|
126
|
+
* @param props.bg_color Card background color.
|
|
127
|
+
* @param props.border_color Card border color.
|
|
128
|
+
* @param props.ring_color Card ring color.
|
|
129
|
+
* @param props.theme Card theme.
|
|
130
|
+
* @returns Card colors.
|
|
131
|
+
*/
|
|
132
|
+
const getCardColors = ({
|
|
133
|
+
title_color,
|
|
134
|
+
text_color,
|
|
135
|
+
icon_color,
|
|
136
|
+
bg_color,
|
|
137
|
+
border_color,
|
|
138
|
+
ring_color,
|
|
139
|
+
theme,
|
|
140
|
+
}: {
|
|
141
|
+
title_color?: string | undefined;
|
|
142
|
+
text_color?: string | undefined;
|
|
143
|
+
icon_color?: string | undefined;
|
|
144
|
+
bg_color?: string | undefined;
|
|
145
|
+
border_color?: string | undefined;
|
|
146
|
+
ring_color?: string | undefined;
|
|
147
|
+
theme?: string | undefined;
|
|
148
|
+
}): CardColors => {
|
|
149
|
+
const defaultTheme = themes.default;
|
|
150
|
+
const isThemeProvided = theme !== undefined && theme in themes;
|
|
151
|
+
|
|
152
|
+
const selectedTheme = isThemeProvided
|
|
153
|
+
? themes[theme as keyof typeof themes]
|
|
154
|
+
: defaultTheme;
|
|
155
|
+
|
|
156
|
+
const defaultBorderColor =
|
|
157
|
+
"border_color" in selectedTheme
|
|
158
|
+
? selectedTheme.border_color
|
|
159
|
+
: defaultTheme.border_color;
|
|
160
|
+
|
|
161
|
+
// get the color provided by the user else the theme color
|
|
162
|
+
// finally if both colors are invalid fallback to default theme
|
|
163
|
+
const titleColor = fallbackColor(
|
|
164
|
+
title_color || selectedTheme.title_color,
|
|
165
|
+
"#" + defaultTheme.title_color,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// get the color provided by the user else the theme color
|
|
169
|
+
// finally if both colors are invalid we use the titleColor
|
|
170
|
+
// NOTE: no built-in theme defines `ring_color`, so it falls back to the title color.
|
|
171
|
+
const ringColor = fallbackColor(ring_color, titleColor);
|
|
172
|
+
const iconColor = fallbackColor(
|
|
173
|
+
icon_color || selectedTheme.icon_color,
|
|
174
|
+
"#" + defaultTheme.icon_color,
|
|
175
|
+
);
|
|
176
|
+
const textColor = fallbackColor(
|
|
177
|
+
text_color || selectedTheme.text_color,
|
|
178
|
+
"#" + defaultTheme.text_color,
|
|
179
|
+
);
|
|
180
|
+
const bgColor = fallbackColor(
|
|
181
|
+
bg_color || selectedTheme.bg_color,
|
|
182
|
+
"#" + defaultTheme.bg_color,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const borderColor = fallbackColor(
|
|
186
|
+
border_color || defaultBorderColor,
|
|
187
|
+
"#" + defaultBorderColor,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
typeof titleColor !== "string" ||
|
|
192
|
+
typeof textColor !== "string" ||
|
|
193
|
+
typeof ringColor !== "string" ||
|
|
194
|
+
typeof iconColor !== "string" ||
|
|
195
|
+
typeof borderColor !== "string"
|
|
196
|
+
) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
"Unexpected behavior, all colors except background should be string.",
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return { titleColor, iconColor, textColor, bgColor, borderColor, ringColor };
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
fallbackColor,
|
|
207
|
+
getCardColors,
|
|
208
|
+
findInvalidColor,
|
|
209
|
+
isValidGradient,
|
|
210
|
+
isBareHexColor,
|
|
211
|
+
isPrefixedHexColor,
|
|
212
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
type Env = Record<string, string | undefined>;
|
|
2
|
+
|
|
3
|
+
interface PersonalAccessToken {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Config {
|
|
9
|
+
whitelist: Array<string> | undefined;
|
|
10
|
+
gistWhitelist: Array<string> | undefined;
|
|
11
|
+
excludeRepositories: Array<string>;
|
|
12
|
+
fetchMultiPageStars: string | undefined;
|
|
13
|
+
pats: Array<PersonalAccessToken>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param value Comma-separated string.
|
|
18
|
+
* @returns Parsed string values.
|
|
19
|
+
*/
|
|
20
|
+
const parseCsv = (value: string | undefined): Array<string> | undefined => {
|
|
21
|
+
if (!value) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return value.split(",");
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param env Environment variables to inspect.
|
|
29
|
+
* @returns Personal access tokens found in the environment.
|
|
30
|
+
*/
|
|
31
|
+
const parsePATsFromEnv = (env: Env): Array<PersonalAccessToken> => {
|
|
32
|
+
return Object.keys(env)
|
|
33
|
+
.filter((key) => /PAT_\d*$/.exec(key))
|
|
34
|
+
.map((name) => ({
|
|
35
|
+
name,
|
|
36
|
+
value: env[name] ?? "",
|
|
37
|
+
}));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @returns `process.env` if available, otherwise `{}`.
|
|
42
|
+
*/
|
|
43
|
+
const getDefaultEnv = (): Env => {
|
|
44
|
+
const processEnv = (globalThis as { process?: { env?: Env } }).process?.env;
|
|
45
|
+
return processEnv ?? {};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param config (Partial) config values to normalize.
|
|
50
|
+
* @returns Normalized config object with defaults applied.
|
|
51
|
+
*/
|
|
52
|
+
const normalizeConfig = (config: Partial<Config> = {}): Config => {
|
|
53
|
+
return {
|
|
54
|
+
whitelist: config.whitelist,
|
|
55
|
+
gistWhitelist: config.gistWhitelist,
|
|
56
|
+
excludeRepositories: config.excludeRepositories ?? [],
|
|
57
|
+
fetchMultiPageStars: config.fetchMultiPageStars,
|
|
58
|
+
pats: config.pats ?? [],
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let currentConfig: Config;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param env Environment variables used to build the runtime config.
|
|
66
|
+
*/
|
|
67
|
+
export const loadConfigFromEnv = (env: Env = getDefaultEnv()): void => {
|
|
68
|
+
const whitelist = parseCsv(env["WHITELIST"]);
|
|
69
|
+
const gistWhitelist = parseCsv(env["GIST_WHITELIST"]);
|
|
70
|
+
const excludeRepositories = parseCsv(env["EXCLUDE_REPO"]) ?? [];
|
|
71
|
+
|
|
72
|
+
currentConfig = normalizeConfig({
|
|
73
|
+
whitelist,
|
|
74
|
+
gistWhitelist,
|
|
75
|
+
excludeRepositories,
|
|
76
|
+
fetchMultiPageStars: env["FETCH_MULTI_PAGE_STARS"],
|
|
77
|
+
pats: parsePATsFromEnv(env),
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
loadConfigFromEnv();
|
|
82
|
+
|
|
83
|
+
export const getConfig = (): Config => currentConfig;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { OWNER_AFFILIATIONS } from "./constants.js";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
|
-
*
|
|
4
|
+
* A general message to ask user to try again later.
|
|
7
5
|
*/
|
|
8
6
|
const TRY_AGAIN_LATER = "Please try again later";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
9
|
+
* A map of error types to secondary error messages.
|
|
12
10
|
*/
|
|
13
11
|
const SECONDARY_ERROR_MESSAGES = {
|
|
14
12
|
MAX_RETRY:
|
|
@@ -28,16 +26,21 @@ const SECONDARY_ERROR_MESSAGES = {
|
|
|
28
26
|
* Custom error class to handle custom GRS errors.
|
|
29
27
|
*/
|
|
30
28
|
class CustomError extends Error {
|
|
29
|
+
type: string;
|
|
30
|
+
secondaryMessage: string;
|
|
31
|
+
|
|
31
32
|
/**
|
|
32
33
|
* Custom error constructor.
|
|
33
34
|
*
|
|
34
|
-
* @param
|
|
35
|
-
* @param
|
|
35
|
+
* @param message Error message.
|
|
36
|
+
* @param type Error type.
|
|
36
37
|
*/
|
|
37
|
-
constructor(message, type) {
|
|
38
|
+
constructor(message: string, type: string) {
|
|
38
39
|
super(message);
|
|
39
40
|
this.type = type;
|
|
40
|
-
|
|
41
|
+
type PartialRecord = Partial<Record<string, string>>;
|
|
42
|
+
this.secondaryMessage =
|
|
43
|
+
(SECONDARY_ERROR_MESSAGES as PartialRecord)[type] ?? type;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
static MAX_RETRY = "MAX_RETRY";
|
|
@@ -53,13 +56,16 @@ class CustomError extends Error {
|
|
|
53
56
|
* Missing query parameter class.
|
|
54
57
|
*/
|
|
55
58
|
class MissingParamError extends Error {
|
|
59
|
+
missedParams: Array<string>;
|
|
60
|
+
secondaryMessage: string | undefined;
|
|
61
|
+
|
|
56
62
|
/**
|
|
57
63
|
* Missing query parameter error constructor.
|
|
58
64
|
*
|
|
59
|
-
* @param
|
|
60
|
-
* @param
|
|
65
|
+
* @param missedParams An array of missing parameters names.
|
|
66
|
+
* @param secondaryMessage Optional secondary message to display.
|
|
61
67
|
*/
|
|
62
|
-
constructor(missedParams
|
|
68
|
+
constructor(missedParams: Array<string>, secondaryMessage?: string) {
|
|
63
69
|
const msg = `Missing params ${missedParams
|
|
64
70
|
.map((p) => `"${p}"`)
|
|
65
71
|
.join(", ")} make sure you pass the parameters in URL`;
|
|
@@ -72,10 +78,10 @@ class MissingParamError extends Error {
|
|
|
72
78
|
/**
|
|
73
79
|
* Retrieve secondary message from an error object.
|
|
74
80
|
*
|
|
75
|
-
* @param
|
|
76
|
-
* @returns
|
|
81
|
+
* @param err The error object.
|
|
82
|
+
* @returns The secondary message if available, otherwise undefined.
|
|
77
83
|
*/
|
|
78
|
-
const retrieveSecondaryMessage = (err) => {
|
|
84
|
+
const retrieveSecondaryMessage = (err: Error): string | undefined => {
|
|
79
85
|
return "secondaryMessage" in err && typeof err.secondaryMessage === "string"
|
|
80
86
|
? err.secondaryMessage
|
|
81
87
|
: undefined;
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { encodeHTML } from "./html.js";
|
|
4
2
|
import { splitWrappedText } from "./render.js";
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* Retrieves num with suffix k(thousands) precise to given decimal places.
|
|
8
6
|
*
|
|
9
|
-
* @param
|
|
10
|
-
* @param
|
|
11
|
-
* @returns
|
|
7
|
+
* @param num The number to format.
|
|
8
|
+
* @param precision The number of decimal places to include.
|
|
9
|
+
* @returns The formatted number.
|
|
12
10
|
*/
|
|
13
|
-
const kFormatter = (num, precision) => {
|
|
11
|
+
const kFormatter = (num: number, precision?: number): string | number => {
|
|
14
12
|
const abs = Math.abs(num);
|
|
15
13
|
const sign = Math.sign(num);
|
|
16
14
|
|
|
@@ -22,17 +20,17 @@ const kFormatter = (num, precision) => {
|
|
|
22
20
|
return sign * abs;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
return sign * parseFloat((abs / 1000).toFixed(1))
|
|
23
|
+
return `${sign * parseFloat((abs / 1000).toFixed(1))}k`;
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
/**
|
|
29
27
|
* Convert bytes to a human-readable string representation.
|
|
30
28
|
*
|
|
31
|
-
* @param
|
|
32
|
-
* @returns
|
|
29
|
+
* @param bytes The number of bytes to convert.
|
|
30
|
+
* @returns The human-readable representation of bytes.
|
|
33
31
|
* @throws {Error} If bytes is negative or too large.
|
|
34
32
|
*/
|
|
35
|
-
const formatBytes = (bytes) => {
|
|
33
|
+
const formatBytes = (bytes: number): string => {
|
|
36
34
|
if (bytes < 0) {
|
|
37
35
|
throw new Error("Bytes must be a non-negative number");
|
|
38
36
|
}
|
|
@@ -45,23 +43,29 @@ const formatBytes = (bytes) => {
|
|
|
45
43
|
const base = 1024;
|
|
46
44
|
const i = Math.floor(Math.log(bytes) / Math.log(base));
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
const unit = sizes[i];
|
|
47
|
+
if (unit === undefined) {
|
|
49
48
|
throw new Error("Bytes is too large to convert to a human-readable string");
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
return `${(bytes / Math.pow(base, i)).toFixed(1)} ${
|
|
51
|
+
return `${(bytes / Math.pow(base, i)).toFixed(1)} ${unit}`;
|
|
53
52
|
};
|
|
54
53
|
|
|
55
54
|
/**
|
|
56
55
|
* Split text over multiple lines based on the card width.
|
|
57
56
|
*
|
|
58
|
-
* @param
|
|
59
|
-
* @param
|
|
60
|
-
* @param
|
|
61
|
-
* @param
|
|
62
|
-
* @returns
|
|
57
|
+
* @param text Text to split.
|
|
58
|
+
* @param width Available wrap width in px.
|
|
59
|
+
* @param fontSize Font size in px.
|
|
60
|
+
* @param maxLines Maximum number of lines.
|
|
61
|
+
* @returns Array of lines.
|
|
63
62
|
*/
|
|
64
|
-
const wrapTextMultiline = (
|
|
63
|
+
const wrapTextMultiline = (
|
|
64
|
+
text: string,
|
|
65
|
+
width: number,
|
|
66
|
+
fontSize: number,
|
|
67
|
+
maxLines = 3,
|
|
68
|
+
): Array<string> => {
|
|
65
69
|
const wrapped = splitWrappedText(text, fontSize, width);
|
|
66
70
|
const lines = wrapped
|
|
67
71
|
.map((line) => encodeHTML(line.trim()))
|
|
@@ -69,7 +73,11 @@ const wrapTextMultiline = (text, width, fontSize, maxLines = 3) => {
|
|
|
69
73
|
|
|
70
74
|
// Add "..." to the last line if the text exceeds maxLines
|
|
71
75
|
if (wrapped.length > maxLines) {
|
|
72
|
-
|
|
76
|
+
const lastIndex = maxLines - 1;
|
|
77
|
+
const lastLine = lines[lastIndex];
|
|
78
|
+
if (lastLine !== undefined) {
|
|
79
|
+
lines[lastIndex] = `${lastLine}...`;
|
|
80
|
+
}
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
// Remove empty lines if text fits in less than maxLines lines
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Encode string as HTML.
|
|
5
3
|
*
|
|
6
4
|
* @see https://stackoverflow.com/a/48073476/10629172
|
|
7
|
-
*
|
|
8
|
-
* @param {string} str String to encode.
|
|
9
|
-
* @returns {string} Encoded string.
|
|
10
5
|
*/
|
|
11
|
-
const encodeHTML = (str) => {
|
|
6
|
+
const encodeHTML = (str: string): string => {
|
|
12
7
|
return (
|
|
13
8
|
str
|
|
14
|
-
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
|
15
|
-
return
|
|
9
|
+
.replace(/[\u00A0-\u9999<>&"'](?!#)/gim, (i: string) => {
|
|
10
|
+
return `&#${i.charCodeAt(0)};`;
|
|
16
11
|
})
|
|
17
12
|
// eslint-disable-next-line no-control-regex
|
|
18
13
|
.replace(/\u0008/gim, "")
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import type { AxiosRequestConfig, AxiosResponse } from "axios";
|
|
3
|
+
|
|
4
|
+
/** Body of a GraphQL request sent to the GitHub API. */
|
|
5
|
+
interface GraphQLRequest {
|
|
6
|
+
/** The GraphQL query. */
|
|
7
|
+
query: string;
|
|
8
|
+
/** Variables referenced by the query. */
|
|
9
|
+
variables: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Send GraphQL request to GitHub API.
|
|
14
|
+
*
|
|
15
|
+
* @param data Request data.
|
|
16
|
+
* @param headers Request headers.
|
|
17
|
+
* @returns Request response.
|
|
18
|
+
*/
|
|
19
|
+
const request = (
|
|
20
|
+
data: GraphQLRequest,
|
|
21
|
+
headers: NonNullable<AxiosRequestConfig["headers"]>,
|
|
22
|
+
): Promise<AxiosResponse> => {
|
|
23
|
+
return axios({
|
|
24
|
+
url: "https://api.github.com/graphql",
|
|
25
|
+
method: "post",
|
|
26
|
+
headers,
|
|
27
|
+
data,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { request };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
/*
|
|
4
2
|
The icons in this file are based on https://github.com/primer/octicons which is released under the MIT license:
|
|
5
3
|
|
|
@@ -46,12 +44,16 @@ const icons = {
|
|
|
46
44
|
/**
|
|
47
45
|
* Get rank icon
|
|
48
46
|
*
|
|
49
|
-
* @param
|
|
50
|
-
* @param
|
|
51
|
-
* @param
|
|
52
|
-
* @returns
|
|
47
|
+
* @param rankIcon - The rank icon type.
|
|
48
|
+
* @param rankLevel - The rank level.
|
|
49
|
+
* @param percentile - The rank percentile.
|
|
50
|
+
* @returns The SVG code of the rank icon
|
|
53
51
|
*/
|
|
54
|
-
const rankIcon = (
|
|
52
|
+
const rankIcon = (
|
|
53
|
+
rankIcon: string,
|
|
54
|
+
rankLevel: string,
|
|
55
|
+
percentile: number,
|
|
56
|
+
): string => {
|
|
55
57
|
switch (rankIcon) {
|
|
56
58
|
case "github":
|
|
57
59
|
return `
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"Awk": "#c30e9b",
|
|
45
45
|
"B (Formal Method)": "#8aa8c5",
|
|
46
46
|
"B4X": "#00e4ff",
|
|
47
|
+
"BAML": "#a855f7",
|
|
47
48
|
"BASIC": "#ff0000",
|
|
48
49
|
"BQN": "#2b7067",
|
|
49
50
|
"Ballerina": "#FF5000",
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"Blade": "#f7523f",
|
|
59
60
|
"BlitzBasic": "#00FFAE",
|
|
60
61
|
"BlitzMax": "#cd6400",
|
|
62
|
+
"Blueprint": "#3584E4",
|
|
61
63
|
"Bluespec": "#12223c",
|
|
62
64
|
"Bluespec BH": "#12223c",
|
|
63
65
|
"Boo": "#d4bec1",
|
|
@@ -69,7 +71,7 @@
|
|
|
69
71
|
"Bru": "#F4AA41",
|
|
70
72
|
"BuildStream": "#006bff",
|
|
71
73
|
"C": "#555555",
|
|
72
|
-
"C#": "#
|
|
74
|
+
"C#": "#7355dd",
|
|
73
75
|
"C++": "#f34b7d",
|
|
74
76
|
"C3": "#2563eb",
|
|
75
77
|
"CAP CDS": "#0092d1",
|
|
@@ -227,6 +229,7 @@
|
|
|
227
229
|
"Graphviz (DOT)": "#2596be",
|
|
228
230
|
"Groovy": "#4298b8",
|
|
229
231
|
"Groovy Server Pages": "#4298b8",
|
|
232
|
+
"GtkRC": "#7fe719",
|
|
230
233
|
"HAProxy": "#106da9",
|
|
231
234
|
"HCL": "#844FBA",
|
|
232
235
|
"HIP": "#4F3A4F",
|
|
@@ -416,6 +419,7 @@
|
|
|
416
419
|
"OpenSCAD": "#e5cd45",
|
|
417
420
|
"Option List": "#476732",
|
|
418
421
|
"Org": "#77aa99",
|
|
422
|
+
"OverPy": "#78b355",
|
|
419
423
|
"OverpassQL": "#cce2aa",
|
|
420
424
|
"Oxygene": "#cdd0e3",
|
|
421
425
|
"Oz": "#fab738",
|
|
@@ -445,10 +449,12 @@
|
|
|
445
449
|
"Portugol": "#f8bd00",
|
|
446
450
|
"PostCSS": "#dc3a0c",
|
|
447
451
|
"PostScript": "#da291c",
|
|
452
|
+
"Power Query": "#d38e0d",
|
|
448
453
|
"PowerBuilder": "#8f0f8d",
|
|
449
454
|
"PowerShell": "#012456",
|
|
450
455
|
"Praat": "#c8506d",
|
|
451
456
|
"Prisma": "#0c344b",
|
|
457
|
+
"Pro*C": "#bb8368",
|
|
452
458
|
"Processing": "#0096D8",
|
|
453
459
|
"Procfile": "#3B2F63",
|
|
454
460
|
"Prolog": "#74283c",
|
|
@@ -490,6 +496,7 @@
|
|
|
490
496
|
"Rebol": "#358a5b",
|
|
491
497
|
"Record Jar": "#0673ba",
|
|
492
498
|
"Red": "#f50000",
|
|
499
|
+
"Redscript": "#f44336",
|
|
493
500
|
"Regular Expression": "#009a00",
|
|
494
501
|
"Ren'Py": "#ff7f7f",
|
|
495
502
|
"Rez": "#FFDAB3",
|
|
@@ -659,6 +666,7 @@
|
|
|
659
666
|
"nanorc": "#2d004d",
|
|
660
667
|
"nesC": "#94B0C7",
|
|
661
668
|
"ooc": "#b0b77e",
|
|
669
|
+
"pkg-config": "#2b5e82",
|
|
662
670
|
"q": "#0040cd",
|
|
663
671
|
"reStructuredText": "#141414",
|
|
664
672
|
"sed": "#64b970",
|