@stoked-ui/github 0.0.0-a.0 → 0.1.0-alpha.11.2

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 (87) hide show
  1. package/GithubBranch/GithubBranch.d.ts +12 -0
  2. package/GithubBranch/GithubBranch.js +177 -0
  3. package/GithubBranch/index.d.ts +2 -0
  4. package/GithubBranch/index.js +2 -0
  5. package/GithubBranch/package.json +6 -0
  6. package/GithubCalendar/GithubCalendar.d.ts +6 -2
  7. package/GithubCalendar/GithubCalendar.js +70 -28
  8. package/GithubCommit/GithubCommit.d.ts +11 -0
  9. package/GithubCommit/GithubCommit.js +170 -0
  10. package/GithubCommit/index.d.ts +2 -0
  11. package/GithubCommit/index.js +2 -0
  12. package/GithubCommit/package.json +6 -0
  13. package/GithubEvents/EventTypes/PullRequest/PullRequestEvent.js +1 -1
  14. package/GithubEvents/GithubEvents.d.ts +4 -24
  15. package/GithubEvents/GithubEvents.js +141 -220
  16. package/apiHandlers/createGithubBranchHandler.d.ts +12 -0
  17. package/apiHandlers/createGithubBranchHandler.js +41 -0
  18. package/apiHandlers/createGithubCommitHandler.d.ts +12 -0
  19. package/apiHandlers/createGithubCommitHandler.js +39 -0
  20. package/apiHandlers/createGithubContributionsHandler.d.ts +15 -0
  21. package/apiHandlers/createGithubContributionsHandler.js +41 -0
  22. package/apiHandlers/createGithubEventsHandler.d.ts +15 -0
  23. package/apiHandlers/createGithubEventsHandler.js +50 -0
  24. package/apiHandlers/getBranchCompareDetails.d.ts +9 -0
  25. package/apiHandlers/getBranchCompareDetails.js +29 -0
  26. package/apiHandlers/getCommitDetails.d.ts +8 -0
  27. package/apiHandlers/getCommitDetails.js +34 -0
  28. package/apiHandlers/getGithubContributions.d.ts +8 -0
  29. package/apiHandlers/getGithubContributions.js +126 -0
  30. package/apiHandlers/getGithubEvents.d.ts +25 -0
  31. package/apiHandlers/getGithubEvents.js +142 -0
  32. package/apiHandlers/getPullRequestDetails.js +2 -2
  33. package/apiHandlers/githubApi.d.ts +17 -0
  34. package/apiHandlers/githubApi.js +155 -0
  35. package/apiHandlers/index.d.ts +9 -0
  36. package/apiHandlers/index.js +9 -1
  37. package/components/GithubContributorsList.d.ts +8 -0
  38. package/components/GithubContributorsList.js +72 -0
  39. package/components/fetchGithubViewData.d.ts +1 -0
  40. package/components/fetchGithubViewData.js +10 -0
  41. package/index.d.ts +7 -3
  42. package/index.js +6 -4
  43. package/modern/GithubBranch/GithubBranch.js +177 -0
  44. package/modern/GithubBranch/index.js +2 -0
  45. package/modern/GithubCalendar/GithubCalendar.js +70 -28
  46. package/modern/GithubCommit/GithubCommit.js +170 -0
  47. package/modern/GithubCommit/index.js +2 -0
  48. package/modern/GithubEvents/EventTypes/PullRequest/PullRequestEvent.js +1 -1
  49. package/modern/GithubEvents/GithubEvents.js +141 -220
  50. package/modern/apiHandlers/createGithubBranchHandler.js +41 -0
  51. package/modern/apiHandlers/createGithubCommitHandler.js +39 -0
  52. package/modern/apiHandlers/createGithubContributionsHandler.js +41 -0
  53. package/modern/apiHandlers/createGithubEventsHandler.js +50 -0
  54. package/modern/apiHandlers/getBranchCompareDetails.js +29 -0
  55. package/modern/apiHandlers/getCommitDetails.js +34 -0
  56. package/modern/apiHandlers/getGithubContributions.js +126 -0
  57. package/modern/apiHandlers/getGithubEvents.js +142 -0
  58. package/modern/apiHandlers/getPullRequestDetails.js +2 -2
  59. package/modern/apiHandlers/githubApi.js +155 -0
  60. package/modern/apiHandlers/index.js +9 -1
  61. package/modern/components/GithubContributorsList.js +72 -0
  62. package/modern/components/fetchGithubViewData.js +10 -0
  63. package/modern/index.js +6 -4
  64. package/node/GithubBranch/GithubBranch.js +185 -0
  65. package/node/GithubBranch/index.js +9 -0
  66. package/node/GithubCalendar/GithubCalendar.js +70 -28
  67. package/node/GithubCommit/GithubCommit.js +178 -0
  68. package/node/GithubCommit/index.js +9 -0
  69. package/node/GithubEvents/EventTypes/PullRequest/PullRequestEvent.js +1 -1
  70. package/node/GithubEvents/GithubEvents.js +148 -223
  71. package/node/apiHandlers/createGithubBranchHandler.js +48 -0
  72. package/node/apiHandlers/createGithubCommitHandler.js +46 -0
  73. package/node/apiHandlers/createGithubContributionsHandler.js +48 -0
  74. package/node/apiHandlers/createGithubEventsHandler.js +57 -0
  75. package/node/apiHandlers/getBranchCompareDetails.js +35 -0
  76. package/node/apiHandlers/getCommitDetails.js +40 -0
  77. package/node/apiHandlers/getGithubContributions.js +132 -0
  78. package/node/apiHandlers/getGithubEvents.js +149 -0
  79. package/node/apiHandlers/getPullRequestDetails.js +2 -2
  80. package/node/apiHandlers/githubApi.js +168 -0
  81. package/node/apiHandlers/index.js +64 -1
  82. package/node/components/GithubContributorsList.js +80 -0
  83. package/node/components/fetchGithubViewData.js +16 -0
  84. package/node/index.js +77 -2
  85. package/package.json +2 -5
  86. package/stoked-ui-github-0.1.0-alpha.11.2.tgz +0 -0
  87. package/types/github.d.ts +75 -11
@@ -0,0 +1,29 @@
1
+ import { buildGithubContributors, fetchGithubResource, normalizeGithubCommit, normalizeGithubFile, summarizeGithubStats } from './githubApi';
2
+ export default async function getBranchCompareDetails(params) {
3
+ const {
4
+ owner,
5
+ repo,
6
+ base,
7
+ head
8
+ } = params;
9
+ if (!owner || !repo || !base || !head) {
10
+ throw new Error('Missing required parameters: owner, repo, base, head');
11
+ }
12
+ const compare = await fetchGithubResource(`/repos/${owner}/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}`);
13
+ const commits = (compare.commits || []).map(commit => normalizeGithubCommit(commit));
14
+ const files = (compare.files || []).map(file => normalizeGithubFile(file));
15
+ return {
16
+ repo: `${owner}/${repo}`,
17
+ baseRef: base,
18
+ headRef: head,
19
+ status: compare.status || 'unknown',
20
+ aheadBy: compare.ahead_by || 0,
21
+ behindBy: compare.behind_by || 0,
22
+ totalCommits: compare.total_commits || commits.length,
23
+ url: compare.html_url || `https://github.com/${owner}/${repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}`,
24
+ contributors: buildGithubContributors(compare.commits || []),
25
+ commits,
26
+ files,
27
+ stats: summarizeGithubStats(files)
28
+ };
29
+ }
@@ -0,0 +1,34 @@
1
+ import { buildGithubContributors, fetchGithubResource, getGithubMessageSummary, getGithubShortRef, normalizeGithubCommit, normalizeGithubFile, summarizeGithubStats } from './githubApi';
2
+ export default async function getCommitDetails(params) {
3
+ var _commit$commit, _commit$commit2, _commit$commit3, _commit$commit4;
4
+ const {
5
+ owner,
6
+ repo,
7
+ ref
8
+ } = params;
9
+ if (!owner || !repo || !ref) {
10
+ throw new Error('Missing required parameters: owner, repo, ref');
11
+ }
12
+ const commit = await fetchGithubResource(`/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}`);
13
+ const files = (commit.files || []).map(file => normalizeGithubFile(file));
14
+ const commits = [normalizeGithubCommit(commit)];
15
+ const contributor = buildGithubContributors([commit])[0] || {
16
+ login: commits[0].author.login,
17
+ name: commits[0].author.name,
18
+ avatarUrl: commits[0].author.avatar,
19
+ contributions: 1
20
+ };
21
+ return {
22
+ repo: `${owner}/${repo}`,
23
+ ref: commit.sha || ref,
24
+ shortRef: getGithubShortRef(commit.sha || ref),
25
+ url: commit.html_url || `https://github.com/${owner}/${repo}/commit/${encodeURIComponent(commit.sha || ref)}`,
26
+ summary: getGithubMessageSummary(((_commit$commit = commit.commit) == null ? void 0 : _commit$commit.message) || ''),
27
+ message: ((_commit$commit2 = commit.commit) == null ? void 0 : _commit$commit2.message) || '',
28
+ committedAt: ((_commit$commit3 = commit.commit) == null || (_commit$commit3 = _commit$commit3.author) == null ? void 0 : _commit$commit3.date) || ((_commit$commit4 = commit.commit) == null || (_commit$commit4 = _commit$commit4.committer) == null ? void 0 : _commit$commit4.date) || '',
29
+ contributor,
30
+ commits,
31
+ files,
32
+ stats: summarizeGithubStats(files)
33
+ };
34
+ }
@@ -0,0 +1,126 @@
1
+ const CONTRIBUTION_LEVEL_MAP = {
2
+ NONE: 0,
3
+ FIRST_QUARTILE: 1,
4
+ SECOND_QUARTILE: 2,
5
+ THIRD_QUARTILE: 3,
6
+ FOURTH_QUARTILE: 4
7
+ };
8
+ const GITHUB_CONTRIBUTIONS_QUERY = `
9
+ query GithubContributions($login: String!, $from: DateTime!, $to: DateTime!) {
10
+ user(login: $login) {
11
+ contributionsCollection(from: $from, to: $to) {
12
+ contributionCalendar {
13
+ totalContributions
14
+ weeks {
15
+ contributionDays {
16
+ date
17
+ contributionCount
18
+ contributionLevel
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ `;
26
+ function isValidDateInput(value) {
27
+ if (!value) {
28
+ return false;
29
+ }
30
+ return !Number.isNaN(new Date(value).getTime());
31
+ }
32
+ function toDateRange(from, to) {
33
+ const rangeEnd = isValidDateInput(to) ? new Date(to) : new Date();
34
+ const rangeStart = isValidDateInput(from) ? new Date(from) : new Date(rangeEnd);
35
+ if (!isValidDateInput(from)) {
36
+ rangeStart.setFullYear(rangeEnd.getFullYear() - 1);
37
+ }
38
+ if (rangeStart > rangeEnd) {
39
+ throw new Error('Invalid contribution range: "from" must be before "to".');
40
+ }
41
+ return {
42
+ from: rangeStart,
43
+ to: rangeEnd
44
+ };
45
+ }
46
+ function buildContributionTotals(contributions) {
47
+ return contributions.reduce((acc, contribution) => {
48
+ const year = contribution.date.slice(0, 4);
49
+ acc[year] = (acc[year] || 0) + contribution.count;
50
+ return acc;
51
+ }, {});
52
+ }
53
+ function buildCountLabel(total, from, to) {
54
+ const fromYear = from.getFullYear();
55
+ const toYear = to.getFullYear();
56
+ if (fromYear === toYear) {
57
+ return `${total} contributions in ${fromYear}`;
58
+ }
59
+ return `${total} contributions from ${fromYear} to ${toYear}`;
60
+ }
61
+ export default async function getGithubContributions({
62
+ githubUser,
63
+ githubToken = process.env.GITHUB_TOKEN,
64
+ from,
65
+ to
66
+ }) {
67
+ var _payload$errors2, _payload$data;
68
+ if (!githubUser) {
69
+ throw new Error('Missing required parameter: githubUser');
70
+ }
71
+ if (!githubToken) {
72
+ throw new Error('GitHub token not configured');
73
+ }
74
+ const range = toDateRange(from, to);
75
+ const response = await fetch('https://api.github.com/graphql', {
76
+ method: 'POST',
77
+ headers: {
78
+ 'Content-Type': 'application/json',
79
+ 'User-Agent': 'stoked-ui-github-calendar',
80
+ Authorization: `Bearer ${githubToken}`
81
+ },
82
+ body: JSON.stringify({
83
+ query: GITHUB_CONTRIBUTIONS_QUERY,
84
+ variables: {
85
+ login: githubUser,
86
+ from: range.from.toISOString(),
87
+ to: range.to.toISOString()
88
+ }
89
+ })
90
+ });
91
+ const payload = await response.json();
92
+ if (!response.ok) {
93
+ var _payload$errors;
94
+ const message = (payload == null ? void 0 : payload.message) || (payload == null || (_payload$errors = payload.errors) == null || (_payload$errors = _payload$errors[0]) == null ? void 0 : _payload$errors.message) || `GitHub GraphQL error: ${response.status}`;
95
+ throw new Error(message);
96
+ }
97
+ if (payload != null && (_payload$errors2 = payload.errors) != null && _payload$errors2.length) {
98
+ throw new Error(payload.errors.map(error => error.message || 'Unknown GitHub GraphQL error').join('; '));
99
+ }
100
+ const calendar = payload == null || (_payload$data = payload.data) == null || (_payload$data = _payload$data.user) == null || (_payload$data = _payload$data.contributionsCollection) == null ? void 0 : _payload$data.contributionCalendar;
101
+ if (!calendar) {
102
+ throw new Error(`No contribution calendar returned for "${githubUser}".`);
103
+ }
104
+ const contributions = (calendar.weeks || []).flatMap(week => week.contributionDays || []).map(day => {
105
+ var _CONTRIBUTION_LEVEL_M;
106
+ return {
107
+ date: day.date,
108
+ count: day.contributionCount,
109
+ level: (_CONTRIBUTION_LEVEL_M = CONTRIBUTION_LEVEL_MAP[day.contributionLevel]) != null ? _CONTRIBUTION_LEVEL_M : 0
110
+ };
111
+ }).sort((a, b) => {
112
+ if (a.date < b.date) {
113
+ return -1;
114
+ }
115
+ if (a.date > b.date) {
116
+ return 1;
117
+ }
118
+ return 0;
119
+ });
120
+ const total = buildContributionTotals(contributions);
121
+ return {
122
+ total,
123
+ contributions,
124
+ countLabel: buildCountLabel(calendar.totalContributions || 0, range.from, range.to)
125
+ };
126
+ }
@@ -0,0 +1,142 @@
1
+ function parseLinkHeader(header) {
2
+ if (!header) return {};
3
+ return header.split(',').reduce((links, part) => {
4
+ const match = part.match(/<(.+)>;\s*rel="([\w]+)"/);
5
+ if (match) {
6
+ const [, url, rel] = match;
7
+ if (rel === 'next' || rel === 'last') {
8
+ links[rel] = url;
9
+ }
10
+ }
11
+ return links;
12
+ }, {});
13
+ }
14
+ export async function githubEventsQuery({
15
+ query,
16
+ githubUser,
17
+ githubToken
18
+ }) {
19
+ try {
20
+ const {
21
+ page = query.page || 1,
22
+ per_page = query.per_page || 100,
23
+ repo,
24
+ action,
25
+ date,
26
+ description
27
+ } = query;
28
+ const pageNum = Number(page);
29
+ const perPage = Number(per_page);
30
+ console.log(`Fetching events for user: ${githubUser}, page: ${pageNum}, per_page: ${perPage}`);
31
+ let allEvents = [];
32
+ let hasMore = true;
33
+ let githubPage = 1;
34
+ const maxPages = 30;
35
+ const fetchPerPage = 100;
36
+ while (hasMore && githubPage <= maxPages) {
37
+ console.log(`Fetching page ${githubPage}...`);
38
+ const fetchOptions = {
39
+ headers: {
40
+ 'User-Agent': 'brianstoker.com-website'
41
+ }
42
+ };
43
+ if (githubToken) {
44
+ fetchOptions.headers.Authorization = `token ${githubToken}`;
45
+ }
46
+ const queryParams = new URLSearchParams({
47
+ page: String(githubPage),
48
+ per_page: String(fetchPerPage)
49
+ });
50
+ const response = await fetch(`https://api.github.com/users/${githubUser}/events?${queryParams}`, fetchOptions);
51
+ if (!response.ok) {
52
+ const errorText = await response.text();
53
+ console.error(`GitHub API error: ${response.status}`, errorText);
54
+ throw new Error(`GitHub API error: ${response.status} - ${errorText}`);
55
+ }
56
+ const data = await response.json();
57
+ console.log(`Page ${githubPage}: Received ${data.length} events`);
58
+ if (data.length === 0) {
59
+ hasMore = false;
60
+ } else {
61
+ allEvents = [...allEvents, ...data];
62
+ console.log(`Total events so far: ${allEvents.length}`);
63
+ const linkHeader = response.headers.get('Link');
64
+ const links = parseLinkHeader(linkHeader);
65
+ hasMore = !!links.next;
66
+ githubPage++;
67
+ }
68
+ }
69
+ allEvents = Array.from(new Map(allEvents.map(event => [event.id, event])).values());
70
+ console.log(`Final total events: ${allEvents.length}`);
71
+ let filteredEvents = allEvents;
72
+ if (repo) {
73
+ filteredEvents = filteredEvents.filter(event => event.repo.name === repo);
74
+ }
75
+ if (action) {
76
+ filteredEvents = filteredEvents.filter(event => event.type.replace('Event', '') === action);
77
+ }
78
+ if (date) {
79
+ const now = new Date();
80
+ let cutoffDate;
81
+ switch (date) {
82
+ case 'today':
83
+ cutoffDate = new Date(now.setHours(0, 0, 0, 0));
84
+ break;
85
+ case 'yesterday':
86
+ cutoffDate = new Date(now);
87
+ cutoffDate.setDate(cutoffDate.getDate() - 1);
88
+ cutoffDate.setHours(0, 0, 0, 0);
89
+ break;
90
+ case 'week':
91
+ cutoffDate = new Date(now);
92
+ cutoffDate.setDate(cutoffDate.getDate() - 7);
93
+ cutoffDate.setHours(0, 0, 0, 0);
94
+ break;
95
+ case 'month':
96
+ cutoffDate = new Date(now);
97
+ cutoffDate.setMonth(cutoffDate.getMonth() - 1);
98
+ cutoffDate.setHours(0, 0, 0, 0);
99
+ break;
100
+ default:
101
+ cutoffDate = new Date(0);
102
+ }
103
+ filteredEvents = filteredEvents.filter(event => new Date(event.created_at) >= cutoffDate);
104
+ }
105
+ if (description) {
106
+ filteredEvents = filteredEvents.filter(event => {
107
+ var _event$payload$commit, _event$payload$pull_r, _event$payload$issue, _event$payload$issue2;
108
+ let eventDescription = '';
109
+ if (event.type === 'PushEvent' && (_event$payload$commit = event.payload.commits) != null && _event$payload$commit.length) {
110
+ eventDescription = `Pushed ${event.payload.commits.length} commits`;
111
+ } else if (event.type === 'PullRequestEvent' && (_event$payload$pull_r = event.payload.pull_request) != null && _event$payload$pull_r.title) {
112
+ eventDescription = event.payload.pull_request.title;
113
+ } else if (event.type === 'IssuesEvent' && (_event$payload$issue = event.payload.issue) != null && _event$payload$issue.title) {
114
+ eventDescription = event.payload.issue.title;
115
+ } else if (event.type === 'IssueCommentEvent' && (_event$payload$issue2 = event.payload.issue) != null && _event$payload$issue2.title) {
116
+ eventDescription = `Commented on issue: ${event.payload.issue.title}`;
117
+ }
118
+ return eventDescription.toLowerCase().includes(description.toLowerCase());
119
+ });
120
+ }
121
+ const startIndex = (pageNum - 1) * perPage;
122
+ const endIndex = startIndex + perPage;
123
+ const paginatedEvents = filteredEvents.slice(startIndex, endIndex);
124
+ const repositories = Array.from(new Set(allEvents.map(event => event.repo.name))).sort();
125
+ const actionTypes = Array.from(new Set(allEvents.map(event => event.type.replace('Event', '')))).sort();
126
+ return {
127
+ events: paginatedEvents,
128
+ total: filteredEvents.length,
129
+ repositories,
130
+ actionTypes,
131
+ page: pageNum,
132
+ per_page: perPage,
133
+ total_pages: Math.ceil(filteredEvents.length / perPage),
134
+ total_fetched_events: allEvents.length,
135
+ max_pages_fetched: githubPage - 1
136
+ };
137
+ } catch (error) {
138
+ console.error('Error fetching GitHub events:', error);
139
+ throw new Error(`${error instanceof Error ? error.message : String(error)}`);
140
+ }
141
+ }
142
+ export default githubEventsQuery;
@@ -37,7 +37,7 @@ export default async function getPullRequestDetails(params) {
37
37
  if (githubToken) {
38
38
  headers.Authorization = `token ${githubToken}`;
39
39
  }
40
- async function fetchWithRateLimit(url) {
40
+ const fetchWithRateLimit = async url => {
41
41
  const response = await fetch(url, {
42
42
  headers
43
43
  });
@@ -59,7 +59,7 @@ export default async function getPullRequestDetails(params) {
59
59
  data: await response.json(),
60
60
  rateLimit
61
61
  };
62
- }
62
+ };
63
63
 
64
64
  // Fetch basic PR information
65
65
  const {
@@ -0,0 +1,155 @@
1
+ const GITHUB_API_BASE_URL = 'https://api.github.com';
2
+ const DEFAULT_DIFF_LINE_LIMIT = 24;
3
+ function getGithubHeaders() {
4
+ const githubToken = process.env.GITHUB_TOKEN;
5
+ const headers = {
6
+ 'User-Agent': 'stoked-ui-github-components',
7
+ Accept: 'application/vnd.github+json'
8
+ };
9
+ if (githubToken) {
10
+ headers.Authorization = `token ${githubToken}`;
11
+ }
12
+ return headers;
13
+ }
14
+ export async function fetchGithubResource(path) {
15
+ const response = await fetch(`${GITHUB_API_BASE_URL}${path}`, {
16
+ headers: getGithubHeaders()
17
+ });
18
+ const rateLimit = {
19
+ remaining: response.headers.get('x-ratelimit-remaining'),
20
+ reset: response.headers.get('x-ratelimit-reset')
21
+ };
22
+ if (!response.ok) {
23
+ const body = await response.text();
24
+ if (response.status === 403 && rateLimit.remaining === '0') {
25
+ const resetDate = rateLimit.reset ? new Date(Number(rateLimit.reset) * 1000).toLocaleString() : 'later';
26
+ throw new Error(`GitHub rate limit exceeded. Resets at ${resetDate}.`);
27
+ }
28
+ throw new Error(body || `GitHub API error: ${response.status}`);
29
+ }
30
+ return response.json();
31
+ }
32
+ export function parseGithubDiff(patch, maxLines = DEFAULT_DIFF_LINE_LIMIT) {
33
+ if (!patch) {
34
+ return [];
35
+ }
36
+ const patchLines = patch.split('\n');
37
+ const visibleLines = patchLines.slice(0, maxLines);
38
+ const diffLines = visibleLines.map((line, index) => {
39
+ let type = 'context';
40
+ if (line.startsWith('+')) {
41
+ type = 'addition';
42
+ } else if (line.startsWith('-')) {
43
+ type = 'deletion';
44
+ }
45
+ return {
46
+ type,
47
+ content: line,
48
+ lineNumber: index + 1
49
+ };
50
+ });
51
+ if (patchLines.length > maxLines) {
52
+ diffLines.push({
53
+ type: 'context',
54
+ content: `... ${patchLines.length - maxLines} more diff lines`,
55
+ lineNumber: diffLines.length + 1
56
+ });
57
+ }
58
+ return diffLines;
59
+ }
60
+ function toFileChangeType(status) {
61
+ switch (status) {
62
+ case 'added':
63
+ return 'added';
64
+ case 'removed':
65
+ return 'deleted';
66
+ default:
67
+ return 'modified';
68
+ }
69
+ }
70
+ export function normalizeGithubFile(file, maxDiffLines = DEFAULT_DIFF_LINE_LIMIT) {
71
+ return {
72
+ path: file.filename || file.path || 'unknown',
73
+ type: toFileChangeType(file.status),
74
+ additions: file.additions || 0,
75
+ deletions: file.deletions || 0,
76
+ diff: file.diff || parseGithubDiff(file.patch, maxDiffLines)
77
+ };
78
+ }
79
+ function getGithubIdentity(commit) {
80
+ var _commit$commit, _commit$commit2;
81
+ const apiAuthor = commit.author || commit.committer || {};
82
+ const commitAuthor = ((_commit$commit = commit.commit) == null ? void 0 : _commit$commit.author) || ((_commit$commit2 = commit.commit) == null ? void 0 : _commit$commit2.committer) || {};
83
+ return {
84
+ login: apiAuthor.login || commitAuthor.name || commitAuthor.email || commit.sha || 'unknown',
85
+ name: commitAuthor.name || apiAuthor.login || commitAuthor.email || 'Unknown author',
86
+ avatarUrl: apiAuthor.avatar_url || ''
87
+ };
88
+ }
89
+ export function normalizeGithubCommit(commit) {
90
+ var _commit$commit3, _commit$commit4, _commit$commit5;
91
+ const identity = getGithubIdentity(commit);
92
+ return {
93
+ id: commit.sha || commit.node_id || identity.login,
94
+ message: ((_commit$commit3 = commit.commit) == null ? void 0 : _commit$commit3.message) || '',
95
+ author: {
96
+ name: identity.name,
97
+ login: identity.login,
98
+ avatar: identity.avatarUrl
99
+ },
100
+ date: ((_commit$commit4 = commit.commit) == null || (_commit$commit4 = _commit$commit4.author) == null ? void 0 : _commit$commit4.date) || ((_commit$commit5 = commit.commit) == null || (_commit$commit5 = _commit$commit5.committer) == null ? void 0 : _commit$commit5.date) || '',
101
+ hash: commit.sha || '',
102
+ url: commit.html_url || ''
103
+ };
104
+ }
105
+ export function buildGithubContributors(commits) {
106
+ const contributors = new Map();
107
+ commits.forEach(commit => {
108
+ const identity = getGithubIdentity(commit);
109
+ const key = identity.login || identity.name;
110
+ const current = contributors.get(key);
111
+ if (current) {
112
+ current.contributions += 1;
113
+ if (!current.avatarUrl && identity.avatarUrl) {
114
+ current.avatarUrl = identity.avatarUrl;
115
+ }
116
+ if (current.name === 'Unknown author' && identity.name) {
117
+ current.name = identity.name;
118
+ }
119
+ return;
120
+ }
121
+ contributors.set(key, {
122
+ login: identity.login,
123
+ name: identity.name,
124
+ avatarUrl: identity.avatarUrl,
125
+ contributions: 1
126
+ });
127
+ });
128
+ return Array.from(contributors.values()).sort((a, b) => {
129
+ if (b.contributions !== a.contributions) {
130
+ return b.contributions - a.contributions;
131
+ }
132
+ return a.login.localeCompare(b.login);
133
+ });
134
+ }
135
+ export function summarizeGithubStats(files) {
136
+ return files.reduce((stats, file) => ({
137
+ additions: stats.additions + file.additions,
138
+ deletions: stats.deletions + file.deletions,
139
+ changedFiles: stats.changedFiles + 1
140
+ }), {
141
+ additions: 0,
142
+ deletions: 0,
143
+ changedFiles: 0
144
+ });
145
+ }
146
+ export function getGithubMessageSummary(message) {
147
+ var _message$split$;
148
+ return ((_message$split$ = message.split('\n')[0]) == null ? void 0 : _message$split$.trim()) || 'Untitled commit';
149
+ }
150
+ export function getGithubShortRef(ref, length = 7) {
151
+ if (!ref) {
152
+ return '';
153
+ }
154
+ return ref.length > length ? ref.slice(0, length) : ref;
155
+ }
@@ -1 +1,9 @@
1
- export { default as getPullRequestDetails } from './getPullRequestDetails';
1
+ export { default as createGithubBranchHandler } from './createGithubBranchHandler';
2
+ export { default as createGithubCommitHandler } from './createGithubCommitHandler';
3
+ export { default as getBranchCompareDetails } from './getBranchCompareDetails';
4
+ export { default as getCommitDetails } from './getCommitDetails';
5
+ export { default as getPullRequestDetails } from './getPullRequestDetails';
6
+ export { default as getGithubContributions } from './getGithubContributions';
7
+ export { default as createGithubContributionsHandler } from './createGithubContributionsHandler';
8
+ export { default as getGithubEvents, githubEventsQuery } from './getGithubEvents';
9
+ export { default as createGithubEventsHandler } from './createGithubEventsHandler';
@@ -0,0 +1,72 @@
1
+ import * as React from 'react';
2
+ import Avatar from '@mui/material/Avatar';
3
+ import Box from '@mui/material/Box';
4
+ import Chip from '@mui/material/Chip';
5
+ import Typography from '@mui/material/Typography';
6
+ import { styled } from '@mui/material/styles';
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ const ContributorCard = styled(Box)(({
9
+ theme
10
+ }) => ({
11
+ display: 'flex',
12
+ alignItems: 'center',
13
+ gap: theme.spacing(1.5),
14
+ padding: theme.spacing(1.5),
15
+ border: `1px solid ${theme.palette.divider}`,
16
+ borderRadius: theme.shape.borderRadius,
17
+ backgroundColor: theme.palette.background.paper
18
+ }));
19
+ export default function GithubContributorsList({
20
+ contributors,
21
+ title
22
+ }) {
23
+ if (!contributors.length) {
24
+ return null;
25
+ }
26
+ return /*#__PURE__*/_jsxs(Box, {
27
+ sx: {
28
+ mb: 3
29
+ },
30
+ children: [/*#__PURE__*/_jsx(Typography, {
31
+ variant: "subtitle2",
32
+ color: "text.secondary",
33
+ sx: {
34
+ mb: 1.5
35
+ },
36
+ children: title
37
+ }), /*#__PURE__*/_jsx(Box, {
38
+ sx: {
39
+ display: 'flex',
40
+ flexWrap: 'wrap',
41
+ gap: 1.5
42
+ },
43
+ children: contributors.map(contributor => /*#__PURE__*/_jsxs(ContributorCard, {
44
+ children: [/*#__PURE__*/_jsx(Avatar, {
45
+ src: contributor.avatarUrl,
46
+ alt: contributor.login,
47
+ sx: {
48
+ width: 40,
49
+ height: 40
50
+ }
51
+ }), /*#__PURE__*/_jsxs(Box, {
52
+ sx: {
53
+ minWidth: 0
54
+ },
55
+ children: [/*#__PURE__*/_jsx(Typography, {
56
+ variant: "body2",
57
+ fontWeight: 600,
58
+ children: contributor.login
59
+ }), contributor.name && contributor.name !== contributor.login ? /*#__PURE__*/_jsx(Typography, {
60
+ variant: "caption",
61
+ color: "text.secondary",
62
+ children: contributor.name
63
+ }) : null]
64
+ }), /*#__PURE__*/_jsx(Chip, {
65
+ label: `${contributor.contributions} commit${contributor.contributions === 1 ? '' : 's'}`,
66
+ size: "small",
67
+ variant: "outlined"
68
+ })]
69
+ }, `${contributor.login}-${contributor.name}`))
70
+ })]
71
+ });
72
+ }
@@ -0,0 +1,10 @@
1
+ export default async function fetchGithubViewData(apiUrl, params) {
2
+ const query = new URLSearchParams(params);
3
+ const separator = apiUrl.includes('?') ? '&' : '?';
4
+ const response = await fetch(`${apiUrl}${separator}${query.toString()}`);
5
+ if (!response.ok) {
6
+ const errorBody = await response.json().catch(() => null);
7
+ throw new Error((errorBody == null ? void 0 : errorBody.message) || 'Failed to fetch GitHub data');
8
+ }
9
+ return response.json();
10
+ }
package/modern/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  /**
2
- * @stoked-ui/github v0.0.0-a.0
2
+ * @stoked-ui/github v0.1.0-alpha.11.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
- import GithubCalendar from './GithubCalendar';
9
- import GithubEvents from './GithubEvents';
10
- export { GithubCalendar, GithubEvents };
8
+ export { default as GithubBranch } from './GithubBranch';
9
+ export { default as GithubCalendar } from './GithubCalendar';
10
+ export { default as GithubCommit } from './GithubCommit';
11
+ export { default as GithubEvents } from './GithubEvents';
12
+ export { createGithubBranchHandler, createGithubCommitHandler, createGithubContributionsHandler, createGithubEventsHandler, getBranchCompareDetails, getCommitDetails, getGithubContributions, getGithubEvents, getPullRequestDetails, githubEventsQuery } from './apiHandlers';