@reviewflow/sdk 6.25.0 → 6.27.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.
@@ -0,0 +1,154 @@
1
+ import type {
2
+ BranchPullRequestHead,
3
+ OpenPullRequest,
4
+ OpenPullRequestsResponse,
5
+ } from "../checkout/overview.ts";
6
+ import { splitOwnerRepo } from "./prQuery.ts";
7
+ import type { GhGraphqlResult, GitHubTransport } from "./transport.ts";
8
+
9
+ /** One page of GitHub's maximum: a repository with more open shows a count of the rest. */
10
+ const openPullRequestsLimit = 100;
11
+
12
+ const openPullRequestsQuery = `query OpenPullRequests($owner: String!, $name: String!, $first: Int!) {
13
+ repository(owner: $owner, name: $name) {
14
+ pullRequests(states: OPEN, first: $first, orderBy: { field: UPDATED_AT, direction: DESC }) {
15
+ totalCount
16
+ nodes {
17
+ number
18
+ title
19
+ isDraft
20
+ updatedAt
21
+ headRefName
22
+ headRepository { nameWithOwner }
23
+ author { login }
24
+ }
25
+ }
26
+ }
27
+ }`;
28
+
29
+ interface OpenPullRequestNode {
30
+ number: number;
31
+ title: string;
32
+ isDraft: boolean;
33
+ updatedAt: string;
34
+ headRefName: string;
35
+ headRepository: { nameWithOwner: string } | null;
36
+ author: { login: string } | null;
37
+ }
38
+
39
+ export interface OpenPullRequestsData {
40
+ repository: {
41
+ pullRequests: {
42
+ totalCount: number;
43
+ nodes: (OpenPullRequestNode | null)[] | null;
44
+ };
45
+ } | null;
46
+ }
47
+
48
+ export const parseOpenPullRequests = ({
49
+ data,
50
+ errors,
51
+ }: GhGraphqlResult<OpenPullRequestsData>): OpenPullRequestsResponse => {
52
+ const connection = data?.repository?.pullRequests ?? null;
53
+ if (connection === null) {
54
+ throw new Error(errors.length > 0 ? errors.join("; ") : "not found");
55
+ }
56
+ const pullRequests = (connection.nodes ?? [])
57
+ .filter((node) => node !== null)
58
+ .map((node): OpenPullRequest => ({
59
+ number: node.number,
60
+ title: node.title,
61
+ isDraft: node.isDraft,
62
+ headRefName: node.headRefName,
63
+ headOwnerRepo: node.headRepository?.nameWithOwner ?? null,
64
+ author: node.author?.login ?? null,
65
+ updatedAt: node.updatedAt,
66
+ }));
67
+ return { pullRequests, totalCount: connection.totalCount };
68
+ };
69
+
70
+ /** The repository's open pull requests, most recently updated first: one GraphQL request. */
71
+ export const fetchOpenPullRequests = async (
72
+ transport: GitHubTransport,
73
+ ownerRepo: string,
74
+ ): Promise<OpenPullRequestsResponse> => {
75
+ const split = splitOwnerRepo(ownerRepo);
76
+ if (split === null) throw new Error(`${ownerRepo} is not owner/repo`);
77
+ return parseOpenPullRequests(
78
+ await transport.graphql<OpenPullRequestsData>(openPullRequestsQuery, {
79
+ owner: split.owner,
80
+ name: split.name,
81
+ first: openPullRequestsLimit,
82
+ }),
83
+ );
84
+ };
85
+
86
+ const branchPullRequestsQuery = `query BranchPullRequests($owner: String!, $name: String!, $branch: String!) {
87
+ repository(owner: $owner, name: $name) {
88
+ pullRequests(headRefName: $branch, first: 20, orderBy: { field: UPDATED_AT, direction: DESC }) {
89
+ nodes {
90
+ number
91
+ state
92
+ headRefOid
93
+ headRepository { nameWithOwner }
94
+ }
95
+ }
96
+ }
97
+ }`;
98
+
99
+ interface BranchPullRequestNode {
100
+ number: number;
101
+ state: BranchPullRequestHead["state"];
102
+ headRefOid: string;
103
+ headRepository: { nameWithOwner: string } | null;
104
+ }
105
+
106
+ export interface BranchPullRequestsData {
107
+ repository: {
108
+ pullRequests: { nodes: (BranchPullRequestNode | null)[] | null };
109
+ } | null;
110
+ }
111
+
112
+ export const parseBranchPullRequestHeads = (
113
+ { data, errors }: GhGraphqlResult<BranchPullRequestsData>,
114
+ ownerRepo: string,
115
+ ): BranchPullRequestHead[] => {
116
+ const connection = data?.repository?.pullRequests ?? null;
117
+ if (connection === null) {
118
+ throw new Error(errors.length > 0 ? errors.join("; ") : "not found");
119
+ }
120
+ return (connection.nodes ?? [])
121
+ .filter((node) => node !== null)
122
+ .filter(
123
+ (node) =>
124
+ node.headRepository?.nameWithOwner.toLowerCase() ===
125
+ ownerRepo.toLowerCase(),
126
+ )
127
+ .map(({ number, state, headRefOid }) => ({
128
+ number,
129
+ state,
130
+ sha: headRefOid,
131
+ }));
132
+ };
133
+
134
+ /**
135
+ * The pull requests whose head is this branch of the repository, open, merged
136
+ * or closed: GitHub keeps each head commit after the branch is deleted, which
137
+ * is what proves a squash-merged branch's commits safe. One GraphQL request.
138
+ */
139
+ export const fetchBranchPullRequestHeads = async (
140
+ transport: GitHubTransport,
141
+ ownerRepo: string,
142
+ branch: string,
143
+ ): Promise<BranchPullRequestHead[]> => {
144
+ const split = splitOwnerRepo(ownerRepo);
145
+ if (split === null) throw new Error(`${ownerRepo} is not owner/repo`);
146
+ return parseBranchPullRequestHeads(
147
+ await transport.graphql<BranchPullRequestsData>(branchPullRequestsQuery, {
148
+ owner: split.owner,
149
+ name: split.name,
150
+ branch,
151
+ }),
152
+ ownerRepo,
153
+ );
154
+ };
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@
2
2
  * Browser-safe surface: nothing here may import node. Reading a diff from a
3
3
  * local checkout is `@reviewflow/sdk/git`, which is node only.
4
4
  */
5
+ export * from "./checkout/overview.ts";
6
+ export * from "./checkout/sync.ts";
7
+ export * from "./checkout/worktreeRemoval.ts";
5
8
  export * from "./diff/diffPaths.ts";
6
9
  export type * from "./diff/impacts.ts";
7
10
  export { isAttachablePath, subjectsOf } from "./diff/readingOrder.ts";
@@ -11,6 +14,7 @@ export * from "./github/applySuggestions.ts";
11
14
  export * from "./github/editPullRequest.ts";
12
15
  export * from "./github/fetchCompareDiff.ts";
13
16
  export * from "./github/lineDiff.ts";
17
+ export * from "./github/openPullRequests.ts";
14
18
  export * from "./github/prQuery.ts";
15
19
  export * from "./github/pullRequestDetail.ts";
16
20
  export * from "./github/pullRequestHead.ts";
@@ -35,6 +39,10 @@ export * from "./reviewflow/board.ts";
35
39
  export * from "./reviewflow/boardFacts.ts";
36
40
  export * from "./reviewflow/follow.ts";
37
41
  export type * from "./reviewflow/status.ts";
38
- export { defaultRepoPath } from "./config/defaultRepoPath.ts";
42
+ export { defaultRepoPaths } from "./config/defaultRepoPaths.ts";
43
+ export {
44
+ readRepoPathsSetting,
45
+ writeRepoPathsSetting,
46
+ } from "./config/repoPathsSetting.ts";
39
47
  export { insertCheckoutEntry } from "./config/insertCheckoutEntry.ts";
40
48
  export type { InsertedCheckoutEntry } from "./config/insertCheckoutEntry.ts";
@@ -1,2 +0,0 @@
1
- /** Where the app searches checkouts when `config.json` sets no `defaultRepoPath`. */
2
- export const defaultRepoPath = "~/dev/";