@stats-forge/github-stats-forge-core 0.0.1 → 0.1.0

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 (208) hide show
  1. package/README.md +0 -10
  2. package/build/api/api-result.d.ts +6 -7
  3. package/build/api/api-result.d.ts.map +1 -1
  4. package/build/api/api-result.js +4 -5
  5. package/build/api/gist.d.ts +3 -10
  6. package/build/api/gist.d.ts.map +1 -1
  7. package/build/api/gist.js +4 -11
  8. package/build/api/index.d.ts +12 -12
  9. package/build/api/params.d.ts +11 -8
  10. package/build/api/params.d.ts.map +1 -1
  11. package/build/api/params.js +16 -14
  12. package/build/api/pin.d.ts +3 -19
  13. package/build/api/pin.d.ts.map +1 -1
  14. package/build/api/pin.js +6 -22
  15. package/build/api/stats.d.ts +10 -4
  16. package/build/api/stats.d.ts.map +1 -1
  17. package/build/api/stats.js +6 -32
  18. package/build/api/top-langs.d.ts +12 -6
  19. package/build/api/top-langs.d.ts.map +1 -1
  20. package/build/api/top-langs.js +6 -26
  21. package/build/api/wakatime.d.ts +13 -5
  22. package/build/api/wakatime.d.ts.map +1 -1
  23. package/build/api/wakatime.js +4 -21
  24. package/build/calculateRank.d.ts +0 -9
  25. package/build/calculateRank.d.ts.map +1 -1
  26. package/build/calculateRank.js +12 -17
  27. package/build/cards/gist.d.ts +2 -4
  28. package/build/cards/gist.d.ts.map +1 -1
  29. package/build/cards/gist.js +27 -36
  30. package/build/cards/options.d.ts +1 -1
  31. package/build/cards/repo.d.ts +2 -4
  32. package/build/cards/repo.d.ts.map +1 -1
  33. package/build/cards/repo.js +61 -93
  34. package/build/cards/stats.d.ts +3 -8
  35. package/build/cards/stats.d.ts.map +1 -1
  36. package/build/cards/stats.js +119 -165
  37. package/build/cards/top-languages.d.ts +5 -31
  38. package/build/cards/top-languages.d.ts.map +1 -1
  39. package/build/cards/top-languages.js +176 -384
  40. package/build/cards/wakatime.d.ts +4 -6
  41. package/build/cards/wakatime.d.ts.map +1 -1
  42. package/build/cards/wakatime.js +118 -185
  43. package/build/common/Card.d.ts +14 -62
  44. package/build/common/Card.d.ts.map +1 -1
  45. package/build/common/Card.js +97 -199
  46. package/build/common/I18n.d.ts +0 -6
  47. package/build/common/I18n.d.ts.map +1 -1
  48. package/build/common/I18n.js +3 -6
  49. package/build/common/color.d.ts +4 -17
  50. package/build/common/color.d.ts.map +1 -1
  51. package/build/common/color.js +67 -49
  52. package/build/common/config.d.ts +4 -14
  53. package/build/common/config.d.ts.map +1 -1
  54. package/build/common/config.js +3 -16
  55. package/build/common/constants.d.ts +4 -2
  56. package/build/common/constants.d.ts.map +1 -1
  57. package/build/common/constants.js +3 -1
  58. package/build/common/date.d.ts +0 -2
  59. package/build/common/date.d.ts.map +1 -1
  60. package/build/common/date.js +0 -2
  61. package/build/common/emojiMap.d.ts +8 -0
  62. package/build/common/emojiMap.d.ts.map +1 -0
  63. package/build/common/emojiMap.js +10 -0
  64. package/build/common/emojiMap.json +1915 -0
  65. package/build/common/error.d.ts +1 -10
  66. package/build/common/error.d.ts.map +1 -1
  67. package/build/common/error.js +1 -10
  68. package/build/common/fmt.d.ts +3 -15
  69. package/build/common/fmt.d.ts.map +1 -1
  70. package/build/common/fmt.js +7 -35
  71. package/build/common/http.d.ts +2 -7
  72. package/build/common/http.d.ts.map +1 -1
  73. package/build/common/http.js +8 -17
  74. package/build/common/icons.d.ts +18 -20
  75. package/build/common/icons.d.ts.map +1 -1
  76. package/build/common/icons.js +103 -67
  77. package/build/common/languageColors.d.ts +0 -1
  78. package/build/common/languageColors.d.ts.map +1 -1
  79. package/build/common/languageColors.js +1 -4
  80. package/build/common/languageColors.json +18 -2
  81. package/build/common/ops.d.ts +3 -14
  82. package/build/common/ops.d.ts.map +1 -1
  83. package/build/common/ops.js +19 -30
  84. package/build/common/render.d.ts +28 -82
  85. package/build/common/render.d.ts.map +1 -1
  86. package/build/common/render.js +175 -211
  87. package/build/common/retryer.d.ts +2 -7
  88. package/build/common/retryer.d.ts.map +1 -1
  89. package/build/common/retryer.js +4 -11
  90. package/build/fetchers/gist.d.ts +2 -5
  91. package/build/fetchers/gist.d.ts.map +1 -1
  92. package/build/fetchers/gist.js +1 -5
  93. package/build/fetchers/index.d.ts +12 -12
  94. package/build/fetchers/repo.d.ts +2 -11
  95. package/build/fetchers/repo.d.ts.map +1 -1
  96. package/build/fetchers/repo.js +20 -33
  97. package/build/fetchers/stats.d.ts +2 -32
  98. package/build/fetchers/stats.d.ts.map +1 -1
  99. package/build/fetchers/stats.js +27 -86
  100. package/build/fetchers/top-languages.d.ts +2 -9
  101. package/build/fetchers/top-languages.d.ts.map +1 -1
  102. package/build/fetchers/top-languages.js +15 -31
  103. package/build/fetchers/types.d.ts +1 -1
  104. package/build/fetchers/wakatime.d.ts +2 -6
  105. package/build/fetchers/wakatime.d.ts.map +1 -1
  106. package/build/fetchers/wakatime.js +1 -5
  107. package/build/graphql/contributionsDocument.d.ts +3 -3
  108. package/build/graphql/contributionsDocument.d.ts.map +1 -1
  109. package/build/graphql/contributionsDocument.js +0 -1
  110. package/build/graphql/generated/gist.d.ts +1 -1
  111. package/build/graphql/generated/repo.d.ts +1 -1
  112. package/build/graphql/generated/stats.d.ts +3 -3
  113. package/build/graphql/generated/top-languages.d.ts +2 -2
  114. package/build/graphql/graphqlDocument.d.ts +0 -1
  115. package/build/graphql/graphqlDocument.d.ts.map +1 -1
  116. package/build/graphql/graphqlDocument.js +0 -1
  117. package/build/graphql/reposContributedToDocument.d.ts +4 -5
  118. package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
  119. package/build/graphql/reposContributedToDocument.js +0 -2
  120. package/build/markup/css.d.ts +35 -0
  121. package/build/markup/css.d.ts.map +1 -0
  122. package/build/markup/css.js +64 -0
  123. package/build/markup/element.d.ts +41 -0
  124. package/build/markup/element.d.ts.map +1 -0
  125. package/build/markup/element.js +127 -0
  126. package/build/markup/html.d.ts +13 -0
  127. package/build/markup/html.d.ts.map +1 -0
  128. package/build/markup/html.js +27 -0
  129. package/build/markup/index.d.ts +9 -0
  130. package/build/markup/index.d.ts.map +1 -0
  131. package/build/markup/index.js +6 -0
  132. package/build/markup/serialize.d.ts +16 -0
  133. package/build/markup/serialize.d.ts.map +1 -0
  134. package/build/markup/serialize.js +17 -0
  135. package/build/themes/index.d.ts +332 -333
  136. package/build/themes/index.d.ts.map +1 -1
  137. package/build/themes/index.js +1 -1
  138. package/build/translations.d.ts +2 -6
  139. package/build/translations.d.ts.map +1 -1
  140. package/build/translations.js +621 -628
  141. package/package.json +17 -26
  142. package/src/api/api-result.ts +10 -9
  143. package/src/api/gist.ts +11 -18
  144. package/src/api/index.ts +12 -12
  145. package/src/api/params.ts +27 -21
  146. package/src/api/pin.ts +13 -28
  147. package/src/api/stats.ts +13 -39
  148. package/src/api/top-langs.ts +13 -33
  149. package/src/api/wakatime.ts +11 -28
  150. package/src/calculateRank.ts +12 -23
  151. package/src/cards/gist.ts +40 -43
  152. package/src/cards/options.ts +1 -1
  153. package/src/cards/repo.ts +97 -107
  154. package/src/cards/stats.ts +165 -184
  155. package/src/cards/top-languages.ts +297 -413
  156. package/src/cards/wakatime.ts +193 -221
  157. package/src/common/Card.ts +165 -217
  158. package/src/common/I18n.ts +3 -6
  159. package/src/common/color.ts +80 -57
  160. package/src/common/config.ts +5 -18
  161. package/src/common/constants.ts +5 -2
  162. package/src/common/date.ts +0 -2
  163. package/src/common/emojiMap.json +1915 -0
  164. package/src/common/emojiMap.ts +13 -0
  165. package/src/common/error.ts +2 -11
  166. package/src/common/fmt.ts +7 -44
  167. package/src/common/http.ts +9 -15
  168. package/src/common/icons.ts +117 -42
  169. package/src/common/languageColors.json +18 -2
  170. package/src/common/languageColors.ts +1 -4
  171. package/src/common/ops.ts +26 -34
  172. package/src/common/render.ts +279 -232
  173. package/src/common/retryer.ts +8 -15
  174. package/src/fetchers/gist.ts +8 -12
  175. package/src/fetchers/index.ts +12 -12
  176. package/src/fetchers/repo.ts +27 -40
  177. package/src/fetchers/stats.ts +44 -102
  178. package/src/fetchers/top-languages.ts +26 -40
  179. package/src/fetchers/types.ts +1 -1
  180. package/src/fetchers/wakatime.ts +5 -9
  181. package/src/graphql/contributionsDocument.ts +7 -5
  182. package/src/graphql/generated/gist.ts +1 -1
  183. package/src/graphql/generated/repo.ts +1 -1
  184. package/src/graphql/generated/stats.ts +2 -2
  185. package/src/graphql/generated/top-languages.ts +2 -2
  186. package/src/graphql/graphqlDocument.ts +0 -1
  187. package/src/graphql/reposContributedToDocument.ts +6 -7
  188. package/src/markup/css.ts +118 -0
  189. package/src/markup/element.ts +246 -0
  190. package/src/markup/html.ts +34 -0
  191. package/src/markup/index.ts +9 -0
  192. package/src/markup/serialize.ts +27 -0
  193. package/src/themes/index.ts +1 -1
  194. package/src/translations.ts +622 -629
  195. package/build/cards/index.d.ts +0 -11
  196. package/build/cards/index.d.ts.map +0 -1
  197. package/build/cards/index.js +0 -7
  198. package/build/common/html.d.ts +0 -8
  199. package/build/common/html.d.ts.map +0 -1
  200. package/build/common/html.js +0 -14
  201. package/build/index.d.ts +0 -15
  202. package/build/index.d.ts.map +0 -1
  203. package/build/index.js +0 -10
  204. package/src/_emoji-name-map.d.ts +0 -10
  205. package/src/_github-username-regex.d.ts +0 -4
  206. package/src/cards/index.ts +0 -21
  207. package/src/common/html.ts +0 -17
  208. package/src/index.ts +0 -18
@@ -1,7 +1,7 @@
1
- import type { CardConfig } from './config.js';
2
- import { CardError } from './error.js';
3
- import type { FetcherContext, HttpResponse } from './http.js';
4
- import { logger } from './log.js';
1
+ import type { CardConfig } from './config.ts';
2
+ import { CardError } from './error.ts';
3
+ import type { FetcherContext, HttpResponse } from './http.ts';
4
+ import { logger } from './log.ts';
5
5
 
6
6
  /**
7
7
  * Error-detection fields the retryer inspects to detect rate-limiting and credential failures.
@@ -19,8 +19,6 @@ interface ResponseErrors {
19
19
  * The value is generated using `Math.random()` and uniformly distributed
20
20
  * across the range.
21
21
  *
22
- * @param max The upper bound (exclusive). Must be a positive number.
23
- *
24
22
  * @returns A random integer `n` such that `0 <= n < max`.
25
23
  */
26
24
  function getRandomInt(max: number): number {
@@ -44,11 +42,6 @@ type FetcherFunction<TData = unknown, TVariables = Record<string, unknown>> = (
44
42
  /**
45
43
  * Try to execute the fetcher function until it succeeds or the max number of retries is reached.
46
44
  *
47
- * @template TData Shape of `response.data` returned by the fetcher.
48
- * @template TVariables Variables the fetcher accepts.
49
- * @param fetcher The fetcher function.
50
- * @param variables Object with arguments to pass to the fetcher function.
51
- * @param config Deployment config supplying the PAT pool and the transport.
52
45
  * @returns The response from the fetcher function.
53
46
  */
54
47
  const retryer = async <TData = unknown, TVariables = Record<string, unknown>>(
@@ -58,12 +51,12 @@ const retryer = async <TData = unknown, TVariables = Record<string, unknown>>(
58
51
  ): Promise<FetcherResponse<TData>> => {
59
52
  const PATs = config.pats;
60
53
 
61
- if (!PATs.length) {
54
+ if (PATs.length === 0) {
62
55
  throw new CardError('No GitHub API tokens found', { code: 'no_tokens' });
63
56
  }
64
57
  const startPAT = getRandomInt(PATs.length);
65
58
 
66
- for (let retries = 0; retries < PATs.length; retries++) {
59
+ for (let retries = 0; retries < PATs.length; retries += 1) {
67
60
  const currentPAT = PATs[(startPAT + retries) % PATs.length];
68
61
  if (!currentPAT) {
69
62
  continue;
@@ -77,7 +70,7 @@ const retryer = async <TData = unknown, TVariables = Record<string, unknown>>(
77
70
 
78
71
  // react on both type and message-based rate-limit signals.
79
72
  // https://github.com/anuraghazra/github-readme-stats/issues/4425
80
- const errors = response.data.errors;
73
+ const { errors } = response.data;
81
74
  const errorType = errors?.[0]?.type;
82
75
  const errorMsg = errors?.[0]?.message ?? '';
83
76
  const isRateLimited =
@@ -89,7 +82,7 @@ const retryer = async <TData = unknown, TVariables = Record<string, unknown>>(
89
82
  }
90
83
 
91
84
  // also checking for bad credentials if any tokens gets invalidated
92
- const message = response.data.message;
85
+ const { message } = response.data;
93
86
  const isBadCredential = message === 'Bad credentials';
94
87
  const isAccountSuspended = message === 'Sorry. Your account was suspended.';
95
88
 
@@ -1,18 +1,17 @@
1
- import type { CardConfig } from '../common/config.js';
2
- import { CardError, GIST_NOT_FOUND } from '../common/error.js';
3
- import { createGraphQLFetcher } from '../common/http.js';
4
- import { retryer } from '../common/retryer.js';
5
- import { GistInfoDocument } from '../graphql/generated/gist.js';
6
- import type { GistFileInfoFragment } from '../graphql/generated/gist.js';
1
+ import type { CardConfig } from '../common/config.ts';
2
+ import { CardError, GIST_NOT_FOUND } from '../common/error.ts';
3
+ import { createGraphQLFetcher } from '../common/http.ts';
4
+ import { retryer } from '../common/retryer.ts';
5
+ import { GistInfoDocument } from '../graphql/generated/gist.ts';
6
+ import type { GistFileInfoFragment } from '../graphql/generated/gist.ts';
7
7
 
8
- import type { GistData } from './types.js';
8
+ import type { GistData } from './types.ts';
9
9
 
10
10
  const fetcher = createGraphQLFetcher(GistInfoDocument, 'token');
11
11
 
12
12
  /**
13
13
  * This function calculates the primary language of a gist by files size.
14
14
  *
15
- * @param files Files.
16
15
  * @returns Primary language, or `null` when no file has a language.
17
16
  */
18
17
  const calculatePrimaryLanguage = (files: Array<GistFileInfoFragment>): string | null => {
@@ -39,9 +38,6 @@ const calculatePrimaryLanguage = (files: Array<GistFileInfoFragment>): string |
39
38
  /**
40
39
  * Fetch GitHub gist information by given username and ID.
41
40
  *
42
- * @param props Fetcher props.
43
- * @param props.id GitHub gist ID.
44
- * @param config Deployment config supplying the PAT pool.
45
41
  * @returns Gist data.
46
42
  */
47
43
  const fetchGist = async (
@@ -57,7 +53,7 @@ const fetchGist = async (
57
53
  code: 'upstream',
58
54
  });
59
55
  }
60
- const gist = res.data.data.viewer.gist;
56
+ const { gist } = res.data.data.viewer;
61
57
  if (!gist) {
62
58
  throw new CardError('Gist not found', {
63
59
  code: 'not_found',
@@ -1,8 +1,8 @@
1
- export { fetchGist } from './gist.js';
2
- export { fetchRepo } from './repo.js';
3
- export { fetchRepoUserStats, fetchStats } from './stats.js';
4
- export { fetchTopLanguages } from './top-languages.js';
5
- export { fetchWakatimeStats } from './wakatime.js';
1
+ export { fetchGist } from './gist.ts';
2
+ export { fetchRepo } from './repo.ts';
3
+ export { fetchRepoUserStats, fetchStats } from './stats.ts';
4
+ export { fetchTopLanguages } from './top-languages.ts';
5
+ export { fetchWakatimeStats } from './wakatime.ts';
6
6
 
7
7
  export type {
8
8
  GistData,
@@ -13,13 +13,13 @@ export type {
13
13
  TopLangData,
14
14
  WakaTimeData,
15
15
  WakaTimeLang,
16
- } from './types.js';
16
+ } from './types.ts';
17
17
 
18
18
  // every fetcher takes a config, so this entry carries it rather than sending a
19
19
  // consumer back to the package root for it.
20
- export { CardConfig } from '../common/config.js';
21
- export type { CardConfigInit, PersonalAccessToken } from '../common/config.js';
22
- export type { FetchLike } from '../common/http.js';
23
- export { retryer } from '../common/retryer.js';
24
- export { CardError } from '../common/error.js';
25
- export type { ErrorCode } from '../common/error.js';
20
+ export { CardConfig } from '../common/config.ts';
21
+ export type { CardConfigInit, PersonalAccessToken } from '../common/config.ts';
22
+ export type { FetchLike } from '../common/http.ts';
23
+ export { retryer } from '../common/retryer.ts';
24
+ export { CardError } from '../common/error.ts';
25
+ export type { ErrorCode } from '../common/error.ts';
@@ -1,11 +1,11 @@
1
- import type { CardConfig } from '../common/config.js';
2
- import { CardError, REPO_NOT_FOUND } from '../common/error.js';
3
- import { createGraphQLFetcher } from '../common/http.js';
4
- import { retryer } from '../common/retryer.js';
5
- import { GetRepoDocument } from '../graphql/generated/repo.js';
1
+ import type { CardConfig } from '../common/config.ts';
2
+ import { CardError, REPO_NOT_FOUND } from '../common/error.ts';
3
+ import { createGraphQLFetcher } from '../common/http.ts';
4
+ import { retryer } from '../common/retryer.ts';
5
+ import { GetRepoDocument } from '../graphql/generated/repo.ts';
6
6
 
7
- import { fetchRepoUserStats } from './stats.js';
8
- import type { RepositoryData } from './types.js';
7
+ import { fetchRepoUserStats } from './stats.ts';
8
+ import type { RepositoryData } from './types.ts';
9
9
 
10
10
  const fetcher = createGraphQLFetcher(GetRepoDocument, 'token');
11
11
 
@@ -14,15 +14,6 @@ const urlExample = '/api/pin?username=USERNAME&repo=REPO_NAME';
14
14
  /**
15
15
  * Fetch repository data.
16
16
  *
17
- * @param props Fetcher props.
18
- * @param props.username GitHub username.
19
- * @param props.reponame GitHub repository name.
20
- * @param props.include_prs_authored Include count of PRs authored.
21
- * @param props.include_prs_commented Include count of PRs commented.
22
- * @param props.include_prs_reviewed Include count of PRs reviewed.
23
- * @param props.include_issues_authored Include count of issues authored.
24
- * @param props.include_issues_commented Include count of issues commented.
25
- * @param config Deployment config supplying the PAT pool.
26
17
  * @returns Repository data.
27
18
  */
28
19
  const fetchRepo = async (
@@ -45,35 +36,31 @@ const fetchRepo = async (
45
36
  },
46
37
  config: CardConfig,
47
38
  ): Promise<RepositoryData> => {
48
- let owner = username;
49
- if (reponame && reponame.includes('/')) {
50
- const [parsedOwner, parsedRepo] = reponame.split('/');
51
- owner = parsedOwner ?? '';
52
- reponame = parsedRepo ?? '';
39
+ // `?repo=owner/name` carries the owner itself, and then it wins over `?username=`.
40
+ let parsedOwner = username;
41
+ let repo = reponame;
42
+ if (repo?.includes('/')) {
43
+ const [ownerFromRepo, nameFromRepo] = repo.split('/');
44
+ parsedOwner = ownerFromRepo ?? '';
45
+ repo = nameFromRepo ?? '';
53
46
  }
54
47
 
55
- if (owner && !username) {
56
- username = owner;
57
- }
58
- if (username && !owner) {
59
- owner = username;
60
- }
61
- if (!username && !reponame) {
48
+ const login = username || parsedOwner;
49
+ if (!login && !repo) {
62
50
  throw CardError.missingParam(['username', 'repo'], urlExample);
63
51
  }
64
- if (!username) {
52
+ if (!login) {
65
53
  throw CardError.missingParam(['username'], urlExample);
66
54
  }
67
- if (!reponame) {
55
+ if (!repo) {
68
56
  throw CardError.missingParam(['repo'], urlExample);
69
57
  }
70
58
 
71
- // the guards above leave `username` set, and `owner` mirrors it when `repo` carried none
72
- const repoOwner = owner ?? username;
59
+ const repoOwner = parsedOwner || login;
73
60
 
74
- const res = await retryer(fetcher, { login: repoOwner, repo: reponame }, config);
61
+ const res = await retryer(fetcher, { login: repoOwner, repo }, config);
75
62
 
76
- const data = res.data.data;
63
+ const { data } = res.data;
77
64
 
78
65
  if (!data.user && !data.organization) {
79
66
  throw new CardError('Not found', {
@@ -83,7 +70,7 @@ const fetchRepo = async (
83
70
  }
84
71
 
85
72
  if (data.organization === null && data.user) {
86
- const repository = data.user.repository;
73
+ const { repository } = data.user;
87
74
  if (!repository || repository.isPrivate) {
88
75
  throw new CardError('User Repository Not found', {
89
76
  code: 'not_found',
@@ -92,8 +79,8 @@ const fetchRepo = async (
92
79
  }
93
80
  const repoUserStats = await fetchRepoUserStats(
94
81
  {
95
- username,
96
- repo: [`${repoOwner}/${reponame}`],
82
+ username: login,
83
+ repo: [`${repoOwner}/${repo}`],
97
84
  include_prs_authored,
98
85
  include_prs_commented,
99
86
  include_prs_reviewed,
@@ -109,7 +96,7 @@ const fetchRepo = async (
109
96
  }
110
97
 
111
98
  if (data.user === null && data.organization) {
112
- const repository = data.organization.repository;
99
+ const { repository } = data.organization;
113
100
  if (!repository || repository.isPrivate) {
114
101
  throw new CardError('Organization Repository Not found', {
115
102
  code: 'not_found',
@@ -118,8 +105,8 @@ const fetchRepo = async (
118
105
  }
119
106
  const repoUserStats = await fetchRepoUserStats(
120
107
  {
121
- username,
122
- repo: [`${repoOwner}/${reponame}`],
108
+ username: login,
109
+ repo: [`${repoOwner}/${repo}`],
123
110
  include_prs_authored,
124
111
  include_prs_commented,
125
112
  include_prs_reviewed,
@@ -1,30 +1,29 @@
1
- import githubUsernameRegex from 'github-username-regex';
2
-
3
- import { calculateRank } from '../calculateRank.js';
4
- import type { CardConfig } from '../common/config.js';
5
- import type { GitHubDateRange } from '../common/date.js';
6
- import { getGitHubYearRange, toGitHubDateTime } from '../common/date.js';
7
- import { CardError, USER_NOT_FOUND } from '../common/error.js';
8
- import { wrapTextMultiline } from '../common/fmt.js';
9
- import { createGraphQLFetcher, httpRequest } from '../common/http.js';
10
- import type { FetcherContext, GraphQLResponse } from '../common/http.js';
11
- import { logger } from '../common/log.js';
12
- import { buildSearchFilter, chunkArray, parseOwnerAffiliations } from '../common/ops.js';
13
- import { retryer } from '../common/retryer.js';
14
- import type { FetcherResponse } from '../common/retryer.js';
15
- import { buildContributionsDocument } from '../graphql/contributionsDocument.js';
16
- import { UserInfoDocument, UserReposDocument } from '../graphql/generated/stats.js';
1
+ import { calculateRank } from '../calculateRank.ts';
2
+ import type { CardConfig } from '../common/config.ts';
3
+ import { GITHUB_USERNAME_PATTERN } from '../common/constants.ts';
4
+ import type { GitHubDateRange } from '../common/date.ts';
5
+ import { getGitHubYearRange, toGitHubDateTime } from '../common/date.ts';
6
+ import { CardError, USER_NOT_FOUND } from '../common/error.ts';
7
+ import { createGraphQLFetcher, httpRequest } from '../common/http.ts';
8
+ import type { FetcherContext, GraphQLResponse } from '../common/http.ts';
9
+ import { logger } from '../common/log.ts';
10
+ import { buildSearchFilter, chunkArray, parseOwnerAffiliations } from '../common/ops.ts';
11
+ import { wrapTextMultiline } from '../common/render.ts';
12
+ import { retryer } from '../common/retryer.ts';
13
+ import type { FetcherResponse } from '../common/retryer.ts';
14
+ import { buildContributionsDocument } from '../graphql/contributionsDocument.ts';
15
+ import { UserInfoDocument, UserReposDocument } from '../graphql/generated/stats.ts';
17
16
  import type {
18
17
  RepoNodeFragment,
19
18
  UserInfoQuery,
20
19
  UserInfoQueryVariables,
21
- } from '../graphql/generated/stats.js';
20
+ } from '../graphql/generated/stats.ts';
22
21
  import {
23
22
  MAX_REPOSITORIES_LIMIT,
24
23
  buildReposContributedToDocument,
25
- } from '../graphql/reposContributedToDocument.js';
24
+ } from '../graphql/reposContributedToDocument.ts';
26
25
 
27
- import type { RepoUserStats, StatsData } from './types.js';
26
+ import type { RepoUserStats, StatsData } from './types.ts';
28
27
 
29
28
  /** The subset of the stats response `statsFetcher` returns and threads on. */
30
29
  type StatsFetcherResponse = Pick<GraphQLResponse<UserInfoQuery>, 'data' | 'statusText'>;
@@ -36,19 +35,10 @@ const reposFetcher = createGraphQLFetcher(UserReposDocument, 'bearer');
36
35
  /**
37
36
  * Fetch stats information for a given username.
38
37
  *
39
- * @param variables Fetcher variables.
40
- * @param variables.username GitHub username.
41
- * @param variables.includeMergedPullRequests Include merged pull requests.
42
- * @param variables.includeDiscussions Include discussions.
43
- * @param variables.includeDiscussionsAnswers Include discussions answers.
44
- * @param variables.startTime Time to start the count of total commits.
45
- * @param variables.ownerAffiliations The owner affiliations to filter by. Default: OWNER.
46
- * @param variables.includeUserRepositories Whether to include the user's own repositories in the repos contributed to.
47
- * @param config Deployment config supplying the PAT pool.
48
- * @returns The stats response, with every fetched page's repos merged in.
49
- *
50
38
  * @description Supports multi-page fetching when the `FETCH_MULTI_PAGE_STARS`
51
39
  * env variable is `true` or a fetch limit.
40
+ *
41
+ * @returns The stats response, with every fetched page's repos merged in.
52
42
  */
53
43
  const statsFetcher = async (
54
44
  {
@@ -118,11 +108,11 @@ const statsFetcher = async (
118
108
 
119
109
  pageRepositories = page.data.data.user?.repositories;
120
110
  extraRepoNodes.push(...(pageRepositories?.nodes ?? []));
121
- fetchedPages++;
111
+ fetchedPages += 1;
122
112
  }
123
113
 
124
114
  if (extraRepoNodes.length > 0) {
125
- // deep copy to avoid mutating the response cached by the frontend
115
+ // defensive: the merge below is the only place a parsed response is mutated
126
116
  stats = structuredClone({
127
117
  data: stats.data,
128
118
  statusText: stats.statusText,
@@ -136,13 +126,9 @@ const statsFetcher = async (
136
126
  /**
137
127
  * Fetch total items count using the REST search API.
138
128
  *
139
- * @param variables Fetcher variables.
140
- * @param token GitHub token.
141
- * @param context What the retryer supplies each attempt.
142
- * @param context.fetch Transport to send the request with.
143
- * @returns The search response, carrying `total_count`.
144
- *
145
129
  * @see https://developer.github.com/v3/search/#search-commits
130
+ *
131
+ * @returns The search response, carrying `total_count`.
146
132
  */
147
133
  const fetchTotalItems = (
148
134
  variables: Record<string, unknown>,
@@ -155,9 +141,10 @@ const fetchTotalItems = (
155
141
  const owner = variables['owner'] as Array<string> | string;
156
142
  return httpRequest(
157
143
  fetch,
158
- `https://api.github.com/search/${type}?per_page=1&q=` +
159
- buildSearchFilter(repo, owner).replaceAll(' ', '+') +
160
- filter,
144
+ `https://api.github.com/search/${type}?per_page=1&q=${buildSearchFilter(repo, owner).replaceAll(
145
+ ' ',
146
+ '+',
147
+ )}${filter}`,
161
148
  {
162
149
  method: 'GET',
163
150
  headers: {
@@ -172,18 +159,11 @@ const fetchTotalItems = (
172
159
  /**
173
160
  * Fetch a total count for a given username via the REST search API.
174
161
  *
175
- * @param props Fetcher props.
176
- * @param props.username GitHub username.
177
- * @param props.repo Repositories the search is scoped to.
178
- * @param props.owner Owners the search is scoped to.
179
- * @param props.type Search endpoint to hit, `issues` or `commits`.
180
- * @param props.filter Query fragment appended to the scope filter.
181
- * @param config Deployment config supplying the PAT pool.
182
- * @returns Total count.
183
- *
184
162
  * The GraphQL API can't return this.
185
163
  * @see https://github.com/anuraghazra/github-readme-stats/issues/92#issuecomment-661026467
186
164
  * @see https://github.com/anuraghazra/github-readme-stats/pull/211
165
+ *
166
+ * @returns Total count.
187
167
  */
188
168
  const totalItemsFetcher = async (
189
169
  {
@@ -201,7 +181,7 @@ const totalItemsFetcher = async (
201
181
  },
202
182
  config: CardConfig,
203
183
  ): Promise<number> => {
204
- if (!githubUsernameRegex.test(username)) {
184
+ if (!GITHUB_USERNAME_PATTERN.test(username)) {
205
185
  logger.log('Invalid username provided.');
206
186
  throw new CardError('Invalid username provided.', {
207
187
  code: 'invalid_param',
@@ -216,14 +196,14 @@ const totalItemsFetcher = async (
216
196
  { login: username, repo, owner, type, filter },
217
197
  config,
218
198
  );
219
- } catch (err) {
220
- logger.log(err);
221
- throw err;
199
+ } catch (error) {
200
+ logger.log(error);
201
+ throw error;
222
202
  }
223
203
 
224
204
  const totalCount = res.data.total_count;
225
- if (totalCount === undefined || isNaN(totalCount)) {
226
- logger.error('GitHub error: ' + JSON.stringify(res.data));
205
+ if (typeof totalCount !== 'number' || Number.isNaN(totalCount)) {
206
+ logger.error(`GitHub error: ${JSON.stringify(res.data)}`);
227
207
  throw new CardError('Could not fetch data from GitHub REST API.', {
228
208
  code: 'upstream',
229
209
  });
@@ -234,16 +214,6 @@ const totalItemsFetcher = async (
234
214
  /**
235
215
  * Fetch the per-repository counts the REST search API answers, one request each.
236
216
  *
237
- * @param props Fetcher props.
238
- * @param props.username GitHub username.
239
- * @param props.repo Repositories the search is scoped to.
240
- * @param props.owner Owners the search is scoped to.
241
- * @param props.include_prs_authored Include count of PRs authored.
242
- * @param props.include_prs_commented Include count of PRs commented.
243
- * @param props.include_prs_reviewed Include count of PRs reviewed.
244
- * @param props.include_issues_authored Include count of issues authored.
245
- * @param props.include_issues_commented Include count of issues commented.
246
- * @param config Deployment config supplying the PAT pool.
247
217
  * @returns Only the counts that were asked for.
248
218
  */
249
219
  const fetchRepoUserStats = async (
@@ -334,10 +304,6 @@ const fetchRepoUserStats = async (
334
304
 
335
305
  /**
336
306
  * Turn a GraphQL `errors` payload into the error to throw.
337
- *
338
- * @param errors Errors from the response envelope.
339
- * @param statusText HTTP status text, used as the error type when GitHub gave a message.
340
- * @param fallback Message when GitHub gave none.
341
307
  */
342
308
  const graphqlError = (
343
309
  errors: NonNullable<GraphQLResponse<unknown>['data']['errors']>,
@@ -382,7 +348,7 @@ const fetchTotalContributions = async (
382
348
  );
383
349
  }
384
350
 
385
- const user = contribRes.data.data.user;
351
+ const { user } = contribRes.data.data;
386
352
  if (!user) {
387
353
  return 0;
388
354
  }
@@ -420,39 +386,35 @@ const REPOS_CONTRIBUTED_TO_ERROR =
420
386
  *
421
387
  * Whether private contributions are included depends on the used PAT.
422
388
  *
423
- * @param canonicalUsername Username as GitHub reports it; `nameWithOwner` uses its
424
- * casing, which the raw query-string username need not match.
425
- * @param years Contribution years to walk.
426
- * @param includeOwnRepos Whether to count the user's own repositories.
427
- * @param config Deployment config supplying the PAT pool.
428
389
  * @returns Count of repositories.
429
390
  */
430
391
  const fetchAllTimeReposContributedTo = async (
392
+ /** `nameWithOwner` uses GitHub's casing, which the query-string username need not match */
431
393
  canonicalUsername: string,
432
394
  years: Array<number>,
433
395
  includeOwnRepos: boolean,
434
396
  config: CardConfig,
435
397
  ): Promise<number> => {
436
398
  const repos = new Set<string>();
437
- let pending = years.map(getGitHubYearRange);
399
+ let pending = years.map((year) => getGitHubYearRange(year));
438
400
 
439
401
  while (pending.length > 0) {
440
402
  const nextPending: Array<GitHubDateRange> = [];
441
403
 
442
404
  for (const chunk of chunkArray(pending, MAX_RANGES_PER_REQUEST)) {
443
- const fetcher = createGraphQLFetcher(
405
+ const chunkFetcher = createGraphQLFetcher(
444
406
  buildReposContributedToDocument(chunk, includeOwnRepos),
445
407
  'bearer',
446
408
  );
447
409
  const res = await retryer(
448
- fetcher,
410
+ chunkFetcher,
449
411
  { login: canonicalUsername, maxRepositories: MAX_REPOSITORIES_LIMIT },
450
412
  config,
451
413
  );
452
414
  if (res.data.errors) {
453
415
  throw graphqlError(res.data.errors, res.statusText, REPOS_CONTRIBUTED_TO_ERROR);
454
416
  }
455
- const user = res.data.data.user;
417
+ const { user } = res.data.data;
456
418
  if (!user) {
457
419
  throw new CardError(REPOS_CONTRIBUTED_TO_ERROR, { code: 'upstream' });
458
420
  }
@@ -515,26 +477,6 @@ const fetchAllTimeReposContributedTo = async (
515
477
  /**
516
478
  * Fetch stats for a given username.
517
479
  *
518
- * @param props Fetcher props.
519
- * @param props.username GitHub username.
520
- * @param props.include_all_commits Include all commits.
521
- * @param props.exclude_repo Repositories to exclude.
522
- * @param props.include_merged_pull_requests Include merged pull requests.
523
- * @param props.include_discussions Include discussions.
524
- * @param props.include_discussions_answers Include discussions answers.
525
- * @param props.commits_year Year to count total commits.
526
- * @param props.repo Repositories to scope the REST search to.
527
- * @param props.owner Owners to scope the REST search to.
528
- * @param props.include_prs_authored Include count of PRs authored.
529
- * @param props.include_prs_commented Include count of PRs commented.
530
- * @param props.include_prs_reviewed Include count of PRs reviewed.
531
- * @param props.include_issues_authored Include count of issues authored.
532
- * @param props.include_issues_commented Include count of issues commented.
533
- * @param props.ownerAffiliations Owner affiliations. Default: OWNER.
534
- * @param props.include_contributions Include all-time contributions.
535
- * @param props.include_all_time_contribs Include all-time count of repos contributed to.
536
- * @param props.contribs_include_own_repos Include user-owned repos in contributed-to counts.
537
- * @param config Deployment config supplying the PAT pool.
538
480
  * @returns Stats data.
539
481
  */
540
482
  const fetchStats = async (
@@ -625,7 +567,7 @@ const fetchStats = async (
625
567
  // Catch GraphQL errors.
626
568
  if (res.data.errors) {
627
569
  logger.error(res.data.errors);
628
- const firstError = res.data.errors[0];
570
+ const [firstError] = res.data.errors;
629
571
  if (firstError?.type === 'NOT_FOUND') {
630
572
  throw new CardError(firstError.message || 'Could not fetch user.', {
631
573
  code: 'not_found',
@@ -644,7 +586,7 @@ const fetchStats = async (
644
586
  );
645
587
  }
646
588
 
647
- const user = res.data.data.user;
589
+ const { user } = res.data.data;
648
590
  if (!user) {
649
591
  throw new CardError('Could not fetch user.', {
650
592
  code: 'not_found',