wuffle 0.73.3 → 0.75.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 (29) hide show
  1. package/lib/apps/auth-routes/AuthRoutes.js +16 -14
  2. package/lib/apps/{automatic-dev-flow.js → automatic-dev-flow/AutomaticDevFlow.js} +12 -13
  3. package/lib/apps/automatic-dev-flow/index.js +6 -0
  4. package/lib/apps/background-sync/BackgroundSync.js +41 -83
  5. package/lib/apps/background-sync/BackgroundSyncBackend.js +140 -0
  6. package/lib/apps/background-sync/index.js +3 -1
  7. package/lib/apps/board-api-routes/board-api-routes.js +20 -14
  8. package/lib/apps/dump-store/s3/S3.js +12 -0
  9. package/lib/apps/{events-sync.js → events-sync/EventsSync.js} +7 -5
  10. package/lib/apps/events-sync/index.js +6 -0
  11. package/lib/apps/github-app/GithubApp.js +69 -6
  12. package/lib/apps/github-checks/GithubChecks.js +2 -0
  13. package/lib/apps/github-comments/GithubComments.js +9 -78
  14. package/lib/apps/github-comments/GithubCommentsBackend.js +132 -0
  15. package/lib/apps/github-comments/index.js +3 -1
  16. package/lib/apps/github-issues/GithubIssues.js +161 -9
  17. package/lib/apps/github-reviews/GithubReviews.js +2 -0
  18. package/lib/apps/github-statuses/GithubStatuses.js +2 -0
  19. package/lib/apps/issue-filter/IssueFilter.js +5 -3
  20. package/lib/apps/log-events.js +4 -4
  21. package/lib/apps/{reindex-store.js → reindex-store/ReindexStore.js} +6 -4
  22. package/lib/apps/reindex-store/index.js +6 -0
  23. package/lib/apps/search/Search.js +9 -5
  24. package/lib/apps/webhook-events/WebhookEvents.js +12 -5
  25. package/lib/events.js +2 -2
  26. package/lib/index.js +13 -5
  27. package/lib/probot/CustomProbot.js +2 -1
  28. package/lib/util/search.js +1 -1
  29. package/package.json +6 -5
@@ -28,6 +28,7 @@ const RequiredEvents = [
28
28
 
29
29
  /**
30
30
  * @typedef {import('./types.js').Installation} Installation
31
+ * @typedef {import('../../types.js').Octokit} Octokit
31
32
  */
32
33
 
33
34
  /**
@@ -53,8 +54,19 @@ export default function GithubApp(config, app, logger, injector) {
53
54
 
54
55
  // cached data //////////////////
55
56
 
57
+ /**
58
+ * @type {Promise<Installation[]> | null}
59
+ */
56
60
  let installations = null;
61
+
62
+ /**
63
+ * @type {Promise<Record<string, Installation>> | null}
64
+ */
57
65
  let installationsByLogin = null;
66
+
67
+ /**
68
+ * @type {Promise<Record<string, Installation>> | null}
69
+ */
58
70
  let installationsById = null;
59
71
 
60
72
  // reactivity ////////////////////
@@ -71,12 +83,17 @@ export default function GithubApp(config, app, logger, injector) {
71
83
  // functionality /////////////////
72
84
 
73
85
  /**
74
- * @return {Promise<import('../../types.js').Octokit>}
86
+ * @return {Promise<Octokit>}
75
87
  */
76
88
  function getAppScopedClient() {
77
89
  return app.auth();
78
90
  }
79
91
 
92
+ /**
93
+ * Return installations
94
+ *
95
+ * @return {Promise<Installation[]>}
96
+ */
80
97
  function getInstallations() {
81
98
  installations = installations || fetchInstallations().then(installations => {
82
99
 
@@ -90,6 +107,11 @@ export default function GithubApp(config, app, logger, injector) {
90
107
  return installations;
91
108
  }
92
109
 
110
+ /**
111
+ * Return installations by ID
112
+ *
113
+ * @return {Promise<Record<string, Installation>>}
114
+ */
93
115
  function getInstallationsById() {
94
116
 
95
117
  installationsById = installationsById || getInstallations().then(
@@ -106,9 +128,9 @@ export default function GithubApp(config, app, logger, injector) {
106
128
  /**
107
129
  * Get an installation for the given id.
108
130
  *
109
- * @param {string} id
131
+ * @param {string|number} id
110
132
  *
111
- * @return {Promise<Installation?>}
133
+ * @return {Promise<Installation | undefined>}
112
134
  */
113
135
  function getInstallationById(id) {
114
136
  return getInstallationsById().then(byId => {
@@ -119,7 +141,7 @@ export default function GithubApp(config, app, logger, injector) {
119
141
  /**
120
142
  * Return map of installations, grouped by org / login.
121
143
  *
122
- * @return {Promise<Object<String, Installation>>}
144
+ * @return {Promise<Record<string, Installation>>}
123
145
  */
124
146
  function getInstallationsByLogin() {
125
147
 
@@ -155,6 +177,13 @@ export default function GithubApp(config, app, logger, injector) {
155
177
  });
156
178
  }
157
179
 
180
+ /**
181
+ * Return true if installation is enabled.
182
+ *
183
+ * @param {Installation} installation
184
+ *
185
+ * @return {Promise<boolean>}
186
+ */
158
187
  function isInstallationEnabled(installation) {
159
188
 
160
189
  const {
@@ -240,10 +269,10 @@ export default function GithubApp(config, app, logger, injector) {
240
269
  /**
241
270
  * Fetch active installations.
242
271
  *
243
- * @return {Promise<Array<Installation>>} installations
272
+ * @return {Promise<Installation[]>} installations
244
273
  */
245
274
  function fetchInstallations() {
246
- return /** @type Promise<Array<Installation>> */ (getAppScopedClient().then(
275
+ return /** @type Promise<Installation[]> */ (getAppScopedClient().then(
247
276
  octokit => octokit.paginate(
248
277
  octokit.rest.apps.listInstallations,
249
278
  { per_page: 100 }
@@ -253,13 +282,47 @@ export default function GithubApp(config, app, logger, injector) {
253
282
 
254
283
  // api ///////////////////
255
284
 
285
+ /**
286
+ * Return an application-scoped octokit client.
287
+ *
288
+ * @return {Promise<Octokit>}
289
+ */
256
290
  this.getAppScopedClient = getAppScopedClient;
257
291
 
292
+ /**
293
+ * Return true if installation is enabled.
294
+ *
295
+ * @param {Installation} installation
296
+ *
297
+ * @return {Promise<boolean>}
298
+ */
258
299
  this.isInstallationEnabled = isInstallationEnabled;
259
300
 
301
+ /**
302
+ * Get an installation for the given login.
303
+ *
304
+ * This method throws if an installation for the given login does not exist.
305
+ *
306
+ * @param {string} login
307
+ *
308
+ * @return {Promise<Installation>}
309
+ */
260
310
  this.getInstallationByLogin = getInstallationByLogin;
311
+
312
+ /**
313
+ * Get an installation for the given id.
314
+ *
315
+ * @param {string|number} id
316
+ *
317
+ * @return {Promise<Installation | undefined>}
318
+ */
261
319
  this.getInstallationById = getInstallationById;
262
320
 
321
+ /**
322
+ * Return installations
323
+ *
324
+ * @return {Promise<Installation[]>}
325
+ */
263
326
  this.getInstallations = getInstallations;
264
327
 
265
328
  }
@@ -3,6 +3,8 @@ import { repoAndOwner, Cache } from '../../util/index.js';
3
3
  /**
4
4
  * This component updates the stored issues based on GitHub events.
5
5
  *
6
+ * It also hooks into `BackgroundSync` to fetch checks for a PR.
7
+ *
6
8
  * @constructor
7
9
  *
8
10
  * @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
@@ -1,21 +1,21 @@
1
- import { repoAndOwner } from '../../util/index.js';
2
1
  import { filterUser, filterIssue } from '../../filters.js';
3
- import gql from 'fake-tag';
4
2
 
5
3
 
6
4
  /**
7
5
  * This component updates the stored issues based on GitHub events.
8
6
  *
7
+ * It also hooks into `BackgroundSync` to fetch comments for issues/PRs.
8
+ *
9
9
  * @constructor
10
10
  *
11
11
  * @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
12
12
  * @param {import('../../events.js').default} events
13
- * @param {import('../github-client/GithubClient.js').default} githubClient
14
13
  * @param {import('../../store.js').default} store
15
14
  * @param {import('../issue-filter/IssueFilter.js').default} issueFilter
16
15
  * @param {import('../../types.js').Logger } logger
16
+ * @param {import('./GithubCommentsBackend.js').default} githubCommentsBackend
17
17
  */
18
- export default function GithubComments(webhookEvents, events, githubClient, store, issueFilter, logger) {
18
+ export default function GithubComments(webhookEvents, events, store, issueFilter, logger, githubCommentsBackend) {
19
19
 
20
20
  const log = logger.child({
21
21
  name: 'wuffle:github-comments'
@@ -32,81 +32,10 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
32
32
  } = event;
33
33
 
34
34
  const {
35
- id,
36
- number
35
+ id
37
36
  } = issue;
38
37
 
39
- const {
40
- repo,
41
- owner
42
- } = repoAndOwner(issue);
43
-
44
- const github = await githubClient.getOrgScoped(owner);
45
-
46
- const result = await github.graphql(gql`
47
-
48
- fragment CommentInfo on IssueComment {
49
- id: databaseId
50
- node_id: id
51
- body: bodyText
52
- created_at: publishedAt
53
- authorAssociation,
54
- html_url: url,
55
- user: author {
56
- login
57
- avatar_url: avatarUrl,
58
- html_url: url
59
- }
60
- }
61
-
62
- query FetchComments(
63
- $repo: String!,
64
- $owner: String!,
65
- $issue_number: Int!,
66
- $after: String
67
- ) {
68
- repository(name: $repo, owner: $owner) {
69
- issueOrPullRequest(number: $issue_number) {
70
- ...on Issue {
71
- comments(first: 100, after: $after) {
72
- edges {
73
- node {
74
- ...CommentInfo
75
- }
76
- }
77
- pageInfo {
78
- endCursor
79
- hasNextPage
80
- }
81
- totalCount
82
- }
83
- }
84
- ...on PullRequest {
85
- comments(first: 100, after: $after) {
86
- edges {
87
- node {
88
- ...CommentInfo
89
- }
90
- }
91
- pageInfo {
92
- endCursor
93
- hasNextPage
94
- }
95
- totalCount
96
- }
97
- }
98
- }
99
- }
100
- }`,
101
- {
102
- owner,
103
- repo,
104
- issue_number: number
105
- });
106
-
107
- const comments = (
108
- result.repository.issueOrPullRequest.comments.edges.map(e => e.node)
109
- );
38
+ const comments = await githubCommentsBackend.getIssueComments(issue);
110
39
 
111
40
  await store.queueUpdate({
112
41
  id,
@@ -115,7 +44,9 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
115
44
  });
116
45
 
117
46
  webhookEvents.on([
118
- 'issue_comment'
47
+ 'issue_comment.created',
48
+ 'issue_comment.edited',
49
+ 'issue_comment.deleted'
119
50
  ], ifEnabled(async ({ payload }) => {
120
51
  const {
121
52
  action,
@@ -0,0 +1,132 @@
1
+ import { repoAndOwner } from '../../util/index.js';
2
+
3
+ import gql from 'fake-tag';
4
+
5
+ /**
6
+ * @typedef { {
7
+ * id: number,
8
+ * node_id: string,
9
+ * body: string,
10
+ * created_at: string,
11
+ * authorAssociation: string,
12
+ * html_url: string,
13
+ * user: {
14
+ * id: number,
15
+ * node_id: string,
16
+ * login: string,
17
+ * avatar_url: string,
18
+ * html_url: string,
19
+ * type: string
20
+ * }
21
+ * } } GithubComment
22
+ *
23
+ * @typedef { import('../../util/meta.js').Issue } Issue
24
+ */
25
+
26
+
27
+ /**
28
+ * This component fetches GitHub comments data required for background synchronization.
29
+ *
30
+ * @constructor
31
+ *
32
+ * @param {import('../github-client/GithubClient.js').default} githubClient
33
+ */
34
+ export default function GithubCommentsBackend(githubClient) {
35
+
36
+ /**
37
+ * Return comments for given issue.
38
+ *
39
+ * @param {Issue} issue
40
+ *
41
+ * @return {Promise<GithubComment[]>}
42
+ */
43
+ this.getIssueComments = async function(issue) {
44
+
45
+ const {
46
+ number
47
+ } = issue;
48
+
49
+ const {
50
+ repo,
51
+ owner
52
+ } = repoAndOwner(issue);
53
+
54
+ const github = await githubClient.getOrgScoped(owner);
55
+
56
+ const result = await github.graphql(gql`
57
+
58
+ fragment CommentInfo on IssueComment {
59
+ id: databaseId
60
+ node_id: id
61
+ body: bodyText
62
+ created_at: publishedAt
63
+ authorAssociation,
64
+ html_url: url,
65
+ user: author {
66
+ login
67
+ avatar_url: avatarUrl,
68
+ html_url: url,
69
+ type: __typename
70
+ ... on User {
71
+ id: databaseId
72
+ node_id: id
73
+ }
74
+ ... on Bot {
75
+ id: databaseId
76
+ node_id: id
77
+ }
78
+ }
79
+ }
80
+
81
+ query FetchComments(
82
+ $repo: String!,
83
+ $owner: String!,
84
+ $issue_number: Int!,
85
+ $after: String
86
+ ) {
87
+ repository(name: $repo, owner: $owner) {
88
+ issueOrPullRequest(number: $issue_number) {
89
+ ...on Issue {
90
+ comments(first: 100, after: $after) {
91
+ edges {
92
+ node {
93
+ ...CommentInfo
94
+ }
95
+ }
96
+ pageInfo {
97
+ endCursor
98
+ hasNextPage
99
+ }
100
+ totalCount
101
+ }
102
+ }
103
+ ...on PullRequest {
104
+ comments(first: 100, after: $after) {
105
+ edges {
106
+ node {
107
+ ...CommentInfo
108
+ }
109
+ }
110
+ pageInfo {
111
+ endCursor
112
+ hasNextPage
113
+ }
114
+ totalCount
115
+ }
116
+ }
117
+ }
118
+ }
119
+ }`,
120
+ {
121
+ owner,
122
+ repo,
123
+ issue_number: number
124
+ });
125
+
126
+ const comments = /** @type { GithubComment[] } */ (
127
+ result.repository.issueOrPullRequest.comments.edges.map(e => e.node)
128
+ );
129
+
130
+ return comments;
131
+ };
132
+ }
@@ -1,6 +1,8 @@
1
1
  import GithubComments from './GithubComments.js';
2
+ import GithubCommentsBackend from './GithubCommentsBackend.js';
2
3
 
3
4
  export default {
4
5
  __init__: [ 'githubComments' ],
5
- githubComments: [ 'type', GithubComments ]
6
+ githubComments: [ 'type', GithubComments ],
7
+ githubCommentsBackend: [ 'type', GithubCommentsBackend ]
6
8
  };
@@ -4,8 +4,17 @@ const {
4
4
  CLOSES
5
5
  } = linkTypes;
6
6
 
7
+ /**
8
+ * @typedef { import('probot').Context } ProbotContext
9
+ *
10
+ * @typedef { import('../../columns.js').ColumnDefinition } ColumnDefinition
11
+ */
7
12
 
8
13
  /**
14
+ * Moves GitHub issues between board columns by applying the
15
+ * corresponding state (open/closed), label, and assignee changes
16
+ * via the GitHub API.
17
+ *
9
18
  * @constructor
10
19
  *
11
20
  * @param {any} config
@@ -18,6 +27,16 @@ export default function GithubIssues(config, logger, columns) {
18
27
  name: 'wuffle:github-issues'
19
28
  });
20
29
 
30
+ /**
31
+ * Return the assignee update required to add `newAssignee` to the
32
+ * issue, or an empty object if the assignee is already set or not
33
+ * provided.
34
+ *
35
+ * @param {Object} issue
36
+ * @param {string|null} newAssignee
37
+ *
38
+ * @return {{ assignees?: string[] }}
39
+ */
21
40
  function getAssigneeUpdate(issue, newAssignee) {
22
41
 
23
42
  if (!newAssignee) {
@@ -39,6 +58,15 @@ export default function GithubIssues(config, logger, columns) {
39
58
 
40
59
  }
41
60
 
61
+ /**
62
+ * Return the state update (`open` / `closed`) required to reflect
63
+ * the new column, or an empty object if no change is needed.
64
+ *
65
+ * @param {Object} issue
66
+ * @param {ColumnDefinition} newColumn
67
+ *
68
+ * @return {{ state?: 'open'|'closed' }}
69
+ */
42
70
  function getStateUpdate(issue, newColumn) {
43
71
 
44
72
  let update = {};
@@ -55,16 +83,29 @@ export default function GithubIssues(config, logger, columns) {
55
83
  return update;
56
84
  }
57
85
 
86
+ /**
87
+ * Return the label changes required to reflect the new column:
88
+ * the column label to add and any other column labels to remove.
89
+ *
90
+ * @param {Object} issue
91
+ * @param {ColumnDefinition} newColumn
92
+ *
93
+ * @return {{ addLabels: string[], removeLabels: string[] }}
94
+ */
58
95
  function getLabelUpdate(issue, newColumn) {
59
96
 
60
- const issueLabels = issue.labels.map(l => l.name);
97
+ const issueLabels = /** @type { string[] } */ (
98
+ issue.labels.map(l => l.name)
99
+ );
61
100
 
62
101
  const newLabel = newColumn.label;
63
102
 
64
103
  const addLabels = (!newLabel || issueLabels.includes(newLabel)) ? [] : [ newLabel ];
65
104
 
66
- const removeLabels = columns.getAll().map(c => c.label).filter(
67
- label => label && label !== newLabel && issueLabels.includes(label)
105
+ const removeLabels = /** @type { string[] } */ (
106
+ columns.getAll().map(c => c.label).filter(
107
+ label => label && label !== newLabel && issueLabels.includes(label)
108
+ )
68
109
  );
69
110
 
70
111
  return {
@@ -73,6 +114,10 @@ export default function GithubIssues(config, logger, columns) {
73
114
  };
74
115
  }
75
116
 
117
+ /**
118
+ * @param {ProbotContext} context
119
+ * @param {number} issue_number
120
+ */
76
121
  function findIssue(context, issue_number) {
77
122
 
78
123
  const params = context.repo({ issue_number });
@@ -88,12 +133,43 @@ export default function GithubIssues(config, logger, columns) {
88
133
  });
89
134
  }
90
135
 
91
- function findAndMoveIssue(context, number, newColumn, newAssignee, test = (issue) => true) {
92
- return findIssue(context, number)
93
- .then((issue) => issue && test(issue) && moveIssue(context, issue, newColumn, newAssignee));
136
+ /**
137
+ * Fetch an issue by number and move it to a new column if the
138
+ * optional predicate passes. Resolves to `false` if the issue is
139
+ * not found or the predicate rejects it.
140
+ *
141
+ * @param {ProbotContext} context
142
+ * @param {number} number
143
+ * @param {ColumnDefinition} newColumn
144
+ * @param {string|null} newAssignee
145
+ * @param {(issue: Object) => boolean} [test]
146
+ *
147
+ * @return {Promise<void|false>}
148
+ */
149
+ async function findAndMoveIssue(context, number, newColumn, newAssignee, test = (issue) => true) {
150
+ const issue = await findIssue(context, number);
151
+
152
+ if (!issue || !test(issue)) {
153
+ return false;
154
+ }
155
+
156
+ return moveIssue(context, issue, newColumn, newAssignee);
94
157
  }
95
158
 
96
- async function moveReferencedIssues(context, issue, newColumn, newAssignee) {
159
+ /**
160
+ * Move all issues referenced via `closes` links in the given issue
161
+ * or pull request body to a new column.
162
+ *
163
+ * Only issues within the same repository are moved.
164
+ *
165
+ * @param {ProbotContext} context
166
+ * @param {Object} issue
167
+ * @param {ColumnDefinition} newColumn
168
+ * @param {string|null} [newAssignee=null]
169
+ *
170
+ * @return {Promise<(void|false)[]>}
171
+ */
172
+ async function moveReferencedIssues(context, issue, newColumn, newAssignee = null) {
97
173
 
98
174
  // TODO(nikku): do that lazily, i.e. react to PR label changes?
99
175
  // would slower the movement but support manual moving-issue with PR
@@ -128,7 +204,18 @@ export default function GithubIssues(config, logger, columns) {
128
204
  }));
129
205
  }
130
206
 
131
- function moveIssue(context, issue, newColumn, newAssignee) {
207
+ /**
208
+ * Move an issue to a new column, applying state, label, and
209
+ * assignee updates via the GitHub API.
210
+ *
211
+ * @param {ProbotContext} context
212
+ * @param {Object} issue
213
+ * @param {ColumnDefinition} newColumn
214
+ * @param {string|null} [newAssignee=null]
215
+ *
216
+ * @return {Promise<void>}
217
+ */
218
+ function moveIssue(context, issue, newColumn, newAssignee = null) {
132
219
 
133
220
  const {
134
221
  number: issue_number
@@ -199,22 +286,87 @@ export default function GithubIssues(config, logger, columns) {
199
286
  );
200
287
  }
201
288
 
202
- return Promise.all(invocations);
289
+ return Promise.all(invocations).then(() => {});
203
290
  }
204
291
 
205
292
 
206
293
  // api /////////////////////////////
207
294
 
295
+ /**
296
+ * Move an issue to a new column, applying state, label, and
297
+ * assignee updates via the GitHub API.
298
+ *
299
+ * @param {ProbotContext} context
300
+ * @param {Object} issue
301
+ * @param {ColumnDefinition} newColumn
302
+ * @param {string} [newAssignee]
303
+ *
304
+ * @return {Promise<void>}
305
+ */
208
306
  this.moveIssue = moveIssue;
209
307
 
308
+ /**
309
+ * Move all issues referenced via `closes` links in the given issue
310
+ * or pull request body to a new column.
311
+ *
312
+ * Only issues within the same repository are moved.
313
+ *
314
+ * @param {ProbotContext} context
315
+ * @param {Object} issue
316
+ * @param {ColumnDefinition} newColumn
317
+ * @param {string|null} [newAssignee=null]
318
+ *
319
+ * @return {Promise<(void|false)[]>}
320
+ */
210
321
  this.moveReferencedIssues = moveReferencedIssues;
211
322
 
323
+ /**
324
+ * Return the state update (`open` / `closed`) required to reflect
325
+ * the new column, or an empty object if no change is needed.
326
+ *
327
+ * @param {Object} issue
328
+ * @param {ColumnDefinition} newColumn
329
+ *
330
+ * @return {{ state?: 'open'|'closed' }}
331
+ */
212
332
  this.getStateUpdate = getStateUpdate;
213
333
 
334
+ /**
335
+ * Return the assignee update required to add `newAssignee` to the
336
+ * issue, or an empty object if the assignee is already set or not
337
+ * provided.
338
+ *
339
+ * @param {Object} issue
340
+ * @param {string} [newAssignee]
341
+ *
342
+ * @return {{ assignees?: string[] }}
343
+ */
214
344
  this.getAssigneeUpdate = getAssigneeUpdate;
215
345
 
346
+ /**
347
+ * Return the label changes required to reflect the new column:
348
+ * the column label to add and any other column labels to remove.
349
+ *
350
+ * @param {Object} issue
351
+ * @param {ColumnDefinition} newColumn
352
+ *
353
+ * @return {{ addLabels: string[], removeLabels: string[] }}
354
+ */
216
355
  this.getLabelUpdate = getLabelUpdate;
217
356
 
357
+ /**
358
+ * Fetch an issue by number and move it to a new column if the
359
+ * optional predicate passes. Resolves to `false` if the issue is
360
+ * not found or the predicate rejects it.
361
+ *
362
+ * @param {ProbotContext} context
363
+ * @param {number} number
364
+ * @param {ColumnDefinition} newColumn
365
+ * @param {string|null} newAssignee
366
+ * @param {(issue: Object) => boolean} [test]
367
+ *
368
+ * @return {Promise<void|false>}
369
+ */
218
370
  this.findAndMoveIssue = findAndMoveIssue;
219
371
 
220
372
  }
@@ -5,6 +5,8 @@ import { filterUser, filterPull } from '../../filters.js';
5
5
  /**
6
6
  * This component updates the stored issues based on GitHub events.
7
7
  *
8
+ * It also hooks into `BackgroundSync` to fetch reviews for a PR.
9
+ *
8
10
  * @constructor
9
11
  *
10
12
  * @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
@@ -5,6 +5,8 @@ import { filterPull } from '../../filters.js';
5
5
  /**
6
6
  * This component updates synchronizes GitHub statuses with pull requests.
7
7
  *
8
+ * It also hooks into `BackgroundSync` to fetch statuses for a PR.
9
+ *
8
10
  * @constructor
9
11
  *
10
12
  * @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents