@volter/twin-github 0.1.2 → 2.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 (215) hide show
  1. package/README.md +88 -36
  2. package/client/github-mirror.css +277 -319
  3. package/client/github-mirror.d.ts +418 -0
  4. package/client/github-mirror.js +485 -0
  5. package/client/github-mirror.tsx +153 -357
  6. package/client/pulls-rest.ts +159 -0
  7. package/client/pulls-workspace.tsx +841 -0
  8. package/dist/client/github-mirror.bundle.js +239 -0
  9. package/dist/client/github-mirror.css +916 -0
  10. package/dist/client/github-mirror.d.ts +418 -0
  11. package/dist/client/github-mirror.js +485 -0
  12. package/dist/client/github-mirror.tsx +1315 -0
  13. package/dist/client/pulls-rest.d.ts +42 -0
  14. package/dist/client/pulls-rest.js +140 -0
  15. package/dist/client/pulls-rest.ts +159 -0
  16. package/dist/client/pulls-workspace.bundle.js +22 -0
  17. package/dist/client/pulls-workspace.d.ts +114 -0
  18. package/dist/client/pulls-workspace.js +418 -0
  19. package/dist/client/pulls-workspace.tsx +841 -0
  20. package/dist/src/cli.d.ts +2 -0
  21. package/dist/src/cli.js +40 -0
  22. package/dist/src/generated/graphql-sdl.gen.json +1 -0
  23. package/dist/src/generated/graphql.gen.json +1 -0
  24. package/dist/src/generated/surface.gen.json +1 -0
  25. package/dist/src/generated/ui.gen.json +1 -0
  26. package/dist/src/github-budget.d.ts +69 -0
  27. package/dist/src/github-budget.js +172 -0
  28. package/dist/src/github-capabilities.d.ts +5 -0
  29. package/dist/src/github-capabilities.js +4468 -0
  30. package/dist/src/github-conformance.d.ts +43 -0
  31. package/dist/src/github-conformance.js +76 -0
  32. package/dist/src/github-connector.d.ts +307 -0
  33. package/dist/src/github-connector.js +1398 -0
  34. package/dist/src/github-events.d.ts +41 -0
  35. package/dist/src/github-events.js +232 -0
  36. package/dist/src/github-git-http.d.ts +49 -0
  37. package/dist/src/github-git-http.js +185 -0
  38. package/dist/src/github-git-plane.d.ts +114 -0
  39. package/dist/src/github-git-plane.js +407 -0
  40. package/dist/src/github-mirror-state.d.ts +2 -0
  41. package/dist/src/github-mirror-state.js +335 -0
  42. package/dist/src/github-mirror-ui.d.ts +20 -0
  43. package/dist/src/github-mirror-ui.js +101 -0
  44. package/dist/src/github-server.d.ts +14 -0
  45. package/dist/src/github-server.js +240 -0
  46. package/dist/src/github-shared.d.ts +12 -0
  47. package/dist/src/github-shared.js +21 -0
  48. package/dist/src/github-twin.d.ts +1411 -0
  49. package/dist/src/github-twin.js +4084 -0
  50. package/dist/src/github-ui-conformance.d.ts +4 -0
  51. package/dist/src/github-ui-conformance.js +105 -0
  52. package/dist/src/github-ui-structure.d.ts +18 -0
  53. package/dist/src/github-ui-structure.js +251 -0
  54. package/dist/src/graphql-wire.d.ts +22 -0
  55. package/dist/src/graphql-wire.js +89 -0
  56. package/dist/src/index.d.ts +17 -0
  57. package/dist/src/index.js +104 -0
  58. package/dist/src/manifest.d.ts +8 -0
  59. package/dist/src/manifest.js +597 -0
  60. package/dist/src/npm-registry.d.ts +7 -0
  61. package/dist/src/npm-registry.js +47 -0
  62. package/dist/src/screens/app-installation.d.ts +3 -0
  63. package/dist/src/screens/app-installation.js +166 -0
  64. package/dist/src/screens/app-manifest.d.ts +3 -0
  65. package/dist/src/screens/app-manifest.js +81 -0
  66. package/dist/src/screens/oauth.d.ts +15 -0
  67. package/dist/src/screens/oauth.js +257 -0
  68. package/dist/src/screens/session.d.ts +8 -0
  69. package/dist/src/screens/session.js +170 -0
  70. package/dist/src/semantics/actions.d.ts +2 -0
  71. package/dist/src/semantics/actions.js +413 -0
  72. package/dist/src/semantics/activity.d.ts +4 -0
  73. package/dist/src/semantics/activity.js +161 -0
  74. package/dist/src/semantics/apps.d.ts +2 -0
  75. package/dist/src/semantics/apps.js +144 -0
  76. package/dist/src/semantics/branches.d.ts +2 -0
  77. package/dist/src/semantics/branches.js +136 -0
  78. package/dist/src/semantics/checks.d.ts +2 -0
  79. package/dist/src/semantics/checks.js +176 -0
  80. package/dist/src/semantics/code-scanning-upload.d.ts +2 -0
  81. package/dist/src/semantics/code-scanning-upload.js +97 -0
  82. package/dist/src/semantics/codespaces.d.ts +2 -0
  83. package/dist/src/semantics/codespaces.js +58 -0
  84. package/dist/src/semantics/commits.d.ts +2 -0
  85. package/dist/src/semantics/commits.js +109 -0
  86. package/dist/src/semantics/contents.d.ts +2 -0
  87. package/dist/src/semantics/contents.js +131 -0
  88. package/dist/src/semantics/deployments.d.ts +2 -0
  89. package/dist/src/semantics/deployments.js +127 -0
  90. package/dist/src/semantics/gists.d.ts +2 -0
  91. package/dist/src/semantics/gists.js +86 -0
  92. package/dist/src/semantics/git.d.ts +2 -0
  93. package/dist/src/semantics/git.js +235 -0
  94. package/dist/src/semantics/graphql.d.ts +7 -0
  95. package/dist/src/semantics/graphql.js +512 -0
  96. package/dist/src/semantics/index.d.ts +5 -0
  97. package/dist/src/semantics/index.js +62 -0
  98. package/dist/src/semantics/issues.d.ts +2 -0
  99. package/dist/src/semantics/issues.js +456 -0
  100. package/dist/src/semantics/keys.d.ts +2 -0
  101. package/dist/src/semantics/keys.js +66 -0
  102. package/dist/src/semantics/labels.d.ts +2 -0
  103. package/dist/src/semantics/labels.js +100 -0
  104. package/dist/src/semantics/meta.d.ts +12 -0
  105. package/dist/src/semantics/meta.js +144 -0
  106. package/dist/src/semantics/notifications.d.ts +4 -0
  107. package/dist/src/semantics/notifications.js +62 -0
  108. package/dist/src/semantics/orgs.d.ts +2 -0
  109. package/dist/src/semantics/orgs.js +420 -0
  110. package/dist/src/semantics/packages.d.ts +2 -0
  111. package/dist/src/semantics/packages.js +55 -0
  112. package/dist/src/semantics/pages.d.ts +2 -0
  113. package/dist/src/semantics/pages.js +176 -0
  114. package/dist/src/semantics/projects.d.ts +2 -0
  115. package/dist/src/semantics/projects.js +239 -0
  116. package/dist/src/semantics/pulls.d.ts +2 -0
  117. package/dist/src/semantics/pulls.js +462 -0
  118. package/dist/src/semantics/push-reactions.d.ts +101 -0
  119. package/dist/src/semantics/push-reactions.js +513 -0
  120. package/dist/src/semantics/releases.d.ts +19 -0
  121. package/dist/src/semantics/releases.js +231 -0
  122. package/dist/src/semantics/repo-invitations.d.ts +2 -0
  123. package/dist/src/semantics/repo-invitations.js +98 -0
  124. package/dist/src/semantics/repos.d.ts +17 -0
  125. package/dist/src/semantics/repos.js +390 -0
  126. package/dist/src/semantics/rulesets.d.ts +2 -0
  127. package/dist/src/semantics/rulesets.js +111 -0
  128. package/dist/src/semantics/search.d.ts +2 -0
  129. package/dist/src/semantics/search.js +84 -0
  130. package/dist/src/semantics/security.d.ts +2 -0
  131. package/dist/src/semantics/security.js +177 -0
  132. package/dist/src/semantics/shared.d.ts +61 -0
  133. package/dist/src/semantics/shared.js +151 -0
  134. package/dist/src/semantics/users.d.ts +2 -0
  135. package/dist/src/semantics/users.js +255 -0
  136. package/dist/src/semantics/webhooks.d.ts +2 -0
  137. package/dist/src/semantics/webhooks.js +107 -0
  138. package/dist/test-fixtures/github-a11y-reference.pr-list.SOURCE.md +56 -0
  139. package/dist/test-fixtures/github-a11y-reference.pr-list.json +1447 -0
  140. package/dist/test-fixtures/github-comment-schema.SOURCE.md +11 -0
  141. package/dist/test-fixtures/github-comment-schema.json +702 -0
  142. package/dist/test-fixtures/github-known-deviations.json +94 -0
  143. package/dist/test-fixtures/github-openapi-operations.SOURCE.md +76 -0
  144. package/dist/test-fixtures/github-openapi-operations.json +362 -0
  145. package/dist/test-fixtures/github-pull-schema.SOURCE.md +37 -0
  146. package/dist/test-fixtures/github-pull-schema.json +3601 -0
  147. package/dist/test-fixtures/github-review-schema.SOURCE.md +12 -0
  148. package/dist/test-fixtures/github-review-schema.json +236 -0
  149. package/package.json +20 -11
  150. package/src/cli.ts +7 -6
  151. package/src/generated/graphql-sdl.gen.json +1 -0
  152. package/src/generated/graphql.gen.json +1 -0
  153. package/src/generated/surface.gen.json +1 -0
  154. package/src/generated/ui.gen.json +1 -0
  155. package/src/github-a11y-snapshot.uitest.ts +5 -5
  156. package/src/github-budget.ts +4 -4
  157. package/src/github-capabilities.ts +1993 -556
  158. package/src/github-conformance.ts +12 -7
  159. package/src/github-connector.ts +108 -96
  160. package/src/github-events.ts +225 -95
  161. package/src/github-git-http.ts +48 -84
  162. package/src/github-git-plane.ts +247 -385
  163. package/src/github-journey.uitest.ts +28 -44
  164. package/src/github-mirror-state.ts +23 -61
  165. package/src/github-mirror-ui.ts +16 -10
  166. package/src/github-server.ts +158 -60
  167. package/src/github-shared.ts +1 -1
  168. package/src/github-twin.ts +1525 -4494
  169. package/src/github-ui-conformance.ts +7 -8
  170. package/src/github-ui-structure.ts +19 -18
  171. package/src/graphql-wire.ts +115 -0
  172. package/src/index.ts +50 -7
  173. package/src/manifest.ts +605 -0
  174. package/src/npm-registry.ts +43 -0
  175. package/src/screens/app-installation.tsx +217 -0
  176. package/src/screens/app-manifest.tsx +97 -0
  177. package/src/screens/oauth.tsx +258 -0
  178. package/src/screens/session.tsx +193 -0
  179. package/src/semantics/actions.ts +395 -0
  180. package/src/semantics/activity.ts +171 -0
  181. package/src/semantics/apps.ts +133 -0
  182. package/src/semantics/branches.ts +133 -0
  183. package/src/semantics/checks.ts +163 -0
  184. package/src/semantics/code-scanning-upload.ts +92 -0
  185. package/src/semantics/codespaces.ts +58 -0
  186. package/src/semantics/commits.ts +109 -0
  187. package/src/semantics/contents.ts +112 -0
  188. package/src/semantics/deployments.ts +116 -0
  189. package/src/semantics/gists.ts +85 -0
  190. package/src/semantics/git.ts +226 -0
  191. package/src/semantics/graphql.ts +505 -0
  192. package/src/semantics/index.ts +68 -0
  193. package/src/semantics/issues.ts +434 -0
  194. package/src/semantics/keys.ts +66 -0
  195. package/src/semantics/labels.ts +91 -0
  196. package/src/semantics/meta.ts +139 -0
  197. package/src/semantics/notifications.ts +58 -0
  198. package/src/semantics/orgs.ts +383 -0
  199. package/src/semantics/packages.ts +59 -0
  200. package/src/semantics/pages.ts +154 -0
  201. package/src/semantics/projects.ts +246 -0
  202. package/src/semantics/pulls.ts +421 -0
  203. package/src/semantics/push-reactions.ts +471 -0
  204. package/src/semantics/releases.ts +213 -0
  205. package/src/semantics/repo-invitations.ts +112 -0
  206. package/src/semantics/repos.ts +373 -0
  207. package/src/semantics/rulesets.ts +107 -0
  208. package/src/semantics/search.ts +83 -0
  209. package/src/semantics/security.ts +153 -0
  210. package/src/semantics/shared.ts +181 -0
  211. package/src/semantics/users.ts +252 -0
  212. package/src/semantics/webhooks.ts +101 -0
  213. package/test-fixtures/github-known-deviations.json +7 -8
  214. package/test-fixtures/github-openapi-operations.json +46 -227
  215. package/src/github-graphql.ts +0 -398
@@ -0,0 +1,159 @@
1
+ // The pull-request workspace's reads, through GitHub's own REST API on this origin (docs/contributing/
2
+ // architecture.md, "Screens": a workspace reads and writes only through the pack's operations). The same
3
+ // calls a GitHub client makes: the signed-in person's repositories and their organizations' repositories,
4
+ // each repository's pull requests, and for each pull request its detail, reviews and CI rollup; the one
5
+ // being read also brings its commits, files and both kinds of comments. What comes back is shaped into the
6
+ // props the workspace's components take.
7
+ import type { ChangedFile, Comment, Commit, PullRequest, Review } from './github-mirror.tsx';
8
+
9
+ type Json = any;
10
+ /** One commit status or check run on the open pull request's head, as its merge box and Checks tab list them. */
11
+ export type Check = { name: string; state: string; description?: string; targetUrl?: string };
12
+ /** A repository as its header counts it: open issues (pull requests excluded) and open pull requests. */
13
+ export type RepoFacts = { openIssues: number; labels: Array<{ name: string; color?: string; description?: string }>; milestones: number; hasDiscussions?: boolean };
14
+ /** A state change on the pull request's timeline (closed, reopened), as GitHub's timeline lists it. */
15
+ export type StateEvent = { event: string; at?: string; actor?: string };
16
+ export type PullsData = {
17
+ pullRequests: PullRequest[]; reviews: Review[]; commits: Commit[]; comments: Comment[]; reviewComments: Comment[]; files: ChangedFile[];
18
+ viewer?: string; checks: Check[]; repoFacts: Record<string, RepoFacts>; stateEvents: StateEvent[];
19
+ };
20
+
21
+ const get = async (base: string, path: string): Promise<Json> => {
22
+ const r = await fetch(`${base}${path}`, { headers: { accept: 'application/vnd.github+json' } });
23
+ if (!r.ok) return undefined;
24
+ return r.json();
25
+ };
26
+ const list = async (base: string, path: string): Promise<Json[]> => {
27
+ const body = await get(base, path);
28
+ return Array.isArray(body) ? body : [];
29
+ };
30
+
31
+ /** A head commit's CI, as GitHub's merge box rolls it up: statuses and check runs together. */
32
+ async function ciOf(base: string, repo: string, sha: string | undefined): Promise<string | undefined> {
33
+ if (!sha) return undefined;
34
+ const [combined, checks] = await Promise.all([get(base, `/repos/${repo}/commits/${sha}/status`), get(base, `/repos/${repo}/commits/${sha}/check-runs`)]);
35
+ const states: string[] = [
36
+ ...((combined?.statuses ?? []) as Json[]).map((s) => String(s.state)),
37
+ ...((checks?.check_runs ?? []) as Json[]).map((c) => (c.status === 'completed' ? String(c.conclusion ?? 'neutral') : 'pending')),
38
+ ];
39
+ if (!states.length) return undefined;
40
+ if (states.some((s) => s === 'failure' || s === 'error' || s === 'timed_out' || s === 'cancelled')) return 'failure';
41
+ if (states.some((s) => s === 'pending')) return 'pending';
42
+ return 'success';
43
+ }
44
+
45
+ function pullOf(repo: string, p: Json, ciStatus: string | undefined): PullRequest {
46
+ const merged = Boolean(p.merged_at);
47
+ return {
48
+ repo,
49
+ number: Number(p.number),
50
+ title: p.title ?? undefined,
51
+ body: p.body ?? undefined,
52
+ state: merged ? 'merged' : String(p.state ?? 'open'),
53
+ merged,
54
+ mergedAt: p.merged_at ?? undefined,
55
+ mergeCommit: p.merge_commit_sha ?? undefined,
56
+ url: p.html_url ?? undefined,
57
+ baseBranch: p.base?.ref,
58
+ headBranch: p.head?.ref,
59
+ headSha: p.head?.sha,
60
+ author: p.user?.login,
61
+ authorAvatarUrl: p.user?.avatar_url,
62
+ createdAt: p.created_at ?? undefined,
63
+ updatedAt: p.updated_at ?? undefined,
64
+ additions: p.additions,
65
+ deletions: p.deletions,
66
+ changedFiles: p.changed_files,
67
+ commitsCount: p.commits,
68
+ commentsCount: p.comments,
69
+ reviewCommentsCount: p.review_comments,
70
+ labels: ((p.labels ?? []) as Json[]).map((l) => ({ name: String(l.name), ...(l.color ? { color: String(l.color) } : {}), ...(l.description ? { description: String(l.description) } : {}) })),
71
+ assignees: ((p.assignees ?? []) as Json[]).map((a) => String(a.login)),
72
+ requestedReviewers: ((p.requested_reviewers ?? []) as Json[]).map((r) => String(r.login)),
73
+ milestone: p.milestone?.title ?? undefined,
74
+ ciStatus,
75
+ closedAt: p.closed_at ?? undefined,
76
+ mergedBy: p.merged_by?.login ?? undefined,
77
+ mergeable: p.mergeable ?? undefined,
78
+ draft: Boolean(p.draft),
79
+ };
80
+ }
81
+
82
+ const commentOf = (repo: string, number: number) => (c: Json): Comment => ({
83
+ repo, number, id: c.id, authorAssociation: c.author_association ?? undefined, author: c.user?.login, authorAvatarUrl: c.user?.avatar_url, body: c.body ?? '', createdAt: c.created_at, url: c.html_url, ...(c.path ? { path: String(c.path) } : {}),
84
+ });
85
+
86
+ /** Every repository the signed-in person can see: their own and their organizations'. */
87
+ async function reposOf(base: string): Promise<string[]> {
88
+ const own = await list(base, '/user/repos?per_page=100');
89
+ const orgs = await list(base, '/user/orgs');
90
+ const theirs = (await Promise.all(orgs.map((o) => list(base, `/orgs/${encodeURIComponent(String(o.login))}/repos?per_page=100`)))).flat();
91
+ return [...new Set([...own, ...theirs].map((r) => String(r.full_name)))].sort();
92
+ }
93
+
94
+ /** The checks a head commit carries, statuses and check runs together (the newest status per context). */
95
+ async function checksOf(base: string, repo: string, sha: string | undefined): Promise<Check[]> {
96
+ if (!sha) return [];
97
+ const [combined, runs] = await Promise.all([get(base, `/repos/${repo}/commits/${sha}/status`), get(base, `/repos/${repo}/commits/${sha}/check-runs`)]);
98
+ return [
99
+ ...((combined?.statuses ?? []) as Json[]).map((s) => ({ name: String(s.context ?? 'default'), state: String(s.state), description: s.description ?? undefined, targetUrl: s.target_url ?? undefined })),
100
+ ...((runs?.check_runs ?? []) as Json[]).map((c) => ({ name: String(c.name), state: c.status === 'completed' ? String(c.conclusion ?? 'neutral') : 'pending', description: c.output?.title ?? undefined, targetUrl: c.details_url ?? c.html_url ?? undefined })),
101
+ ];
102
+ }
103
+
104
+ /** What a repository's header and list filters show: its open issues, its labels (with their colours) and milestones. */
105
+ async function factsOf(base: string, repo: string): Promise<RepoFacts> {
106
+ const [issues, labels, milestones, meta] = await Promise.all([
107
+ list(base, `/repos/${repo}/issues?state=open&per_page=100`),
108
+ list(base, `/repos/${repo}/labels?per_page=100`),
109
+ list(base, `/repos/${repo}/milestones?per_page=100`),
110
+ get(base, `/repos/${repo}`),
111
+ ]);
112
+ return {
113
+ openIssues: issues.filter((i) => !i.pull_request).length,
114
+ labels: labels.map((l) => ({ name: String(l.name), ...(l.color ? { color: String(l.color) } : {}), ...(l.description ? { description: String(l.description) } : {}) })),
115
+ milestones: milestones.length,
116
+ hasDiscussions: Boolean(meta?.has_discussions),
117
+ };
118
+ }
119
+
120
+ /** The workspace's data: every pull request with its reviews and CI, and the open one's thread in full. */
121
+ export async function loadPulls(base: string, open?: { repo: string; number: number }): Promise<PullsData> {
122
+ const repos = await reposOf(base);
123
+ const viewer = (await get(base, '/user'))?.login as string | undefined;
124
+ const repoFacts: Record<string, RepoFacts> = Object.fromEntries(await Promise.all(repos.map(async (r) => [r, await factsOf(base, r)] as const)));
125
+ const pullRequests: PullRequest[] = [];
126
+ const reviews: Review[] = [];
127
+ for (const repo of repos) {
128
+ for (const summary of await list(base, `/repos/${repo}/pulls?state=all&per_page=100`)) {
129
+ const number = Number(summary.number);
130
+ const [detail, reviewed] = await Promise.all([get(base, `/repos/${repo}/pulls/${number}`), list(base, `/repos/${repo}/pulls/${number}/reviews`)]);
131
+ const p = detail ?? summary;
132
+ // GitHub's pull request carries no review count; the list the workspace just read is it
133
+ pullRequests.push({ ...pullOf(repo, p, await ciOf(base, repo, p.head?.sha)), reviewCount: reviewed.length });
134
+ for (const r of reviewed) reviews.push({ repo, number, id: r.id, reviewer: r.user?.login, reviewerAvatarUrl: r.user?.avatar_url, state: r.state, body: r.body ?? '', submittedAt: r.submitted_at });
135
+ }
136
+ }
137
+ if (!open) return { pullRequests, reviews, commits: [], comments: [], reviewComments: [], files: [], viewer, checks: [], repoFacts, stateEvents: [] };
138
+ const { repo, number } = open;
139
+ const [commits, files, comments, reviewComments, timeline] = await Promise.all([
140
+ list(base, `/repos/${repo}/pulls/${number}/commits?per_page=100`),
141
+ list(base, `/repos/${repo}/pulls/${number}/files?per_page=100`),
142
+ list(base, `/repos/${repo}/issues/${number}/comments?per_page=100`),
143
+ list(base, `/repos/${repo}/pulls/${number}/comments?per_page=100`),
144
+ list(base, `/repos/${repo}/issues/${number}/timeline?per_page=100`),
145
+ ]);
146
+ const checks = await checksOf(base, repo, pullRequests.find((p) => p.repo === repo && p.number === number)?.headSha);
147
+ return {
148
+ pullRequests,
149
+ reviews,
150
+ viewer,
151
+ checks,
152
+ repoFacts,
153
+ stateEvents: timeline.filter((e) => e.event === "closed" || e.event === "reopened").map((e) => ({ event: String(e.event), at: e.created_at ?? undefined, actor: e.actor?.login ?? undefined })),
154
+ commits: commits.map((c) => ({ repo, number, sha: String(c.sha), message: c.commit?.message, author: c.commit?.author?.name, authorLogin: c.author?.login, authorAvatarUrl: c.author?.avatar_url, date: c.commit?.author?.date, url: c.html_url })),
155
+ files: files.map((f) => ({ repo, number, filename: String(f.filename), status: f.status, additions: f.additions, deletions: f.deletions, changes: f.changes, patch: f.patch })),
156
+ comments: comments.map(commentOf(repo, number)),
157
+ reviewComments: reviewComments.map(commentOf(repo, number)),
158
+ };
159
+ }