wuffle 0.74.0 → 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.
- package/lib/apps/auth-routes/AuthRoutes.js +16 -14
- package/lib/apps/automatic-dev-flow/AutomaticDevFlow.js +7 -8
- package/lib/apps/background-sync/BackgroundSync.js +30 -5
- package/lib/apps/board-api-routes/board-api-routes.js +20 -14
- package/lib/apps/dump-store/s3/S3.js +12 -0
- package/lib/apps/github-app/GithubApp.js +1 -1
- package/lib/apps/github-checks/GithubChecks.js +2 -0
- package/lib/apps/github-comments/GithubComments.js +9 -78
- package/lib/apps/github-comments/GithubCommentsBackend.js +132 -0
- package/lib/apps/github-comments/index.js +3 -1
- package/lib/apps/github-issues/GithubIssues.js +161 -9
- package/lib/apps/github-reviews/GithubReviews.js +2 -0
- package/lib/apps/github-statuses/GithubStatuses.js +2 -0
- package/lib/apps/issue-filter/IssueFilter.js +5 -3
- package/lib/apps/search/Search.js +9 -5
- package/lib/apps/webhook-events/WebhookEvents.js +12 -5
- package/lib/events.js +2 -2
- package/lib/probot/CustomProbot.js +2 -1
- package/lib/util/search.js +1 -1
- package/package.json +3 -3
|
@@ -60,7 +60,7 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
const params = new URLSearchParams();
|
|
63
|
-
params.append('client_id', process.env.GITHUB_CLIENT_ID);
|
|
63
|
+
params.append('client_id', /** @type {string} */ (process.env.GITHUB_CLIENT_ID));
|
|
64
64
|
params.append('state', state);
|
|
65
65
|
params.append('redirect_uri', appUrl('/wuffle/login/callback'));
|
|
66
66
|
|
|
@@ -81,10 +81,9 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
81
81
|
|
|
82
82
|
log.debug({ session_id }, 'logging out');
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
|
|
84
|
+
req.session.destroy(function(err) {
|
|
85
|
+
res.redirect(redirectTo);
|
|
86
|
+
});
|
|
88
87
|
});
|
|
89
88
|
|
|
90
89
|
|
|
@@ -128,8 +127,8 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
128
127
|
const params = new URLSearchParams();
|
|
129
128
|
params.append('code', /** @type {string} */ (code));
|
|
130
129
|
params.append('state', /** @type {string} */ (state));
|
|
131
|
-
params.append('client_id', process.env.GITHUB_CLIENT_ID);
|
|
132
|
-
params.append('client_secret', process.env.GITHUB_CLIENT_SECRET);
|
|
130
|
+
params.append('client_id', /** @type {string} */ (process.env.GITHUB_CLIENT_ID));
|
|
131
|
+
params.append('client_secret', /** @type {string} */ (process.env.GITHUB_CLIENT_SECRET));
|
|
133
132
|
params.append('redirect_uri', appUrl('/wuffle/login/callback'));
|
|
134
133
|
|
|
135
134
|
const {
|
|
@@ -185,7 +184,9 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
185
184
|
} = session;
|
|
186
185
|
|
|
187
186
|
if (!githubUser) {
|
|
188
|
-
|
|
187
|
+
res.type('json').json(null);
|
|
188
|
+
|
|
189
|
+
return;
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
const {
|
|
@@ -216,17 +217,18 @@ export default function AuthRoutes(logger, router, securityContext) {
|
|
|
216
217
|
}, 'failed to check GitHub authentication');
|
|
217
218
|
|
|
218
219
|
// access is not granted anymore, clear current session
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
})
|
|
220
|
+
req.session.destroy(function(err) {
|
|
221
|
+
res.type('json').json(null);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return;
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
|
|
225
|
-
|
|
228
|
+
res.type('json').json({
|
|
226
229
|
login,
|
|
227
230
|
avatar_url
|
|
228
|
-
})
|
|
229
|
-
|
|
231
|
+
});
|
|
230
232
|
});
|
|
231
233
|
|
|
232
234
|
|
|
@@ -31,14 +31,13 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
31
31
|
'pull_request.closed'
|
|
32
32
|
], ifEnabled(async (context) => {
|
|
33
33
|
|
|
34
|
-
const {
|
|
35
|
-
pull_request,
|
|
36
|
-
issue
|
|
37
|
-
} = context.payload;
|
|
38
|
-
|
|
39
34
|
const column = columns.getByState(DONE);
|
|
40
35
|
|
|
41
|
-
|
|
36
|
+
const issueOrPull = 'issue' in context.payload
|
|
37
|
+
? context.payload.issue
|
|
38
|
+
: context.payload.pull_request;
|
|
39
|
+
|
|
40
|
+
await githubIssues.moveIssue(context, issueOrPull, column);
|
|
42
41
|
}));
|
|
43
42
|
|
|
44
43
|
webhookEvents.on('pull_request.converted_to_draft', ifEnabled(async (context) => {
|
|
@@ -107,7 +106,7 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
107
106
|
const newAssignee = (
|
|
108
107
|
process.env.AUTO_ASSIGN_PULLS && !external &&
|
|
109
108
|
author && author.type === 'User' && author.login
|
|
110
|
-
);
|
|
109
|
+
) || null;
|
|
111
110
|
|
|
112
111
|
await Promise.all([
|
|
113
112
|
githubIssues.moveIssue(context, pull_request, column, newAssignee),
|
|
@@ -172,7 +171,7 @@ export default function(webhookEvents, githubIssues, columns, issueFilter, logge
|
|
|
172
171
|
return;
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
const issue_number = match[1];
|
|
174
|
+
const issue_number = parseInt(match[1], 10);
|
|
176
175
|
|
|
177
176
|
const column = columns.getByState(IN_PROGRESS);
|
|
178
177
|
|
|
@@ -9,6 +9,17 @@ function isInternalError(error) {
|
|
|
9
9
|
/**
|
|
10
10
|
* This component performs a periodic background sync of a project.
|
|
11
11
|
*
|
|
12
|
+
* Unless disabled via `process.env.DISABLE_BACKGROUND_SYNC` it will
|
|
13
|
+
* register a recurring check.
|
|
14
|
+
*
|
|
15
|
+
* Background check performs various optimizations to ensure only relevant
|
|
16
|
+
* data is stored on the board:
|
|
17
|
+
*
|
|
18
|
+
* * Closed issues/PRs on the board will be thrashed
|
|
19
|
+
* * Closed issues/PRs wiil not be synchronized from GitHub
|
|
20
|
+
* * Open but stale issues/PRs will not be synchronized to the board
|
|
21
|
+
* * Open issue/PR details will only be synchronized for recent issues
|
|
22
|
+
*
|
|
12
23
|
* @constructor
|
|
13
24
|
*
|
|
14
25
|
* @param {Object} config
|
|
@@ -21,25 +32,25 @@ export default function BackgroundSync(config, logger, store, events, background
|
|
|
21
32
|
|
|
22
33
|
// 30 days
|
|
23
34
|
const syncClosedLookback = (
|
|
24
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_LOOKBACK, 10) ||
|
|
35
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_LOOKBACK || '', 10) ||
|
|
25
36
|
1000 * 60 * 60 * 24 * 30
|
|
26
37
|
);
|
|
27
38
|
|
|
28
39
|
// 4 hours
|
|
29
40
|
const syncClosedDetailsLookback = (
|
|
30
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_DETAILS_LOOKBACK, 10) ||
|
|
41
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_CLOSED_DETAILS_LOOKBACK || '', 10) ||
|
|
31
42
|
1000 * 60 * 60 * 4
|
|
32
43
|
);
|
|
33
44
|
|
|
34
45
|
// 1 day
|
|
35
46
|
const syncOpenDetailsLookback = (
|
|
36
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_OPEN_DETAILS_LOOKBACK, 10) ||
|
|
47
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_OPEN_DETAILS_LOOKBACK || '', 10) ||
|
|
37
48
|
1000 * 60 * 60 * 24
|
|
38
49
|
);
|
|
39
50
|
|
|
40
51
|
// 60 days
|
|
41
52
|
const removeClosedLookback = (
|
|
42
|
-
parseInt(process.env.BACKGROUND_SYNC_REMOVE_CLOSED_LOOKBACK, 10) ||
|
|
53
|
+
parseInt(process.env.BACKGROUND_SYNC_REMOVE_CLOSED_LOOKBACK || '', 10) ||
|
|
43
54
|
1000 * 60 * 60 * 24 * 60
|
|
44
55
|
);
|
|
45
56
|
|
|
@@ -393,6 +404,13 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
393
404
|
return Promise.all(jobs);
|
|
394
405
|
}
|
|
395
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Trigger background synchronization for all connected repositories.
|
|
409
|
+
*
|
|
410
|
+
* This ensures that data out-of-sync with the board is fetched from remote.
|
|
411
|
+
*
|
|
412
|
+
* @return {Promise<void>}
|
|
413
|
+
*/
|
|
396
414
|
async function backgroundSync() {
|
|
397
415
|
|
|
398
416
|
log.info('start');
|
|
@@ -410,7 +428,7 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
410
428
|
}
|
|
411
429
|
|
|
412
430
|
const syncInterval = (
|
|
413
|
-
parseInt(process.env.BACKGROUND_SYNC_SYNC_INTERVAL, 10) || (
|
|
431
|
+
parseInt(process.env.BACKGROUND_SYNC_SYNC_INTERVAL || '', 10) || (
|
|
414
432
|
process.env.NODE_ENV !== 'production'
|
|
415
433
|
|
|
416
434
|
// five minutes
|
|
@@ -442,6 +460,13 @@ We automatically synchronize all repositories you granted us access to via the G
|
|
|
442
460
|
|
|
443
461
|
// api ///////////////////
|
|
444
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Trigger background synchronization for all connected repositories.
|
|
465
|
+
*
|
|
466
|
+
* This ensures that data out-of-sync with the board is fetched from remote.
|
|
467
|
+
*
|
|
468
|
+
* @return {Promise<void>}
|
|
469
|
+
*/
|
|
445
470
|
this.backgroundSync = backgroundSync;
|
|
446
471
|
|
|
447
472
|
|
|
@@ -172,7 +172,7 @@ export default async function BoardApiRoutes(
|
|
|
172
172
|
const items = store.getBoard();
|
|
173
173
|
const cursor = store.getUpdateCursor();
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
filterBoardItems(req, items).then(filteredItems => {
|
|
176
176
|
|
|
177
177
|
return res.type('json').json({
|
|
178
178
|
items: filteredItems,
|
|
@@ -196,7 +196,7 @@ export default async function BoardApiRoutes(
|
|
|
196
196
|
name
|
|
197
197
|
} = config;
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
res.type('json').json({
|
|
200
200
|
columns: columns.map(c => {
|
|
201
201
|
const { name, collapsed } = c;
|
|
202
202
|
|
|
@@ -236,7 +236,9 @@ export default async function BoardApiRoutes(
|
|
|
236
236
|
const user = authRoutes.getGitHubUser(req);
|
|
237
237
|
|
|
238
238
|
if (!user) {
|
|
239
|
-
|
|
239
|
+
res.status(401).json({});
|
|
240
|
+
|
|
241
|
+
return;
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
const body = JSON.parse(req.body);
|
|
@@ -251,13 +253,17 @@ export default async function BoardApiRoutes(
|
|
|
251
253
|
const issue = await store.getIssueById(id);
|
|
252
254
|
|
|
253
255
|
if (!issue) {
|
|
254
|
-
|
|
256
|
+
res.status(404).json({});
|
|
257
|
+
|
|
258
|
+
return;
|
|
255
259
|
}
|
|
256
260
|
|
|
257
261
|
const column = columns.getByName(columnName);
|
|
258
262
|
|
|
259
263
|
if (!column) {
|
|
260
|
-
|
|
264
|
+
res.status(404).json({});
|
|
265
|
+
|
|
266
|
+
return;
|
|
261
267
|
}
|
|
262
268
|
|
|
263
269
|
const repo = repoAndOwner(issue);
|
|
@@ -265,7 +271,9 @@ export default async function BoardApiRoutes(
|
|
|
265
271
|
const canWrite = await userAccess.canWrite(user, repo);
|
|
266
272
|
|
|
267
273
|
if (!canWrite) {
|
|
268
|
-
|
|
274
|
+
res.status(403).json({});
|
|
275
|
+
|
|
276
|
+
return;
|
|
269
277
|
}
|
|
270
278
|
|
|
271
279
|
const octokit = await githubClient.getUserScoped(user);
|
|
@@ -280,15 +288,13 @@ export default async function BoardApiRoutes(
|
|
|
280
288
|
}
|
|
281
289
|
};
|
|
282
290
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
log.error(err, 'failed to move issue');
|
|
291
|
+
moveIssue(context, issue, column, { before, after }).then(() => {
|
|
292
|
+
res.type('json').json({});
|
|
293
|
+
}).catch(err => {
|
|
294
|
+
log.error(err, 'failed to move issue');
|
|
288
295
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
) && null;
|
|
296
|
+
res.status(500).json({ error : true });
|
|
297
|
+
});
|
|
292
298
|
|
|
293
299
|
});
|
|
294
300
|
|
|
@@ -16,6 +16,18 @@ export default function S3() {
|
|
|
16
16
|
S3_ENDPOINT: endpoint
|
|
17
17
|
} = process.env;
|
|
18
18
|
|
|
19
|
+
if (!accessKeyId) {
|
|
20
|
+
throw new Error('process.env.AWS_ACCESS_KEY_ID required');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!secretAccessKey) {
|
|
24
|
+
throw new Error('process.env.AWS_SECRET_ACCESS_KEY required');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!bucket) {
|
|
28
|
+
throw new Error('process.env.S3_BUCKET required');
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
const s3client = new S3Client({
|
|
20
32
|
region,
|
|
21
33
|
endpoint,
|
|
@@ -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,
|
|
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 =
|
|
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 =
|
|
67
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { { ignoreFilter?: string } } StoreFilterConfig
|
|
3
|
+
*
|
|
4
|
+
* @typedef { Extract<import('@octokit/webhooks').EmitterWebhookEventName, 'issues' | `issues.${string}` | 'issue_comment' | `issue_comment.${string}` | 'pull_request' | `pull_request.${string}` | 'pull_request_review' | `pull_request_review.${string}` | 'pull_request_review_comment' | `pull_request_review_comment.${string}` | 'pull_request_review_thread' | `pull_request_review_thread.${string}`> } IssueOrPullWebhookEventName
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
7
|
import { filterIssueOrPull } from '../../filters.js';
|
|
@@ -41,7 +43,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
41
43
|
/**
|
|
42
44
|
* @param {import('../../types.js').Logger } log
|
|
43
45
|
*
|
|
44
|
-
* @return { (
|
|
46
|
+
* @return {<E extends IssueOrPullWebhookEventName>(fn: (context: import('probot').Context<E>) => any) => (context: import('probot').Context<E>) => any}
|
|
45
47
|
*/
|
|
46
48
|
function createWebhookFilter(log) {
|
|
47
49
|
|
|
@@ -49,7 +51,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
49
51
|
|
|
50
52
|
return (context) => {
|
|
51
53
|
|
|
52
|
-
const payload = context.payload;
|
|
54
|
+
const payload = /** @type {any} */ (context.payload);
|
|
53
55
|
|
|
54
56
|
const issueOrPull = filterIssueOrPull(
|
|
55
57
|
payload.issue || payload.pull_request,
|
|
@@ -79,7 +81,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
79
81
|
/**
|
|
80
82
|
* @param {import('../../types.js').Logger } log
|
|
81
83
|
*
|
|
82
|
-
* @return { (
|
|
84
|
+
* @return {<E extends IssueOrPullWebhookEventName>(fn: (context: import('probot').Context<E>) => any) => (context: import('probot').Context<E>) => any}
|
|
83
85
|
*/
|
|
84
86
|
this.createWebhookFilter = createWebhookFilter;
|
|
85
87
|
}
|
|
@@ -37,7 +37,7 @@ export default function Search(config, logger, store) {
|
|
|
37
37
|
|
|
38
38
|
const {
|
|
39
39
|
treatBotsAsReviewers = false,
|
|
40
|
-
defaultFilter
|
|
40
|
+
defaultFilter = ''
|
|
41
41
|
} = config;
|
|
42
42
|
|
|
43
43
|
function filterNoop(issue) {
|
|
@@ -63,13 +63,17 @@ export default function Search(config, logger, store) {
|
|
|
63
63
|
*
|
|
64
64
|
* @return { boolean }
|
|
65
65
|
*/
|
|
66
|
-
function includes(actual, pattern, exact) {
|
|
66
|
+
function includes(actual, pattern, exact = false) {
|
|
67
|
+
|
|
68
|
+
if (!pattern) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
67
71
|
|
|
68
72
|
if (exact) {
|
|
69
|
-
return
|
|
73
|
+
return actual === pattern;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
return
|
|
76
|
+
return actual.toLowerCase().includes(pattern.toLowerCase());
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
function isPull(issue) {
|
|
@@ -445,7 +449,7 @@ export default function Search(config, logger, store) {
|
|
|
445
449
|
* Retrieve a filter function from the given search string.
|
|
446
450
|
*
|
|
447
451
|
* @param {string} search
|
|
448
|
-
* @param {GitHubUser} [user]
|
|
452
|
+
* @param {GitHubUser|null} [user]
|
|
449
453
|
*
|
|
450
454
|
* @return {FilterFn}
|
|
451
455
|
*/
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @typedef { import('probot').Probot } Probot
|
|
3
|
+
* @typedef { import('@octokit/webhooks').EmitterWebhookEventName } WebhookEventName
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A lightweight wrapper around {@link Probot#on} and {@link Probot#onAny}.
|
|
8
|
+
*
|
|
2
9
|
* @constructor
|
|
3
10
|
*
|
|
4
11
|
* @param {import('../../types.js').ProbotApp} app
|
|
@@ -7,8 +14,7 @@
|
|
|
7
14
|
export default function WebhookEvents(app, githubApp) {
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
|
-
* @
|
|
11
|
-
* @param {T} fn
|
|
17
|
+
* @param {(context: import('probot').Context) => any} fn
|
|
12
18
|
*/
|
|
13
19
|
function ifEnabled(fn) {
|
|
14
20
|
|
|
@@ -37,8 +43,9 @@ export default function WebhookEvents(app, githubApp) {
|
|
|
37
43
|
* Register a event lister for a single
|
|
38
44
|
* or a number of webhook events.
|
|
39
45
|
*
|
|
40
|
-
* @
|
|
41
|
-
* @param {
|
|
46
|
+
* @template {WebhookEventName} E
|
|
47
|
+
* @param {E | E[]} events
|
|
48
|
+
* @param {(context: import('probot').Context<E>) => any} fn listener
|
|
42
49
|
*/
|
|
43
50
|
function on(events, fn) {
|
|
44
51
|
app.on(events, ifEnabled(fn));
|
|
@@ -48,7 +55,7 @@ export default function WebhookEvents(app, githubApp) {
|
|
|
48
55
|
* Register an event listener for all
|
|
49
56
|
* webhook events.
|
|
50
57
|
*
|
|
51
|
-
* @param {
|
|
58
|
+
* @param {(context: import('probot').Context) => any} fn
|
|
52
59
|
*/
|
|
53
60
|
function onAny(fn) {
|
|
54
61
|
app.onAny(ifEnabled(fn));
|
package/lib/events.js
CHANGED
|
@@ -139,8 +139,8 @@ Events.prototype.createEvent = function(data) {
|
|
|
139
139
|
* @param {Object} [data] the event object
|
|
140
140
|
* @param {...Object} additionalArgs to be passed to the callback functions
|
|
141
141
|
*
|
|
142
|
-
* @return {Promise<boolean>} the events return value, if
|
|
143
|
-
*
|
|
142
|
+
* @return {Promise<boolean|undefined|any>} the events return value, if
|
|
143
|
+
* specified or false if the default action was prevented by listeners
|
|
144
144
|
*/
|
|
145
145
|
Events.prototype.emit = async function(type, data, ...additionalArgs) {
|
|
146
146
|
|
|
@@ -103,7 +103,8 @@ function validateSetup() {
|
|
|
103
103
|
|
|
104
104
|
const setup = new ManifestCreation();
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
// TODO(nikku): process.env.BASE_URL may not be defined here
|
|
107
|
+
const manifest = JSON.parse(setup.getManifest(setup.pkg, /** @type { string } */ (process.env.BASE_URL)));
|
|
107
108
|
|
|
108
109
|
return [
|
|
109
110
|
!manifest.url && new Error('No <url> configured in app.yml'),
|
package/lib/util/search.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wuffle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "A multi-repository task board for GitHub issues",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nico Rehwaldt",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"@types/mocha": "^10.0.10",
|
|
64
64
|
"@types/sinon": "^21.0.1",
|
|
65
65
|
"chai": "^6.2.2",
|
|
66
|
-
"graphql": "^16.
|
|
66
|
+
"graphql": "^16.14.0",
|
|
67
67
|
"mocha": "^11.7.5",
|
|
68
68
|
"nock": "^14.0.13",
|
|
69
69
|
"nodemon": "^3.1.14",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"index.js",
|
|
84
84
|
"wuffle.config.example.js"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "132f68f8372f71a15c4e34b0fd9db837ccc02b6b"
|
|
87
87
|
}
|