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

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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/build/api/api-result.d.ts +44 -0
  4. package/build/api/api-result.d.ts.map +1 -0
  5. package/build/api/api-result.js +33 -0
  6. package/build/api/gist.d.ts +29 -0
  7. package/build/api/gist.d.ts.map +1 -0
  8. package/build/api/gist.js +52 -0
  9. package/build/api/index.d.ts +13 -0
  10. package/build/api/index.d.ts.map +1 -0
  11. package/build/api/index.js +8 -0
  12. package/build/api/params.d.ts +93 -0
  13. package/build/api/params.d.ts.map +1 -0
  14. package/build/api/params.js +145 -0
  15. package/build/api/pin.d.ts +47 -0
  16. package/build/api/pin.d.ts.map +1 -0
  17. package/build/api/pin.js +87 -0
  18. package/build/api/stats.d.ts +42 -0
  19. package/build/api/stats.d.ts.map +1 -0
  20. package/build/api/stats.js +132 -0
  21. package/build/api/top-langs.d.ts +37 -0
  22. package/build/api/top-langs.d.ts.map +1 -0
  23. package/build/api/top-langs.js +101 -0
  24. package/build/api/wakatime.d.ts +34 -0
  25. package/build/api/wakatime.d.ts.map +1 -0
  26. package/build/api/wakatime.js +91 -0
  27. package/build/calculateRank.d.ts +29 -0
  28. package/build/calculateRank.d.ts.map +1 -0
  29. package/build/calculateRank.js +58 -0
  30. package/build/cards/gist.d.ts +16 -0
  31. package/build/cards/gist.d.ts.map +1 -0
  32. package/build/cards/gist.js +119 -0
  33. package/build/cards/index.d.ts +11 -0
  34. package/build/cards/index.d.ts.map +1 -0
  35. package/build/cards/index.js +7 -0
  36. package/build/cards/options.d.ts +19 -0
  37. package/build/cards/options.d.ts.map +1 -0
  38. package/build/cards/options.js +1 -0
  39. package/build/cards/repo.d.ts +25 -0
  40. package/build/cards/repo.d.ts.map +1 -0
  41. package/build/cards/repo.js +276 -0
  42. package/build/cards/stats.d.ts +37 -0
  43. package/build/cards/stats.d.ts.map +1 -0
  44. package/build/cards/stats.js +457 -0
  45. package/build/cards/top-languages.d.ts +153 -0
  46. package/build/cards/top-languages.d.ts.map +1 -0
  47. package/build/cards/top-languages.js +805 -0
  48. package/build/cards/wakatime.d.ts +31 -0
  49. package/build/cards/wakatime.d.ts.map +1 -0
  50. package/build/cards/wakatime.js +376 -0
  51. package/build/common/Card.d.ts +121 -0
  52. package/build/common/Card.d.ts.map +1 -0
  53. package/build/common/Card.js +305 -0
  54. package/build/common/I18n.d.ts +27 -0
  55. package/build/common/I18n.d.ts.map +1 -0
  56. package/build/common/I18n.js +35 -0
  57. package/build/common/color.d.ts +112 -0
  58. package/build/common/color.d.ts.map +1 -0
  59. package/build/common/color.js +188 -0
  60. package/build/common/config.d.ts +53 -0
  61. package/build/common/config.d.ts.map +1 -0
  62. package/build/common/config.js +87 -0
  63. package/build/common/constants.d.ts +7 -0
  64. package/build/common/constants.d.ts.map +1 -0
  65. package/build/common/constants.js +9 -0
  66. package/build/common/date.d.ts +29 -0
  67. package/build/common/date.d.ts.map +1 -0
  68. package/build/common/date.js +23 -0
  69. package/build/common/error.d.ts +87 -0
  70. package/build/common/error.d.ts.map +1 -0
  71. package/build/common/error.js +108 -0
  72. package/build/common/fmt.d.ts +28 -0
  73. package/build/common/fmt.d.ts.map +1 -0
  74. package/build/common/fmt.js +68 -0
  75. package/build/common/html.d.ts +8 -0
  76. package/build/common/html.d.ts.map +1 -0
  77. package/build/common/html.js +14 -0
  78. package/build/common/http.d.ts +53 -0
  79. package/build/common/http.d.ts.map +1 -0
  80. package/build/common/http.js +47 -0
  81. package/build/common/icons.d.ts +28 -0
  82. package/build/common/icons.d.ts.map +1 -0
  83. package/build/common/icons.js +77 -0
  84. package/build/common/languageColors.d.ts +11 -0
  85. package/build/common/languageColors.d.ts.map +1 -0
  86. package/build/common/languageColors.js +15 -0
  87. package/build/common/languageColors.json +678 -0
  88. package/build/common/log.d.ts +9 -0
  89. package/build/common/log.d.ts.map +1 -0
  90. package/build/common/log.js +8 -0
  91. package/build/common/ops.d.ts +59 -0
  92. package/build/common/ops.d.ts.map +1 -0
  93. package/build/common/ops.js +115 -0
  94. package/build/common/render.d.ts +196 -0
  95. package/build/common/render.d.ts.map +1 -0
  96. package/build/common/render.js +474 -0
  97. package/build/common/retryer.d.ts +36 -0
  98. package/build/common/retryer.d.ts.map +1 -0
  99. package/build/common/retryer.js +67 -0
  100. package/build/fetchers/gist.d.ts +15 -0
  101. package/build/fetchers/gist.d.ts.map +1 -0
  102. package/build/fetchers/gist.js +68 -0
  103. package/build/fetchers/index.d.ts +13 -0
  104. package/build/fetchers/index.d.ts.map +1 -0
  105. package/build/fetchers/index.js +10 -0
  106. package/build/fetchers/repo.d.ts +27 -0
  107. package/build/fetchers/repo.d.ts.map +1 -0
  108. package/build/fetchers/repo.js +100 -0
  109. package/build/fetchers/stats.d.ts +74 -0
  110. package/build/fetchers/stats.d.ts.map +1 -0
  111. package/build/fetchers/stats.js +498 -0
  112. package/build/fetchers/top-languages.d.ts +23 -0
  113. package/build/fetchers/top-languages.d.ts.map +1 -0
  114. package/build/fetchers/top-languages.js +89 -0
  115. package/build/fetchers/types.d.ts +130 -0
  116. package/build/fetchers/types.d.ts.map +1 -0
  117. package/build/fetchers/types.js +1 -0
  118. package/build/fetchers/wakatime.d.ts +19 -0
  119. package/build/fetchers/wakatime.d.ts.map +1 -0
  120. package/build/fetchers/wakatime.js +36 -0
  121. package/build/graphql/contributionsDocument.d.ts +18 -0
  122. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  123. package/build/graphql/contributionsDocument.js +30 -0
  124. package/build/graphql/generated/common.d.ts +42 -0
  125. package/build/graphql/generated/common.d.ts.map +1 -0
  126. package/build/graphql/generated/common.js +2 -0
  127. package/build/graphql/generated/gist.d.ts +44 -0
  128. package/build/graphql/generated/gist.d.ts.map +1 -0
  129. package/build/graphql/generated/gist.js +27 -0
  130. package/build/graphql/generated/repo.d.ts +67 -0
  131. package/build/graphql/generated/repo.d.ts.map +1 -0
  132. package/build/graphql/generated/repo.js +30 -0
  133. package/build/graphql/generated/stats.d.ts +150 -0
  134. package/build/graphql/generated/stats.d.ts.map +1 -0
  135. package/build/graphql/generated/stats.js +95 -0
  136. package/build/graphql/generated/top-languages.d.ts +54 -0
  137. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  138. package/build/graphql/generated/top-languages.js +27 -0
  139. package/build/graphql/graphqlDocument.d.ts +22 -0
  140. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  141. package/build/graphql/graphqlDocument.js +8 -0
  142. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  143. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  144. package/build/graphql/reposContributedToDocument.js +59 -0
  145. package/build/index.d.ts +15 -0
  146. package/build/index.d.ts.map +1 -0
  147. package/build/index.js +10 -0
  148. package/build/themes/index.d.ts +505 -0
  149. package/build/themes/index.d.ts.map +1 -0
  150. package/build/themes/index.js +505 -0
  151. package/build/translations.d.ts +1153 -0
  152. package/build/translations.d.ts.map +1 -0
  153. package/build/translations.js +1156 -0
  154. package/package.json +114 -0
  155. package/src/_emoji-name-map.d.ts +10 -0
  156. package/src/_github-username-regex.d.ts +4 -0
  157. package/src/api/api-result.ts +69 -0
  158. package/src/api/gist.ts +66 -0
  159. package/src/api/index.ts +16 -0
  160. package/src/api/params.ts +221 -0
  161. package/src/api/pin.ts +126 -0
  162. package/src/api/stats.ts +195 -0
  163. package/src/api/top-langs.ts +150 -0
  164. package/src/api/wakatime.ts +130 -0
  165. package/src/calculateRank.ts +91 -0
  166. package/src/cards/gist.ts +169 -0
  167. package/src/cards/index.ts +21 -0
  168. package/src/cards/options.ts +18 -0
  169. package/src/cards/repo.ts +376 -0
  170. package/src/cards/stats.ts +615 -0
  171. package/src/cards/top-languages.ts +1077 -0
  172. package/src/cards/wakatime.ts +501 -0
  173. package/src/common/Card.ts +364 -0
  174. package/src/common/I18n.ts +51 -0
  175. package/src/common/color.ts +296 -0
  176. package/src/common/config.ts +118 -0
  177. package/src/common/constants.ts +12 -0
  178. package/src/common/date.ts +34 -0
  179. package/src/common/error.ts +163 -0
  180. package/src/common/fmt.ts +86 -0
  181. package/src/common/html.ts +17 -0
  182. package/src/common/http.ts +98 -0
  183. package/src/common/icons.ts +80 -0
  184. package/src/common/languageColors.json +678 -0
  185. package/src/common/languageColors.ts +19 -0
  186. package/src/common/log.ts +12 -0
  187. package/src/common/ops.ts +145 -0
  188. package/src/common/render.ts +641 -0
  189. package/src/common/retryer.ts +111 -0
  190. package/src/fetchers/gist.ts +82 -0
  191. package/src/fetchers/index.ts +25 -0
  192. package/src/fetchers/repo.ts +140 -0
  193. package/src/fetchers/stats.ts +742 -0
  194. package/src/fetchers/top-languages.ts +134 -0
  195. package/src/fetchers/types.ts +136 -0
  196. package/src/fetchers/wakatime.ts +56 -0
  197. package/src/graphql/contributionsDocument.ts +44 -0
  198. package/src/graphql/generated/common.ts +26 -0
  199. package/src/graphql/generated/gist.ts +56 -0
  200. package/src/graphql/generated/repo.ts +79 -0
  201. package/src/graphql/generated/stats.ts +180 -0
  202. package/src/graphql/generated/top-languages.ts +66 -0
  203. package/src/graphql/graphqlDocument.ts +25 -0
  204. package/src/graphql/queries/gist.graphql +25 -0
  205. package/src/graphql/queries/repo.graphql +28 -0
  206. package/src/graphql/queries/stats.graphql +113 -0
  207. package/src/graphql/queries/top-languages.graphql +27 -0
  208. package/src/graphql/reposContributedToDocument.ts +83 -0
  209. package/src/index.ts +18 -0
  210. package/src/themes/index.ts +519 -0
  211. package/src/translations.ts +1169 -0
@@ -0,0 +1,195 @@
1
+ import * as z from 'zod/mini';
2
+
3
+ import { RANK_ICONS, renderStatsCard } from '../cards/stats.js';
4
+ import type { CardConfig } from '../common/config.js';
5
+ import { fetchStats } from '../fetchers/stats.js';
6
+
7
+ import type { ApiResult } from './api-result.js';
8
+ import { errorResult } from './api-result.js';
9
+ import type { ApiQuery } from './params.js';
10
+ import {
11
+ booleanParam,
12
+ enumParam,
13
+ listParam,
14
+ localeParam,
15
+ looseIntParam,
16
+ numberParam,
17
+ parseColorParams,
18
+ parseParams,
19
+ rawParam,
20
+ safeListParam,
21
+ safeParam,
22
+ yearParam,
23
+ } from './params.js';
24
+
25
+ /** What the stats endpoint accepts, on top of the shared color params. */
26
+ const statsQuery = z.object({
27
+ username: safeParam,
28
+ repo: safeListParam,
29
+ owner: safeListParam,
30
+ hide: listParam,
31
+ hide_title: booleanParam,
32
+ hide_border: booleanParam,
33
+ card_width: looseIntParam,
34
+ hide_rank: booleanParam,
35
+ show_icons: booleanParam,
36
+ include_all_commits: booleanParam,
37
+ commits_year: yearParam,
38
+ line_height: rawParam,
39
+ text_bold: booleanParam,
40
+ exclude_repo: listParam,
41
+ custom_title: rawParam,
42
+ locale: localeParam,
43
+ disable_animations: booleanParam,
44
+ border_radius: numberParam,
45
+ number_format: rawParam,
46
+ role: listParam,
47
+ number_precision: looseIntParam,
48
+ rank_icon: enumParam(RANK_ICONS),
49
+ show: listParam,
50
+ contribs_include_own_repos: booleanParam,
51
+ });
52
+
53
+ /** The query this endpoint accepts, checked against the schema above. */
54
+ type StatsApiQuery = ApiQuery<typeof statsQuery>;
55
+
56
+ /**
57
+ * Render the stats card for a set of query params.
58
+ *
59
+ * @param query Raw query params, plus any of the shared color params.
60
+ * @param query.username GitHub username.
61
+ * @param query.repo Comma-separated repositories the search-based stats are scoped to.
62
+ * @param query.owner Comma-separated owners the search-based stats are scoped to.
63
+ * @param query.hide Comma-separated stats to hide.
64
+ * @param query.hide_title Whether to hide the card title.
65
+ * @param query.hide_border Whether to hide the card border.
66
+ * @param query.card_width Card width.
67
+ * @param query.hide_rank Whether to hide the rank circle.
68
+ * @param query.show_icons Whether to show the stat icons.
69
+ * @param query.include_all_commits Whether to count commits of all time.
70
+ * @param query.commits_year Year the commits are counted for.
71
+ * @param query.line_height Line height between the stats.
72
+ * @param query.text_bold Whether the stat values are bold.
73
+ * @param query.exclude_repo Comma-separated repositories to exclude.
74
+ * @param query.custom_title Card title.
75
+ * @param query.locale Language the card is rendered in.
76
+ * @param query.disable_animations Whether to disable the card animations.
77
+ * @param query.border_radius Card border radius.
78
+ * @param query.number_format How numbers are abbreviated.
79
+ * @param query.role Comma-separated owner affiliations to include.
80
+ * @param query.number_precision Decimals kept when a number is abbreviated.
81
+ * @param query.rank_icon Which rank indicator to draw.
82
+ * @param query.show Comma-separated extra stats to show.
83
+ * @param query.contribs_include_own_repos Whether the contributed-to counts include the user's own repositories.
84
+ * @param config Deployment config supplying the PAT pool.
85
+ * @returns The rendered card, or a rendered error.
86
+ */
87
+ const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<ApiResult> => {
88
+ let colors;
89
+ try {
90
+ colors = parseColorParams(query);
91
+ } catch (err) {
92
+ // A rejected color cannot be used to draw its own error card.
93
+ return errorResult(err);
94
+ }
95
+
96
+ try {
97
+ const {
98
+ username,
99
+ repo,
100
+ owner,
101
+ hide,
102
+ hide_title,
103
+ hide_border,
104
+ card_width,
105
+ hide_rank,
106
+ show_icons,
107
+ include_all_commits,
108
+ commits_year,
109
+ line_height,
110
+ text_bold,
111
+ exclude_repo,
112
+ custom_title,
113
+ locale,
114
+ disable_animations,
115
+ border_radius,
116
+ number_format,
117
+ role,
118
+ number_precision,
119
+ rank_icon,
120
+ show,
121
+ contribs_include_own_repos,
122
+ } = parseParams(statsQuery, query);
123
+
124
+ // A bare repo name is scoped to the user whose card this is.
125
+ const repository = repo.map((name) =>
126
+ name.includes('/') ? name : `${username ?? ''}/${name}`,
127
+ );
128
+
129
+ const stats = await fetchStats(
130
+ {
131
+ username,
132
+ include_all_commits,
133
+ exclude_repo,
134
+ include_merged_pull_requests:
135
+ show.includes('prs_merged') || show.includes('prs_merged_percentage'),
136
+ include_discussions: show.includes('discussions_started'),
137
+ include_discussions_answers: show.includes('discussions_answered'),
138
+ commits_year,
139
+ repo: repository,
140
+ owner,
141
+ include_prs_authored: show.includes('prs_authored'),
142
+ include_prs_commented: show.includes('prs_commented'),
143
+ include_prs_reviewed: show.includes('prs_reviewed'),
144
+ include_issues_authored: show.includes('issues_authored'),
145
+ include_issues_commented: show.includes('issues_commented'),
146
+ ownerAffiliations: role,
147
+ include_contributions: show.includes('contributions'),
148
+ include_all_time_contribs: show.includes('all_time_contribs'),
149
+ contribs_include_own_repos,
150
+ },
151
+ config,
152
+ );
153
+
154
+ return {
155
+ status: 'success',
156
+ content: renderStatsCard(
157
+ stats,
158
+ {
159
+ ...colors,
160
+ hide,
161
+ show_icons,
162
+ hide_title,
163
+ hide_border,
164
+ card_width,
165
+ hide_rank,
166
+ include_all_commits,
167
+ commits_year,
168
+ line_height,
169
+ text_bold,
170
+ custom_title,
171
+ border_radius,
172
+ number_format,
173
+ number_precision,
174
+ locale,
175
+ disable_animations,
176
+ rank_icon,
177
+ show,
178
+ },
179
+ username,
180
+ repository,
181
+ owner,
182
+ ),
183
+ };
184
+ } catch (err) {
185
+ return errorResult(err, colors);
186
+ }
187
+ };
188
+
189
+ /**
190
+ * The card, with the values its enum params accept.
191
+ * A UI reads them off the function it calls, e.g. `stats.LAYOUTS`.
192
+ */
193
+ export const stats = Object.assign(renderStats, {
194
+ RANK_ICONS,
195
+ });
@@ -0,0 +1,150 @@
1
+ import * as z from 'zod/mini';
2
+
3
+ import {
4
+ TOP_LANG_LAYOUTS,
5
+ TOP_LANG_STATS_FORMATS,
6
+ renderTopLanguages,
7
+ } from '../cards/top-languages.js';
8
+ import type { CardConfig } from '../common/config.js';
9
+ import { fetchTopLanguages } from '../fetchers/top-languages.js';
10
+
11
+ import type { ApiResult } from './api-result.js';
12
+ import { errorResult } from './api-result.js';
13
+ import type { ApiQuery } from './params.js';
14
+ import {
15
+ booleanParam,
16
+ enumParam,
17
+ listParam,
18
+ localeParam,
19
+ looseIntParam,
20
+ numberParam,
21
+ parseColorParams,
22
+ parseParams,
23
+ rawParam,
24
+ safeParam,
25
+ } from './params.js';
26
+
27
+ /** What the top-languages endpoint accepts, on top of the shared color params. */
28
+ const topLangsQuery = z.object({
29
+ username: safeParam,
30
+ hide: listParam,
31
+ hide_title: booleanParam,
32
+ hide_border: booleanParam,
33
+ card_width: looseIntParam,
34
+ layout: enumParam(TOP_LANG_LAYOUTS),
35
+ langs_count: looseIntParam,
36
+ exclude_repo: listParam,
37
+ size_weight: numberParam,
38
+ count_weight: numberParam,
39
+ custom_title: rawParam,
40
+ locale: localeParam,
41
+ border_radius: numberParam,
42
+ role: listParam,
43
+ disable_animations: booleanParam,
44
+ hide_progress: booleanParam,
45
+ hide_values: booleanParam,
46
+ stats_format: enumParam(TOP_LANG_STATS_FORMATS),
47
+ });
48
+
49
+ /** The query this endpoint accepts, checked against the schema above. */
50
+ type TopLangsApiQuery = ApiQuery<typeof topLangsQuery>;
51
+
52
+ /**
53
+ * Render the top languages card for a set of query params.
54
+ *
55
+ * @param query Raw query params, plus any of the shared color params.
56
+ * @param query.username GitHub username.
57
+ * @param query.hide Comma-separated languages to hide.
58
+ * @param query.hide_title Whether to hide the card title.
59
+ * @param query.hide_border Whether to hide the card border.
60
+ * @param query.card_width Card width.
61
+ * @param query.layout How the languages are laid out.
62
+ * @param query.langs_count Number of languages to show.
63
+ * @param query.exclude_repo Comma-separated repositories to exclude.
64
+ * @param query.size_weight Weight given to a language's size.
65
+ * @param query.count_weight Weight given to a language's repository count.
66
+ * @param query.custom_title Card title.
67
+ * @param query.locale Language the card is rendered in.
68
+ * @param query.border_radius Card border radius.
69
+ * @param query.role Comma-separated owner affiliations to include.
70
+ * @param query.disable_animations Whether to disable the card animations.
71
+ * @param query.hide_progress Whether to hide the progress bars.
72
+ * @param query.hide_values Whether to hide the language values.
73
+ * @param query.stats_format Whether values are shown as bytes or percentages.
74
+ * @param config Deployment config supplying the PAT pool.
75
+ * @returns The rendered card, or a rendered error.
76
+ */
77
+ const renderTopLangs = async (query: TopLangsApiQuery, config: CardConfig): Promise<ApiResult> => {
78
+ let colors;
79
+ try {
80
+ colors = parseColorParams(query);
81
+ } catch (err) {
82
+ // A rejected color cannot be used to draw its own error card.
83
+ return errorResult(err);
84
+ }
85
+
86
+ try {
87
+ const {
88
+ username,
89
+ hide,
90
+ hide_title,
91
+ hide_border,
92
+ card_width,
93
+ layout,
94
+ langs_count,
95
+ exclude_repo,
96
+ size_weight,
97
+ count_weight,
98
+ custom_title,
99
+ locale,
100
+ border_radius,
101
+ role,
102
+ disable_animations,
103
+ hide_progress,
104
+ hide_values,
105
+ stats_format,
106
+ } = parseParams(topLangsQuery, query);
107
+
108
+ const topLangs = await fetchTopLanguages(
109
+ {
110
+ username,
111
+ exclude_repo,
112
+ size_weight,
113
+ count_weight,
114
+ ownerAffiliations: role,
115
+ },
116
+ config,
117
+ );
118
+
119
+ return {
120
+ status: 'success',
121
+ content: renderTopLanguages(topLangs, {
122
+ ...colors,
123
+ custom_title,
124
+ hide_title,
125
+ hide_border,
126
+ card_width,
127
+ hide,
128
+ layout,
129
+ langs_count,
130
+ border_radius,
131
+ locale,
132
+ disable_animations,
133
+ hide_progress,
134
+ hide_values,
135
+ stats_format,
136
+ }),
137
+ };
138
+ } catch (err) {
139
+ return errorResult(err, colors);
140
+ }
141
+ };
142
+
143
+ /**
144
+ * The card, with the values its enum params accept.
145
+ * A UI reads them off the function it calls, e.g. `topLangs.LAYOUTS`.
146
+ */
147
+ export const topLangs = Object.assign(renderTopLangs, {
148
+ LAYOUTS: TOP_LANG_LAYOUTS,
149
+ STATS_FORMATS: TOP_LANG_STATS_FORMATS,
150
+ });
@@ -0,0 +1,130 @@
1
+ import * as z from 'zod/mini';
2
+
3
+ import { DISPLAY_FORMATS, WAKATIME_LAYOUTS, renderWakatimeCard } from '../cards/wakatime.js';
4
+ import type { CardConfig } from '../common/config.js';
5
+ import { fetchWakatimeStats } from '../fetchers/wakatime.js';
6
+
7
+ import type { ApiResult } from './api-result.js';
8
+ import { errorResult } from './api-result.js';
9
+ import type { ApiQuery } from './params.js';
10
+ import {
11
+ booleanParam,
12
+ enumParam,
13
+ listParam,
14
+ localeParam,
15
+ looseIntParam,
16
+ numberParam,
17
+ parseColorParams,
18
+ parseParams,
19
+ rawParam,
20
+ safeParam,
21
+ } from './params.js';
22
+
23
+ /** What the wakatime endpoint accepts, on top of the shared color params. */
24
+ const wakatimeQuery = z.object({
25
+ username: safeParam,
26
+ hide_border: booleanParam,
27
+ card_width: looseIntParam,
28
+ line_height: rawParam,
29
+ hide_title: booleanParam,
30
+ hide_progress: booleanParam,
31
+ custom_title: rawParam,
32
+ locale: localeParam,
33
+ layout: enumParam(WAKATIME_LAYOUTS),
34
+ langs_count: looseIntParam,
35
+ hide: listParam,
36
+ api_domain: rawParam,
37
+ border_radius: numberParam,
38
+ display_format: enumParam(DISPLAY_FORMATS),
39
+ disable_animations: booleanParam,
40
+ });
41
+
42
+ /** The query this endpoint accepts, checked against the schema above. */
43
+ type WakatimeApiQuery = ApiQuery<typeof wakatimeQuery>;
44
+
45
+ /**
46
+ * Render the WakaTime card for a set of query params.
47
+ *
48
+ * WakaTime needs no GitHub token, so the config is read only for the transport it carries.
49
+ *
50
+ * @param query Raw query params, plus any of the shared color params.
51
+ * @param query.username WakaTime username.
52
+ * @param query.hide_border Whether to hide the card border.
53
+ * @param query.card_width Card width.
54
+ * @param query.line_height Line height between the languages.
55
+ * @param query.hide_title Whether to hide the card title.
56
+ * @param query.hide_progress Whether to hide the progress bars.
57
+ * @param query.custom_title Card title.
58
+ * @param query.locale Language the card is rendered in.
59
+ * @param query.layout How the languages are laid out.
60
+ * @param query.langs_count Number of languages to show.
61
+ * @param query.hide Comma-separated languages to hide.
62
+ * @param query.api_domain WakaTime instance the stats are read from.
63
+ * @param query.border_radius Card border radius.
64
+ * @param query.display_format Whether values are shown as time or percentages.
65
+ * @param query.disable_animations Whether to disable the card animations.
66
+ * @param config Deployment config supplying the transport.
67
+ * @returns The rendered card, or a rendered error.
68
+ */
69
+ const renderWakatime = async (query: WakatimeApiQuery, config: CardConfig): Promise<ApiResult> => {
70
+ let colors;
71
+ try {
72
+ colors = parseColorParams(query);
73
+ } catch (err) {
74
+ // A rejected color cannot be used to draw its own error card.
75
+ return errorResult(err);
76
+ }
77
+
78
+ try {
79
+ const {
80
+ username,
81
+ hide_border,
82
+ card_width,
83
+ line_height,
84
+ hide_title,
85
+ hide_progress,
86
+ custom_title,
87
+ locale,
88
+ layout,
89
+ langs_count,
90
+ hide,
91
+ api_domain,
92
+ border_radius,
93
+ display_format,
94
+ disable_animations,
95
+ } = parseParams(wakatimeQuery, query);
96
+
97
+ const stats = await fetchWakatimeStats({ username, api_domain }, config);
98
+
99
+ return {
100
+ status: 'success',
101
+ content: renderWakatimeCard(stats, {
102
+ ...colors,
103
+ custom_title,
104
+ hide_title,
105
+ hide_border,
106
+ card_width,
107
+ hide,
108
+ line_height,
109
+ hide_progress,
110
+ border_radius,
111
+ locale,
112
+ layout,
113
+ langs_count,
114
+ display_format,
115
+ disable_animations,
116
+ }),
117
+ };
118
+ } catch (err) {
119
+ return errorResult(err, colors);
120
+ }
121
+ };
122
+
123
+ /**
124
+ * The card, with the values its enum params accept.
125
+ * A UI reads them off the function it calls, e.g. `wakatime.LAYOUTS`.
126
+ */
127
+ export const wakatime = Object.assign(renderWakatime, {
128
+ LAYOUTS: WAKATIME_LAYOUTS,
129
+ DISPLAY_FORMATS,
130
+ });
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Calculates the exponential cdf.
3
+ *
4
+ * @param x The value.
5
+ * @returns The exponential cdf.
6
+ */
7
+ function exponential_cdf(x: number): number {
8
+ return 1 - 2 ** -x;
9
+ }
10
+
11
+ /**
12
+ * Calculates the log normal cdf.
13
+ *
14
+ * @param x The value.
15
+ * @returns The log normal cdf.
16
+ */
17
+ function log_normal_cdf(x: number): number {
18
+ // approximation
19
+ return x / (1 + x);
20
+ }
21
+
22
+ /**
23
+ * Calculates the users rank.
24
+ *
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
+ */
36
+ function calculateRank({
37
+ all_commits,
38
+ commits,
39
+ prs,
40
+ issues,
41
+ reviews,
42
+ stars,
43
+ followers,
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 } {
54
+ const COMMITS_MEDIAN = all_commits ? 1000 : 250,
55
+ COMMITS_WEIGHT = 2;
56
+ const PRS_MEDIAN = 50,
57
+ PRS_WEIGHT = 3;
58
+ const ISSUES_MEDIAN = 25,
59
+ ISSUES_WEIGHT = 1;
60
+ const REVIEWS_MEDIAN = 2,
61
+ REVIEWS_WEIGHT = 1;
62
+ const STARS_MEDIAN = 50,
63
+ STARS_WEIGHT = 4;
64
+ const FOLLOWERS_MEDIAN = 10,
65
+ FOLLOWERS_WEIGHT = 1;
66
+
67
+ const TOTAL_WEIGHT =
68
+ COMMITS_WEIGHT + PRS_WEIGHT + ISSUES_WEIGHT + REVIEWS_WEIGHT + STARS_WEIGHT + FOLLOWERS_WEIGHT;
69
+
70
+ const THRESHOLDS = [1, 12.5, 25, 37.5, 50, 62.5, 75, 87.5, 100];
71
+ const LEVELS = ['S', 'A+', 'A', 'A-', 'B+', 'B', 'B-', 'C+', 'C'];
72
+
73
+ const rank =
74
+ 1 -
75
+ (COMMITS_WEIGHT * exponential_cdf(commits / COMMITS_MEDIAN) +
76
+ PRS_WEIGHT * exponential_cdf(prs / PRS_MEDIAN) +
77
+ ISSUES_WEIGHT * exponential_cdf(issues / ISSUES_MEDIAN) +
78
+ REVIEWS_WEIGHT * exponential_cdf(reviews / REVIEWS_MEDIAN) +
79
+ STARS_WEIGHT * log_normal_cdf(stars / STARS_MEDIAN) +
80
+ FOLLOWERS_WEIGHT * log_normal_cdf(followers / FOLLOWERS_MEDIAN)) /
81
+ TOTAL_WEIGHT;
82
+
83
+ const level = LEVELS[THRESHOLDS.findIndex((t) => rank * 100 <= t)];
84
+ if (level === undefined) {
85
+ throw new Error('Unable to determine rank level');
86
+ }
87
+
88
+ return { level, percentile: rank * 100 };
89
+ }
90
+
91
+ export { calculateRank };