@stats-forge/github-stats-forge-core 0.1.0 → 0.2.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 (142) hide show
  1. package/build/api/contributed-to.d.ts +23 -0
  2. package/build/api/contributed-to.d.ts.map +1 -0
  3. package/build/api/contributed-to.js +43 -0
  4. package/build/api/gist.d.ts +8 -15
  5. package/build/api/gist.d.ts.map +1 -1
  6. package/build/api/gist.js +14 -29
  7. package/build/api/handler.d.ts +16 -0
  8. package/build/api/handler.d.ts.map +1 -0
  9. package/build/api/handler.js +29 -0
  10. package/build/api/index.d.ts +1 -0
  11. package/build/api/index.d.ts.map +1 -1
  12. package/build/api/index.js +1 -0
  13. package/build/api/params.d.ts +11 -5
  14. package/build/api/params.d.ts.map +1 -1
  15. package/build/api/params.js +26 -6
  16. package/build/api/pin.d.ts +11 -15
  17. package/build/api/pin.d.ts.map +1 -1
  18. package/build/api/pin.js +34 -46
  19. package/build/api/stats.d.ts +15 -22
  20. package/build/api/stats.d.ts.map +1 -1
  21. package/build/api/stats.js +59 -72
  22. package/build/api/top-langs.d.ts +11 -22
  23. package/build/api/top-langs.d.ts.map +1 -1
  24. package/build/api/top-langs.js +34 -51
  25. package/build/api/wakatime.d.ts +10 -24
  26. package/build/api/wakatime.d.ts.map +1 -1
  27. package/build/api/wakatime.js +27 -47
  28. package/build/cards/contributed-to.d.ts +19 -0
  29. package/build/cards/contributed-to.d.ts.map +1 -0
  30. package/build/cards/contributed-to.js +242 -0
  31. package/build/cards/gist.d.ts +1 -0
  32. package/build/cards/gist.d.ts.map +1 -1
  33. package/build/cards/gist.js +13 -9
  34. package/build/cards/repo.d.ts +12 -1
  35. package/build/cards/repo.d.ts.map +1 -1
  36. package/build/cards/repo.js +40 -18
  37. package/build/cards/stats.d.ts +15 -3
  38. package/build/cards/stats.d.ts.map +1 -1
  39. package/build/cards/stats.js +91 -76
  40. package/build/cards/top-languages.d.ts +13 -2
  41. package/build/cards/top-languages.d.ts.map +1 -1
  42. package/build/cards/top-languages.js +26 -9
  43. package/build/cards/wakatime.d.ts +13 -2
  44. package/build/cards/wakatime.d.ts.map +1 -1
  45. package/build/cards/wakatime.js +22 -9
  46. package/build/common/Card.d.ts +9 -0
  47. package/build/common/Card.d.ts.map +1 -1
  48. package/build/common/Card.js +44 -9
  49. package/build/common/I18n.d.ts +1 -1
  50. package/build/common/I18n.d.ts.map +1 -1
  51. package/build/common/I18n.js +4 -2
  52. package/build/common/brand.d.ts +76 -0
  53. package/build/common/brand.d.ts.map +1 -0
  54. package/build/common/brand.js +73 -0
  55. package/build/common/constants.d.ts +3 -1
  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 +50 -3
  59. package/build/common/date.d.ts.map +1 -1
  60. package/build/common/date.js +104 -2
  61. package/build/common/icons.d.ts +4 -2
  62. package/build/common/icons.d.ts.map +1 -1
  63. package/build/common/icons.js +10 -5
  64. package/build/common/ops.d.ts +8 -3
  65. package/build/common/ops.d.ts.map +1 -1
  66. package/build/common/ops.js +8 -14
  67. package/build/common/render.d.ts +7 -1
  68. package/build/common/render.d.ts.map +1 -1
  69. package/build/common/render.js +60 -58
  70. package/build/fetchers/contributed-to.d.ts +62 -0
  71. package/build/fetchers/contributed-to.d.ts.map +1 -0
  72. package/build/fetchers/contributed-to.js +184 -0
  73. package/build/fetchers/graphql-error.d.ts +13 -0
  74. package/build/fetchers/graphql-error.d.ts.map +1 -0
  75. package/build/fetchers/graphql-error.js +29 -0
  76. package/build/fetchers/index.d.ts +2 -1
  77. package/build/fetchers/index.d.ts.map +1 -1
  78. package/build/fetchers/index.js +1 -0
  79. package/build/fetchers/stats.d.ts +14 -8
  80. package/build/fetchers/stats.d.ts.map +1 -1
  81. package/build/fetchers/stats.js +72 -195
  82. package/build/fetchers/top-languages.d.ts.map +1 -1
  83. package/build/fetchers/top-languages.js +3 -18
  84. package/build/fetchers/types.d.ts +24 -0
  85. package/build/fetchers/types.d.ts.map +1 -1
  86. package/build/graphql/contributionsCollection.d.ts +9 -0
  87. package/build/graphql/contributionsCollection.d.ts.map +1 -0
  88. package/build/graphql/contributionsCollection.js +12 -0
  89. package/build/graphql/contributionsDocument.d.ts +5 -5
  90. package/build/graphql/contributionsDocument.d.ts.map +1 -1
  91. package/build/graphql/contributionsDocument.js +8 -15
  92. package/build/graphql/generated/contributed-to.d.ts +22 -0
  93. package/build/graphql/generated/contributed-to.d.ts.map +1 -0
  94. package/build/graphql/generated/contributed-to.js +11 -0
  95. package/build/graphql/generated/stats.d.ts +13 -1
  96. package/build/graphql/generated/stats.d.ts.map +1 -1
  97. package/build/graphql/generated/stats.js +2 -2
  98. package/build/graphql/graphqlDocument.d.ts +1 -1
  99. package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
  100. package/build/graphql/reposContributedToDocument.js +11 -5
  101. package/build/translations.d.ts +64 -1
  102. package/build/translations.d.ts.map +1 -1
  103. package/build/translations.js +65 -1
  104. package/package.json +1 -1
  105. package/src/api/contributed-to.ts +84 -0
  106. package/src/api/gist.ts +20 -37
  107. package/src/api/handler.ts +42 -0
  108. package/src/api/index.ts +1 -0
  109. package/src/api/params.ts +48 -9
  110. package/src/api/pin.ts +33 -45
  111. package/src/api/stats.ts +78 -89
  112. package/src/api/top-langs.ts +34 -56
  113. package/src/api/wakatime.ts +33 -54
  114. package/src/cards/contributed-to.ts +373 -0
  115. package/src/cards/gist.ts +17 -8
  116. package/src/cards/repo.ts +44 -20
  117. package/src/cards/stats.ts +119 -90
  118. package/src/cards/top-languages.ts +27 -18
  119. package/src/cards/wakatime.ts +23 -13
  120. package/src/common/Card.ts +56 -11
  121. package/src/common/I18n.ts +4 -2
  122. package/src/common/brand.ts +109 -0
  123. package/src/common/constants.ts +4 -1
  124. package/src/common/date.ts +135 -3
  125. package/src/common/icons.ts +10 -5
  126. package/src/common/ops.ts +10 -19
  127. package/src/common/render.ts +65 -62
  128. package/src/fetchers/contributed-to.ts +271 -0
  129. package/src/fetchers/graphql-error.ts +36 -0
  130. package/src/fetchers/index.ts +3 -0
  131. package/src/fetchers/stats.ts +123 -274
  132. package/src/fetchers/top-languages.ts +5 -19
  133. package/src/fetchers/types.ts +26 -0
  134. package/src/graphql/contributionsCollection.ts +18 -0
  135. package/src/graphql/contributionsDocument.ts +11 -18
  136. package/src/graphql/generated/contributed-to.ts +25 -0
  137. package/src/graphql/generated/stats.ts +19 -6
  138. package/src/graphql/graphqlDocument.ts +1 -1
  139. package/src/graphql/queries/contributed-to.graphql +8 -0
  140. package/src/graphql/queries/stats.graphql +14 -2
  141. package/src/graphql/reposContributedToDocument.ts +11 -9
  142. package/src/translations.ts +77 -0
package/src/api/params.ts CHANGED
@@ -3,6 +3,7 @@ import * as z from 'zod/mini';
3
3
  import type { ColorParams } from '../common/color.ts';
4
4
  import { COLOR_PARAM_KEYS, THEME_PARAM_KEYS, isValidColorInput } from '../common/color.ts';
5
5
  import { GITHUB_USERNAME_PATTERN } from '../common/constants.ts';
6
+ import { getWidestRange, parseRangeDate } from '../common/date.ts';
6
7
  import { CardError } from '../common/error.ts';
7
8
  import { parseArray, parseBoolean } from '../common/ops.ts';
8
9
  import { isLocaleAvailable } from '../translations.ts';
@@ -24,7 +25,16 @@ const rawParam = z.optional(z.string());
24
25
  const SAFE_PATTERN = /^[-\w/.,]+$/;
25
26
 
26
27
  /** What a check rejected a param for. */
27
- type Rejection = 'number' | 'year' | 'unsafe' | 'username' | 'locale' | 'enum' | 'color';
28
+ type Rejection =
29
+ | 'number'
30
+ | 'date'
31
+ | 'out_of_range'
32
+ | 'inverted_range'
33
+ | 'unsafe'
34
+ | 'username'
35
+ | 'locale'
36
+ | 'enum'
37
+ | 'color';
28
38
 
29
39
  /**
30
40
  * Every rejection the api can put on an error card, in one place.
@@ -32,7 +42,9 @@ type Rejection = 'number' | 'year' | 'unsafe' | 'username' | 'locale' | 'enum' |
32
42
  */
33
43
  const REJECTION_MESSAGES: Record<Rejection, (param: string) => string> = {
34
44
  number: (param) => `Invalid number input for parameter "${param}"`,
35
- year: (param) => `Invalid number input for parameter "${param}"`,
45
+ date: (param) => `Invalid date input for parameter "${param}"`,
46
+ out_of_range: (param) => `Out of range date for parameter "${param}"`,
47
+ inverted_range: () => 'Range "from" is after "to"',
36
48
  unsafe: (param) => `Parameter "${param}" contains unsafe characters`,
37
49
  username: (param) => `Invalid username input for parameter "${param}"`,
38
50
  locale: () => 'Locale not found',
@@ -86,13 +98,38 @@ const looseIntParam = z.pipe(
86
98
  );
87
99
 
88
100
  /**
89
- * A four-digit year.
90
- * Anything else builds a `DateTime` GitHub rejects, so it is a permanent error here rather than a failed request later.
91
- * Yields the year, or `undefined` when the param is absent.
101
+ * One end of the range a card counts within, written as `2024`, `2024-03` or `2024-03-15`.
102
+ *
103
+ * @returns Schema yielding the instant, or `undefined` when the param is absent.
104
+ */
105
+ const rangeParam = (end: 'from' | 'to'): z.ZodMiniType<Date | undefined, string | undefined> => {
106
+ const parse = (value: string): Date | undefined => parseRangeDate(value, end);
107
+ return z.pipe(
108
+ rawParam.check(rejects('date', (value) => parse(value) !== undefined)).check(
109
+ rejects('out_of_range', (value) => {
110
+ const date = parse(value);
111
+ const widest = getWidestRange();
112
+ // a bad shape is already reported by the check above
113
+ return date === undefined || (date >= widest.from && date <= widest.to);
114
+ }),
115
+ ),
116
+ z.transform((value) => (value === undefined ? undefined : parse(value))),
117
+ );
118
+ };
119
+
120
+ /** The start of a card's range; see {@link rangeParam}. */
121
+ const fromParam = rangeParam('from');
122
+
123
+ /** The end of a card's range; see {@link rangeParam}. */
124
+ const toParam = rangeParam('to');
125
+
126
+ /**
127
+ * The pair-level rule the two ends cannot see on their own.
128
+ * Every endpoint taking a range applies it, so an order no card could draw is rejected once.
92
129
  */
93
- const yearParam: z.ZodMiniType<number | undefined, string | undefined> = z.pipe(
94
- rawParam.check(rejects('year', (value) => /^\d{4}$/.test(value))),
95
- z.transform((value) => (value === undefined ? undefined : Number(value))),
130
+ const ORDERED_RANGE = z.refine<{ from?: Date | undefined; to?: Date | undefined }>(
131
+ ({ from, to }) => from === undefined || to === undefined || from <= to,
132
+ { error: REJECTION_MESSAGES.inverted_range('from'), path: ['from'] },
96
133
  );
97
134
 
98
135
  /**
@@ -213,7 +250,9 @@ export type { ApiQuery };
213
250
  export {
214
251
  booleanParam,
215
252
  enumParam,
253
+ fromParam,
216
254
  listParam,
255
+ ORDERED_RANGE,
217
256
  localeParam,
218
257
  looseIntParam,
219
258
  numberParam,
@@ -222,6 +261,6 @@ export {
222
261
  rawParam,
223
262
  safeListParam,
224
263
  safeParam,
264
+ toParam,
225
265
  usernameParam,
226
- yearParam,
227
266
  };
package/src/api/pin.ts CHANGED
@@ -1,20 +1,15 @@
1
1
  import * as z from 'zod/mini';
2
2
 
3
3
  import { renderRepoCard } from '../cards/repo.ts';
4
- import type { CardConfig } from '../common/config.ts';
5
4
  import { fetchRepo } from '../fetchers/repo.ts';
6
5
 
7
- import type { ApiResult } from './api-result.ts';
8
- import { errorResult } from './api-result.ts';
9
- import type { ApiQuery } from './params.ts';
6
+ import { cardHandler } from './handler.ts';
10
7
  import {
11
8
  booleanParam,
12
9
  listParam,
13
10
  localeParam,
14
11
  looseIntParam,
15
12
  numberParam,
16
- parseColorParams,
17
- parseParams,
18
13
  rawParam,
19
14
  safeParam,
20
15
  usernameParam,
@@ -38,25 +33,15 @@ const pinQuery = z.object({
38
33
  description_lines_count: looseIntParam,
39
34
  });
40
35
 
41
- /** The query this endpoint accepts, checked against the schema above. */
42
- type PinApiQuery = ApiQuery<typeof pinQuery>;
43
-
44
36
  /**
45
37
  * Render the repository card for a set of query params.
46
38
  *
47
39
  * @returns The rendered card, or a rendered error.
48
40
  */
49
- export const pin = async (query: PinApiQuery, config: CardConfig): Promise<ApiResult> => {
50
- let colors;
51
- try {
52
- colors = parseColorParams(query);
53
- } catch (error) {
54
- // A rejected color cannot be used to draw its own error card.
55
- return errorResult(error);
56
- }
57
-
58
- try {
59
- const {
41
+ const renderPin = cardHandler(
42
+ pinQuery,
43
+ async (
44
+ {
60
45
  username,
61
46
  repo,
62
47
  hide_border,
@@ -71,8 +56,10 @@ export const pin = async (query: PinApiQuery, config: CardConfig): Promise<ApiRe
71
56
  locale,
72
57
  border_radius,
73
58
  description_lines_count,
74
- } = parseParams(pinQuery, query);
75
-
59
+ },
60
+ colors,
61
+ config,
62
+ ) => {
76
63
  const repoData = await fetchRepo(
77
64
  {
78
65
  username,
@@ -86,26 +73,27 @@ export const pin = async (query: PinApiQuery, config: CardConfig): Promise<ApiRe
86
73
  config,
87
74
  );
88
75
 
89
- return {
90
- status: 'success',
91
- content: renderRepoCard(repoData, {
92
- ...colors,
93
- hide_border,
94
- border_radius,
95
- card_width_input: card_width,
96
- show_owner,
97
- browser_rendering,
98
- show,
99
- show_icons,
100
- number_format,
101
- text_bold,
102
- line_height,
103
- username,
104
- locale,
105
- description_lines_count,
106
- }),
107
- };
108
- } catch (error) {
109
- return errorResult(error, colors);
110
- }
111
- };
76
+ return renderRepoCard(repoData, {
77
+ ...colors,
78
+ hide_border,
79
+ border_radius,
80
+ card_width_input: card_width,
81
+ show_owner,
82
+ browser_rendering,
83
+ show,
84
+ show_icons,
85
+ number_format,
86
+ text_bold,
87
+ line_height,
88
+ username,
89
+ locale,
90
+ description_lines_count,
91
+ });
92
+ },
93
+ );
94
+
95
+ /**
96
+ * The card, and the values each of its options accepts, keyed by the option's own name.
97
+ * A UI reads them off the function it calls: `pin.OPTIONS.show`.
98
+ */
99
+ export const pin = Object.assign(renderPin, { OPTIONS: renderRepoCard.OPTIONS });
package/src/api/stats.ts CHANGED
@@ -1,74 +1,65 @@
1
1
  import * as z from 'zod/mini';
2
2
 
3
- import { RANK_ICONS, renderStatsCard } from '../cards/stats.ts';
4
- import type { CardConfig } from '../common/config.ts';
3
+ import { renderStatsCard } from '../cards/stats.ts';
4
+ import { OWNER_AFFILIATIONS } from '../common/constants.ts';
5
5
  import { fetchStats } from '../fetchers/stats.ts';
6
6
 
7
- import type { ApiResult } from './api-result.ts';
8
- import { errorResult } from './api-result.ts';
9
- import type { ApiQuery } from './params.ts';
7
+ import { cardHandler } from './handler.ts';
10
8
  import {
11
9
  booleanParam,
12
10
  enumParam,
11
+ fromParam,
13
12
  listParam,
14
13
  localeParam,
15
14
  looseIntParam,
16
15
  numberParam,
17
- parseColorParams,
18
- parseParams,
16
+ ORDERED_RANGE,
19
17
  rawParam,
20
18
  safeListParam,
19
+ toParam,
21
20
  usernameParam,
22
- yearParam,
23
21
  } from './params.ts';
24
22
 
25
23
  /** What the stats endpoint accepts, on top of the shared color params. */
26
- const statsQuery = z.object({
27
- username: usernameParam,
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>;
24
+ const statsQuery = z
25
+ .object({
26
+ username: usernameParam,
27
+ repo: safeListParam,
28
+ owner: safeListParam,
29
+ hide: listParam,
30
+ hide_title: booleanParam,
31
+ hide_border: booleanParam,
32
+ card_width: looseIntParam,
33
+ hide_rank: booleanParam,
34
+ show_icons: booleanParam,
35
+ include_all_commits: booleanParam,
36
+ from: fromParam,
37
+ to: toParam,
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(renderStatsCard.OPTIONS.rank_icon),
49
+ show: listParam,
50
+ contribs_include_own_repos: booleanParam,
51
+ })
52
+ .check(ORDERED_RANGE);
55
53
 
56
54
  /**
57
55
  * Render the stats card for a set of query params.
58
56
  *
59
57
  * @returns The rendered card, or a rendered error.
60
58
  */
61
- const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<ApiResult> => {
62
- let colors;
63
- try {
64
- colors = parseColorParams(query);
65
- } catch (error) {
66
- // A rejected color cannot be used to draw its own error card.
67
- return errorResult(error);
68
- }
69
-
70
- try {
71
- const {
59
+ const renderStats = cardHandler(
60
+ statsQuery,
61
+ async (
62
+ {
72
63
  username,
73
64
  repo,
74
65
  owner,
@@ -79,7 +70,8 @@ const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<Ap
79
70
  hide_rank,
80
71
  show_icons,
81
72
  include_all_commits,
82
- commits_year,
73
+ from,
74
+ to,
83
75
  line_height,
84
76
  text_bold,
85
77
  exclude_repo,
@@ -93,8 +85,10 @@ const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<Ap
93
85
  rank_icon,
94
86
  show,
95
87
  contribs_include_own_repos,
96
- } = parseParams(statsQuery, query);
97
-
88
+ },
89
+ colors,
90
+ config,
91
+ ) => {
98
92
  // A bare repo name is scoped to the user whose card this is.
99
93
  const repository = repo.map((name) =>
100
94
  name.includes('/') ? name : `${username ?? ''}/${name}`,
@@ -109,7 +103,8 @@ const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<Ap
109
103
  show.includes('prs_merged') || show.includes('prs_merged_percentage'),
110
104
  include_discussions: show.includes('discussions_started'),
111
105
  include_discussions_answers: show.includes('discussions_answered'),
112
- commits_year,
106
+ from,
107
+ to,
113
108
  repo: repository,
114
109
  owner,
115
110
  include_prs_authored: show.includes('prs_authored'),
@@ -125,45 +120,39 @@ const renderStats = async (query: StatsApiQuery, config: CardConfig): Promise<Ap
125
120
  config,
126
121
  );
127
122
 
128
- return {
129
- status: 'success',
130
- content: renderStatsCard(
131
- stats,
132
- {
133
- ...colors,
134
- hide,
135
- show_icons,
136
- hide_title,
137
- hide_border,
138
- card_width,
139
- hide_rank,
140
- include_all_commits,
141
- commits_year,
142
- line_height,
143
- text_bold,
144
- custom_title,
145
- border_radius,
146
- number_format,
147
- number_precision,
148
- locale,
149
- disable_animations,
150
- rank_icon,
151
- show,
152
- },
153
- username,
154
- repository,
155
- owner,
156
- ),
157
- };
158
- } catch (error) {
159
- return errorResult(error, colors);
160
- }
161
- };
123
+ return renderStatsCard(
124
+ stats,
125
+ {
126
+ ...colors,
127
+ hide,
128
+ show_icons,
129
+ hide_title,
130
+ hide_border,
131
+ card_width,
132
+ hide_rank,
133
+ include_all_commits,
134
+ line_height,
135
+ text_bold,
136
+ custom_title,
137
+ border_radius,
138
+ number_format,
139
+ number_precision,
140
+ locale,
141
+ disable_animations,
142
+ rank_icon,
143
+ show,
144
+ },
145
+ username,
146
+ repository,
147
+ owner,
148
+ );
149
+ },
150
+ );
162
151
 
163
152
  /**
164
- * The card, with the values its enum params accept.
165
- * A UI reads them off the function it calls, e.g. `stats.LAYOUTS`.
153
+ * The card, and the values each of its options accepts, keyed by the option's own name.
154
+ * A UI reads them off the function it calls: `stats.OPTIONS.rank_icon`.
166
155
  */
167
156
  export const stats = Object.assign(renderStats, {
168
- RANK_ICONS,
157
+ OPTIONS: { ...renderStatsCard.OPTIONS, role: OWNER_AFFILIATIONS },
169
158
  });
@@ -1,16 +1,10 @@
1
1
  import * as z from 'zod/mini';
2
2
 
3
- import {
4
- TOP_LANG_LAYOUTS,
5
- TOP_LANG_STATS_FORMATS,
6
- renderTopLanguages,
7
- } from '../cards/top-languages.ts';
8
- import type { CardConfig } from '../common/config.ts';
3
+ import { renderTopLanguages } from '../cards/top-languages.ts';
4
+ import { OWNER_AFFILIATIONS } from '../common/constants.ts';
9
5
  import { fetchTopLanguages } from '../fetchers/top-languages.ts';
10
6
 
11
- import type { ApiResult } from './api-result.ts';
12
- import { errorResult } from './api-result.ts';
13
- import type { ApiQuery } from './params.ts';
7
+ import { cardHandler } from './handler.ts';
14
8
  import {
15
9
  booleanParam,
16
10
  enumParam,
@@ -18,8 +12,6 @@ import {
18
12
  localeParam,
19
13
  looseIntParam,
20
14
  numberParam,
21
- parseColorParams,
22
- parseParams,
23
15
  rawParam,
24
16
  usernameParam,
25
17
  } from './params.ts';
@@ -31,7 +23,7 @@ const topLangsQuery = z.object({
31
23
  hide_title: booleanParam,
32
24
  hide_border: booleanParam,
33
25
  card_width: looseIntParam,
34
- layout: enumParam(TOP_LANG_LAYOUTS),
26
+ layout: enumParam(renderTopLanguages.OPTIONS.layout),
35
27
  langs_count: looseIntParam,
36
28
  exclude_repo: listParam,
37
29
  size_weight: numberParam,
@@ -43,28 +35,18 @@ const topLangsQuery = z.object({
43
35
  disable_animations: booleanParam,
44
36
  hide_progress: booleanParam,
45
37
  hide_values: booleanParam,
46
- stats_format: enumParam(TOP_LANG_STATS_FORMATS),
38
+ stats_format: enumParam(renderTopLanguages.OPTIONS.stats_format),
47
39
  });
48
40
 
49
- /** The query this endpoint accepts, checked against the schema above. */
50
- type TopLangsApiQuery = ApiQuery<typeof topLangsQuery>;
51
-
52
41
  /**
53
42
  * Render the top languages card for a set of query params.
54
43
  *
55
44
  * @returns The rendered card, or a rendered error.
56
45
  */
57
- const renderTopLangs = async (query: TopLangsApiQuery, config: CardConfig): Promise<ApiResult> => {
58
- let colors;
59
- try {
60
- colors = parseColorParams(query);
61
- } catch (error) {
62
- // A rejected color cannot be used to draw its own error card.
63
- return errorResult(error);
64
- }
65
-
66
- try {
67
- const {
46
+ const renderTopLangs = cardHandler(
47
+ topLangsQuery,
48
+ async (
49
+ {
68
50
  username,
69
51
  hide,
70
52
  hide_title,
@@ -83,8 +65,10 @@ const renderTopLangs = async (query: TopLangsApiQuery, config: CardConfig): Prom
83
65
  hide_progress,
84
66
  hide_values,
85
67
  stats_format,
86
- } = parseParams(topLangsQuery, query);
87
-
68
+ },
69
+ colors,
70
+ config,
71
+ ) => {
88
72
  const topLangs = await fetchTopLanguages(
89
73
  {
90
74
  username,
@@ -96,35 +80,29 @@ const renderTopLangs = async (query: TopLangsApiQuery, config: CardConfig): Prom
96
80
  config,
97
81
  );
98
82
 
99
- return {
100
- status: 'success',
101
- content: renderTopLanguages(topLangs, {
102
- ...colors,
103
- custom_title,
104
- hide_title,
105
- hide_border,
106
- card_width,
107
- hide,
108
- layout,
109
- langs_count,
110
- border_radius,
111
- locale,
112
- disable_animations,
113
- hide_progress,
114
- hide_values,
115
- stats_format,
116
- }),
117
- };
118
- } catch (error) {
119
- return errorResult(error, colors);
120
- }
121
- };
83
+ return renderTopLanguages(topLangs, {
84
+ ...colors,
85
+ custom_title,
86
+ hide_title,
87
+ hide_border,
88
+ card_width,
89
+ hide,
90
+ layout,
91
+ langs_count,
92
+ border_radius,
93
+ locale,
94
+ disable_animations,
95
+ hide_progress,
96
+ hide_values,
97
+ stats_format,
98
+ });
99
+ },
100
+ );
122
101
 
123
102
  /**
124
- * The card, with the values its enum params accept.
125
- * A UI reads them off the function it calls, e.g. `topLangs.LAYOUTS`.
103
+ * The card, and the values each of its options accepts, keyed by the option's own name.
104
+ * A UI reads them off the function it calls: `topLangs.OPTIONS.layout`.
126
105
  */
127
106
  export const topLangs = Object.assign(renderTopLangs, {
128
- LAYOUTS: TOP_LANG_LAYOUTS,
129
- STATS_FORMATS: TOP_LANG_STATS_FORMATS,
107
+ OPTIONS: { ...renderTopLanguages.OPTIONS, role: OWNER_AFFILIATIONS },
130
108
  });