@ycodium-ai/plugin-pull-requests 0.2.3088 → 0.2.3096
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/assets/ui.js +14 -14
- package/dist/index.js +453 -427
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -145,35 +145,6 @@ var createStaleWindow = (options) => {
|
|
|
145
145
|
};
|
|
146
146
|
};
|
|
147
147
|
|
|
148
|
-
// src/service/cursor.ts
|
|
149
|
-
var LIST_CURSOR_PATTERN = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2}))\|(\d{1,9})\|(\d{1,9}(?:,\d{1,9})*)?$/;
|
|
150
|
-
var parseListCursor = (raw) => {
|
|
151
|
-
const match = LIST_CURSOR_PATTERN.exec(raw);
|
|
152
|
-
if (match === null) return null;
|
|
153
|
-
const seenAt = match[3];
|
|
154
|
-
return {
|
|
155
|
-
updatedBefore: match[1],
|
|
156
|
-
delivered: Number(match[2]),
|
|
157
|
-
seenAt: seenAt === void 0 ? [] : seenAt.split(",").map(Number)
|
|
158
|
-
};
|
|
159
|
-
};
|
|
160
|
-
var listCursorKey = (host, repository) => `${host} ${repository.toLowerCase()}`;
|
|
161
|
-
var listCursorAt = (previous, boundary, fetched, deliveredCount) => {
|
|
162
|
-
const seenAt = [
|
|
163
|
-
...previous?.updatedBefore === boundary ? previous.seenAt : [],
|
|
164
|
-
...fetched.filter((item) => item.updatedAt === boundary).map((item) => item.number)
|
|
165
|
-
];
|
|
166
|
-
return `${boundary}|${(previous?.delivered ?? 0) + deliveredCount}|${seenAt.join(",")}`;
|
|
167
|
-
};
|
|
168
|
-
var nextListCursor = (previous, fetched, delivered, cursorAdvance = delivered.length) => {
|
|
169
|
-
if (fetched.length === 0) return null;
|
|
170
|
-
const oldest = fetched.reduce((left, right) => right.updatedAt < left.updatedAt ? right : left);
|
|
171
|
-
return listCursorAt(previous, oldest.updatedAt, fetched, cursorAdvance);
|
|
172
|
-
};
|
|
173
|
-
var withoutAlreadySent = (cursor, items) => cursor === void 0 ? items : items.filter(
|
|
174
|
-
(item) => item.updatedAt !== cursor.updatedBefore || !cursor.seenAt.includes(item.number)
|
|
175
|
-
);
|
|
176
|
-
|
|
177
148
|
// src/service/errors.ts
|
|
178
149
|
var PROVIDER_REQUIREMENT = {
|
|
179
150
|
github: {
|
|
@@ -235,6 +206,35 @@ var PullRequestOperationFailed = class extends Error {
|
|
|
235
206
|
}
|
|
236
207
|
};
|
|
237
208
|
|
|
209
|
+
// src/service/cursor.ts
|
|
210
|
+
var LIST_CURSOR_PATTERN = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2}))\|(\d{1,9})\|(\d{1,9}(?:,\d{1,9})*)?$/;
|
|
211
|
+
var parseListCursor = (raw) => {
|
|
212
|
+
const match = LIST_CURSOR_PATTERN.exec(raw);
|
|
213
|
+
if (match === null) return null;
|
|
214
|
+
const seenAt = match[3];
|
|
215
|
+
return {
|
|
216
|
+
updatedBefore: match[1],
|
|
217
|
+
delivered: Number(match[2]),
|
|
218
|
+
seenAt: seenAt === void 0 ? [] : seenAt.split(",").map(Number)
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
var listCursorKey = (host, repository) => `${host} ${repository.toLowerCase()}`;
|
|
222
|
+
var listCursorAt = (previous, boundary, fetched, deliveredCount) => {
|
|
223
|
+
const seenAt = [
|
|
224
|
+
...previous?.updatedBefore === boundary ? previous.seenAt : [],
|
|
225
|
+
...fetched.filter((item) => item.updatedAt === boundary).map((item) => item.number)
|
|
226
|
+
];
|
|
227
|
+
return `${boundary}|${(previous?.delivered ?? 0) + deliveredCount}|${seenAt.join(",")}`;
|
|
228
|
+
};
|
|
229
|
+
var nextListCursor = (previous, fetched, delivered, cursorAdvance = delivered.length) => {
|
|
230
|
+
if (fetched.length === 0) return null;
|
|
231
|
+
const oldest = fetched.reduce((left, right) => right.updatedAt < left.updatedAt ? right : left);
|
|
232
|
+
return listCursorAt(previous, oldest.updatedAt, fetched, cursorAdvance);
|
|
233
|
+
};
|
|
234
|
+
var withoutAlreadySent = (cursor, items) => cursor === void 0 ? items : items.filter(
|
|
235
|
+
(item) => item.updatedAt !== cursor.updatedBefore || !cursor.seenAt.includes(item.number)
|
|
236
|
+
);
|
|
237
|
+
|
|
238
238
|
// src/githubHttp.ts
|
|
239
239
|
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
240
240
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
@@ -3396,124 +3396,30 @@ var withRateLimitBackoff = (api, host, backoff) => {
|
|
|
3396
3396
|
};
|
|
3397
3397
|
};
|
|
3398
3398
|
|
|
3399
|
-
// src/service/
|
|
3400
|
-
var
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
const normalized = remoteUrl.trim().replace(/\/+$/g, "").toLowerCase();
|
|
3410
|
-
if (/^(?:ssh|https?|git):\/\//i.test(normalized)) {
|
|
3411
|
-
try {
|
|
3412
|
-
return new URL(normalized).hostname;
|
|
3413
|
-
} catch {
|
|
3414
|
-
return "";
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
return /^[a-z0-9._-]+@([^:/\s]+):/i.exec(normalized)?.[1] ?? "";
|
|
3418
|
-
};
|
|
3419
|
-
var claimTable = (apis) => {
|
|
3420
|
-
const claims = /* @__PURE__ */ new Map();
|
|
3421
|
-
for (const api of apis) {
|
|
3422
|
-
for (const host of api.hosts) {
|
|
3423
|
-
if (host.length > 0 && !claims.has(host)) claims.set(host, api.kind);
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
return claims;
|
|
3427
|
-
};
|
|
3428
|
-
var isArchivedWorkspaceProject = (project) => project.archivedAt != null;
|
|
3429
|
-
var listWorkspaceProjects = async (input) => {
|
|
3430
|
-
const projects = await input.listProjects();
|
|
3431
|
-
const claims = claimTable(input.apis);
|
|
3432
|
-
const byKind = new Map(input.apis.map((api) => [api.kind, api]));
|
|
3433
|
-
const supported = [];
|
|
3434
|
-
const unimplemented = /* @__PURE__ */ new Map();
|
|
3435
|
-
const viewerRoots = /* @__PURE__ */ new Map();
|
|
3436
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3437
|
-
for (const project of projects) {
|
|
3438
|
-
if (input.filter.projectId !== void 0 && project.projectId !== input.filter.projectId) {
|
|
3439
|
-
continue;
|
|
3440
|
-
}
|
|
3441
|
-
if (input.filter.projectId === void 0 && isArchivedWorkspaceProject(project)) continue;
|
|
3442
|
-
if (input.filter.projectIds !== void 0 && !input.filter.projectIds.includes(project.projectId)) {
|
|
3443
|
-
continue;
|
|
3444
|
-
}
|
|
3445
|
-
const identity = project.repositoryIdentity;
|
|
3446
|
-
if (identity === null) continue;
|
|
3447
|
-
const repository = repositoryIdentityOf(identity);
|
|
3448
|
-
if (repository === null) continue;
|
|
3449
|
-
let kind = identity.provider;
|
|
3450
|
-
if (kind === "unknown") {
|
|
3451
|
-
const claimed = claims.get(remoteHostOf(identity.remoteUrl));
|
|
3452
|
-
if (claimed !== void 0) kind = claimed;
|
|
3453
|
-
}
|
|
3454
|
-
const host = hostOf(identity, kind);
|
|
3455
|
-
if (input.filter.host !== void 0 && host !== input.filter.host.trim().toLowerCase()) {
|
|
3456
|
-
continue;
|
|
3457
|
-
}
|
|
3458
|
-
const api = byKind.get(kind) ?? null;
|
|
3459
|
-
if (api !== null) {
|
|
3460
|
-
const roots = viewerRoots.get(host);
|
|
3461
|
-
if (roots === void 0) viewerRoots.set(host, [project.workspaceRoot]);
|
|
3462
|
-
else if (!roots.includes(project.workspaceRoot)) roots.push(project.workspaceRoot);
|
|
3463
|
-
}
|
|
3464
|
-
const key = listCursorKey(host, repository);
|
|
3465
|
-
if (seen.has(key)) continue;
|
|
3466
|
-
seen.add(key);
|
|
3467
|
-
if (api === null) {
|
|
3468
|
-
const counted = unimplemented.get(host);
|
|
3469
|
-
if (counted === void 0) unimplemented.set(host, { kind, projectCount: 1 });
|
|
3470
|
-
else counted.projectCount += 1;
|
|
3471
|
-
continue;
|
|
3472
|
-
}
|
|
3473
|
-
supported.push({
|
|
3474
|
-
projectId: project.projectId,
|
|
3475
|
-
projectTitle: project.title,
|
|
3476
|
-
workspaceRoot: project.workspaceRoot,
|
|
3477
|
-
api: withRateLimitBackoff(api, host, input.backoff),
|
|
3478
|
-
repository,
|
|
3479
|
-
host
|
|
3399
|
+
// src/service/serviceSupport.ts
|
|
3400
|
+
var isProviderUnusable = (failure) => failure.code === "missing-tool" || failure.code === "unauthenticated";
|
|
3401
|
+
var unavailableReasonOf = (failure) => failure.code === "missing-tool" ? "token-missing" : "token-rejected";
|
|
3402
|
+
var toPullRequestError = (operation, error, kind, host) => {
|
|
3403
|
+
const failure = providerFailureOf(error);
|
|
3404
|
+
if (isProviderUnusable(failure)) {
|
|
3405
|
+
return new PullRequestUnavailable({
|
|
3406
|
+
reason: unavailableReasonOf(failure),
|
|
3407
|
+
provider: kind,
|
|
3408
|
+
...host === void 0 ? {} : { host }
|
|
3480
3409
|
});
|
|
3481
3410
|
}
|
|
3482
|
-
return {
|
|
3411
|
+
return new PullRequestOperationFailed({
|
|
3412
|
+
operation,
|
|
3413
|
+
detail: failure.detail,
|
|
3414
|
+
...failure.retryAt === void 0 ? {} : { retryAt: failure.retryAt }
|
|
3415
|
+
});
|
|
3483
3416
|
};
|
|
3484
3417
|
|
|
3485
|
-
// src/service/
|
|
3418
|
+
// src/service/listReads.ts
|
|
3486
3419
|
var DEFAULT_REPOSITORY_LIST_LIMIT = 99;
|
|
3487
3420
|
var REPOSITORY_CONCURRENCY = 12;
|
|
3488
3421
|
var REPOSITORY_SEARCH_CHUNK = 100;
|
|
3489
|
-
var LIST_CACHE_TTL_MS = 3e4;
|
|
3490
|
-
var DETAIL_CACHE_TTL_MS = 15e3;
|
|
3491
|
-
var DIFF_CACHE_TTL_MS = 6e4;
|
|
3492
|
-
var COMMIT_DIFF_CACHE_TTL_MS = 10 * 6e4;
|
|
3493
|
-
var LIST_STATS_CACHE_TTL_MS = 6e4;
|
|
3494
|
-
var DIFF_STALE_WINDOW_MS = 10 * 6e4;
|
|
3495
3422
|
var VIEWER_CACHE_TTL_MS = 10 * 6e4;
|
|
3496
|
-
var LIST_CACHE_CAPACITY = 64;
|
|
3497
|
-
var LIST_STATS_CACHE_CAPACITY = 32;
|
|
3498
|
-
var DETAIL_CACHE_CAPACITY = 128;
|
|
3499
|
-
var DIFF_CACHE_CAPACITY = 128;
|
|
3500
|
-
var REF_EPOCH_CAPACITY = 2048;
|
|
3501
|
-
var VERDICT_LABELS = {
|
|
3502
|
-
comment: "review",
|
|
3503
|
-
approve: "approve",
|
|
3504
|
-
"request-changes": "request changes on"
|
|
3505
|
-
};
|
|
3506
|
-
var ACTION_ACCESS_REFUSALS = {
|
|
3507
|
-
merge: "You need write access on this repository to merge.",
|
|
3508
|
-
ready: "You need write access on this repository, or to have opened this change request, to mark it ready for review.",
|
|
3509
|
-
draft: "You need write access on this repository, or to have opened this change request, to return it to a draft.",
|
|
3510
|
-
close: "You need write access on this repository, or to have opened this change request, to close it.",
|
|
3511
|
-
"update-branch": "You need write access on this repository, or to have opened this change request, to update its branch.",
|
|
3512
|
-
reopen: "You need write access on this repository, or to have opened this change request, to reopen it.",
|
|
3513
|
-
"enable-auto-merge": "You need write access on this repository to have it merged for you once it is ready.",
|
|
3514
|
-
"disable-auto-merge": "You need write access on this repository to stop it being merged for you once it is ready."
|
|
3515
|
-
};
|
|
3516
|
-
var REVIEWER_REQUEST_REFUSAL = "You need write access on this repository to ask for a review.";
|
|
3517
3423
|
var mapConcurrent = async (items, limit, run) => {
|
|
3518
3424
|
const results = Array.from({ length: items.length });
|
|
3519
3425
|
let next = 0;
|
|
@@ -3539,24 +3445,7 @@ var firstSuccess = async (runs) => {
|
|
|
3539
3445
|
}
|
|
3540
3446
|
throw last;
|
|
3541
3447
|
};
|
|
3542
|
-
var isProviderUnusable = (failure) => failure.code === "missing-tool" || failure.code === "unauthenticated";
|
|
3543
|
-
var unavailableReasonOf = (failure) => failure.code === "missing-tool" ? "token-missing" : "token-rejected";
|
|
3544
3448
|
var providerDetail = (failure, kind) => isProviderUnusable(failure) ? providerRequirement(kind, unavailableReasonOf(failure)) ?? failure.detail : failure.detail;
|
|
3545
|
-
var toPullRequestError = (operation, error, kind, host) => {
|
|
3546
|
-
const failure = providerFailureOf(error);
|
|
3547
|
-
if (isProviderUnusable(failure)) {
|
|
3548
|
-
return new PullRequestUnavailable({
|
|
3549
|
-
reason: unavailableReasonOf(failure),
|
|
3550
|
-
provider: kind,
|
|
3551
|
-
...host === void 0 ? {} : { host }
|
|
3552
|
-
});
|
|
3553
|
-
}
|
|
3554
|
-
return new PullRequestOperationFailed({
|
|
3555
|
-
operation,
|
|
3556
|
-
detail: failure.detail,
|
|
3557
|
-
...failure.retryAt === void 0 ? {} : { retryAt: failure.retryAt }
|
|
3558
|
-
});
|
|
3559
|
-
};
|
|
3560
3449
|
var resolveAuthorFilter = (author, viewer) => {
|
|
3561
3450
|
const trimmed = author.trim();
|
|
3562
3451
|
if (!/^@?me$/iu.test(trimmed)) return trimmed;
|
|
@@ -3570,45 +3459,8 @@ var matchesRowFilters = (item, filters, viewer) => {
|
|
|
3570
3459
|
// 也就是 "none" 问的那个;`undefined` 是「这台主机根本不汇总」,那种行不判。
|
|
3571
3460
|
(filters.review === void 0 || item.reviewDecision === void 0 || (filters.review === "none" ? item.reviewDecision === null : item.reviewDecision === filters.review)) && (filters.labels === void 0 || filters.labels.every((group) => group.some(holds))) && (filters.excludedLabels === void 0 || !filters.excludedLabels.some(holds)) && (filters.author === void 0 || item.author?.login.toLowerCase() === resolveAuthorFilter(filters.author, viewer).toLowerCase());
|
|
3572
3461
|
};
|
|
3573
|
-
var
|
|
3574
|
-
const
|
|
3575
|
-
const backoff = createBackoff({ now });
|
|
3576
|
-
const workspace = (filter) => listWorkspaceProjects({
|
|
3577
|
-
listProjects: options.listProjects,
|
|
3578
|
-
apis: options.apis,
|
|
3579
|
-
backoff,
|
|
3580
|
-
filter
|
|
3581
|
-
}).catch(() => {
|
|
3582
|
-
throw new PullRequestOperationFailed({
|
|
3583
|
-
operation: "listProjects",
|
|
3584
|
-
detail: "The project list could not be read."
|
|
3585
|
-
});
|
|
3586
|
-
});
|
|
3587
|
-
const requireProject = async (ref) => {
|
|
3588
|
-
const { supported } = await workspace({ projectId: ref.projectId });
|
|
3589
|
-
const match = supported[0];
|
|
3590
|
-
if (!match) throw new PullRequestUnavailable({ reason: "provider-unsupported" });
|
|
3591
|
-
if (match.repository.toLowerCase() !== ref.repository.trim().toLowerCase()) {
|
|
3592
|
-
throw new PullRequestOperationFailed({
|
|
3593
|
-
operation: "resolveRepository",
|
|
3594
|
-
detail: "The change request does not belong to the selected project."
|
|
3595
|
-
});
|
|
3596
|
-
}
|
|
3597
|
-
return match;
|
|
3598
|
-
};
|
|
3599
|
-
const changeRequestRef = (project, ref) => ({
|
|
3600
|
-
cwd: project.workspaceRoot,
|
|
3601
|
-
repository: project.repository,
|
|
3602
|
-
host: project.host,
|
|
3603
|
-
number: ref.number
|
|
3604
|
-
});
|
|
3605
|
-
const viewerPermissionsOf = async (project, ref, operation) => {
|
|
3606
|
-
try {
|
|
3607
|
-
return await project.api.getViewerPermissions(changeRequestRef(project, ref));
|
|
3608
|
-
} catch (error) {
|
|
3609
|
-
throw toPullRequestError(operation, error, project.api.kind, project.host);
|
|
3610
|
-
}
|
|
3611
|
-
};
|
|
3462
|
+
var makePullRequestListReads = (deps) => {
|
|
3463
|
+
const { workspace, now, viewersByHost } = deps;
|
|
3612
3464
|
const decodeCursors = (cursors) => {
|
|
3613
3465
|
if (cursors === void 0) return null;
|
|
3614
3466
|
const decoded = /* @__PURE__ */ new Map();
|
|
@@ -3624,7 +3476,6 @@ var createPullRequestService = (options) => {
|
|
|
3624
3476
|
}
|
|
3625
3477
|
return decoded;
|
|
3626
3478
|
};
|
|
3627
|
-
const viewersByHost = /* @__PURE__ */ new Map();
|
|
3628
3479
|
const resolveViewers = (projects, viewerRoots) => mapConcurrent(
|
|
3629
3480
|
[...new Set(projects.map(({ host }) => host))],
|
|
3630
3481
|
REPOSITORY_CONCURRENCY,
|
|
@@ -3865,117 +3716,6 @@ ${cursorOf(project)?.updatedBefore ?? ""}`;
|
|
|
3865
3716
|
nextCursors
|
|
3866
3717
|
};
|
|
3867
3718
|
};
|
|
3868
|
-
const detailUncached = async (input) => {
|
|
3869
|
-
const project = await requireProject(input);
|
|
3870
|
-
const [changeRequest, viewer] = await Promise.all([
|
|
3871
|
-
project.api.getChangeRequest(changeRequestRef(project, input)).catch((error) => {
|
|
3872
|
-
throw toPullRequestError("detail", error, project.api.kind, project.host);
|
|
3873
|
-
}),
|
|
3874
|
-
viewerOf(project)
|
|
3875
|
-
]);
|
|
3876
|
-
return {
|
|
3877
|
-
provider: project.api.kind,
|
|
3878
|
-
capabilities: project.api.capabilities,
|
|
3879
|
-
projectId: project.projectId,
|
|
3880
|
-
projectTitle: project.projectTitle,
|
|
3881
|
-
workspaceRoot: project.workspaceRoot,
|
|
3882
|
-
repository: project.repository,
|
|
3883
|
-
number: changeRequest.number,
|
|
3884
|
-
title: changeRequest.title,
|
|
3885
|
-
body: changeRequest.body,
|
|
3886
|
-
url: changeRequest.url,
|
|
3887
|
-
author: changeRequest.author,
|
|
3888
|
-
state: changeRequest.state,
|
|
3889
|
-
isDraft: changeRequest.isDraft,
|
|
3890
|
-
mergeability: changeRequest.mergeability,
|
|
3891
|
-
additions: changeRequest.additions,
|
|
3892
|
-
deletions: changeRequest.deletions,
|
|
3893
|
-
changedFiles: changeRequest.changedFiles,
|
|
3894
|
-
headBranch: changeRequest.headBranch,
|
|
3895
|
-
baseBranch: changeRequest.baseBranch,
|
|
3896
|
-
createdAt: changeRequest.createdAt,
|
|
3897
|
-
updatedAt: changeRequest.updatedAt,
|
|
3898
|
-
mergedAt: changeRequest.mergedAt,
|
|
3899
|
-
closedAt: changeRequest.closedAt,
|
|
3900
|
-
reviewers: changeRequest.reviewers,
|
|
3901
|
-
labels: changeRequest.labels,
|
|
3902
|
-
checks: changeRequest.checks,
|
|
3903
|
-
mergeCapabilities: changeRequest.mergeCapabilities,
|
|
3904
|
-
viewerPermissions: changeRequest.viewerPermissions,
|
|
3905
|
-
...viewer === null || viewer.trim().length === 0 ? {} : { viewer },
|
|
3906
|
-
...changeRequest.baseComparison === void 0 ? {} : { baseComparison: changeRequest.baseComparison },
|
|
3907
|
-
...changeRequest.behindBy === void 0 ? {} : { behindBy: changeRequest.behindBy },
|
|
3908
|
-
...changeRequest.autoMergeEnabled === void 0 ? {} : { autoMergeEnabled: changeRequest.autoMergeEnabled }
|
|
3909
|
-
};
|
|
3910
|
-
};
|
|
3911
|
-
const activityUncached = async (input) => {
|
|
3912
|
-
const project = await requireProject(input);
|
|
3913
|
-
const activity2 = await project.api.getChangeRequestActivity(changeRequestRef(project, input)).catch((error) => {
|
|
3914
|
-
throw toPullRequestError("activity", error, project.api.kind, project.host);
|
|
3915
|
-
});
|
|
3916
|
-
return {
|
|
3917
|
-
...activity2.author === void 0 ? {} : { author: activity2.author },
|
|
3918
|
-
...activity2.reviewers === void 0 ? {} : { reviewers: activity2.reviewers },
|
|
3919
|
-
comments: activity2.comments,
|
|
3920
|
-
commentCount: activity2.commentCount,
|
|
3921
|
-
commentsTruncated: activity2.commentsTruncated,
|
|
3922
|
-
reviewThreads: activity2.reviewThreads,
|
|
3923
|
-
commits: activity2.commits,
|
|
3924
|
-
...activity2.reactions === void 0 ? {} : { reactions: activity2.reactions }
|
|
3925
|
-
};
|
|
3926
|
-
};
|
|
3927
|
-
const threadComments = async (input) => {
|
|
3928
|
-
const project = await requireProject(input);
|
|
3929
|
-
const read = project.api.getReviewThreadComments;
|
|
3930
|
-
if (read === void 0) {
|
|
3931
|
-
throw new PullRequestOperationFailed({
|
|
3932
|
-
operation: "threadComments",
|
|
3933
|
-
detail: "This host does not page review thread comments."
|
|
3934
|
-
});
|
|
3935
|
-
}
|
|
3936
|
-
return read({
|
|
3937
|
-
...changeRequestRef(project, input),
|
|
3938
|
-
threadId: input.threadId,
|
|
3939
|
-
cursor: input.cursor
|
|
3940
|
-
}).catch((error) => {
|
|
3941
|
-
throw toPullRequestError("threadComments", error, project.api.kind, project.host);
|
|
3942
|
-
});
|
|
3943
|
-
};
|
|
3944
|
-
const diffUncached = async (input) => {
|
|
3945
|
-
const project = await requireProject(input);
|
|
3946
|
-
if (!project.api.capabilities.diff) {
|
|
3947
|
-
throw new PullRequestOperationFailed({
|
|
3948
|
-
operation: "diff",
|
|
3949
|
-
detail: "This host cannot provide a diff for a change request."
|
|
3950
|
-
});
|
|
3951
|
-
}
|
|
3952
|
-
return project.api.getDiff({
|
|
3953
|
-
...changeRequestRef(project, input),
|
|
3954
|
-
...input.cursor === void 0 ? {} : { cursor: input.cursor },
|
|
3955
|
-
...input.commit === void 0 ? {} : { commit: input.commit }
|
|
3956
|
-
}).catch((error) => {
|
|
3957
|
-
throw toPullRequestError("diff", error, project.api.kind, project.host);
|
|
3958
|
-
});
|
|
3959
|
-
};
|
|
3960
|
-
const diffFileContents = async (input) => {
|
|
3961
|
-
const project = await requireProject(input);
|
|
3962
|
-
const read = project.api.getDiffFileContents;
|
|
3963
|
-
if (!project.api.capabilities.diff || read === void 0) {
|
|
3964
|
-
throw new PullRequestOperationFailed({
|
|
3965
|
-
operation: "diffFileContents",
|
|
3966
|
-
detail: "This host cannot expand unchanged pull request lines."
|
|
3967
|
-
});
|
|
3968
|
-
}
|
|
3969
|
-
return read({
|
|
3970
|
-
...changeRequestRef(project, input),
|
|
3971
|
-
...input.commit === void 0 ? {} : { commit: input.commit },
|
|
3972
|
-
changeType: input.changeType,
|
|
3973
|
-
oldPath: input.oldPath,
|
|
3974
|
-
newPath: input.newPath
|
|
3975
|
-
}).catch((error) => {
|
|
3976
|
-
throw toPullRequestError("diffFileContents", error, project.api.kind, project.host);
|
|
3977
|
-
});
|
|
3978
|
-
};
|
|
3979
3719
|
const listStatsUncached = async (input) => {
|
|
3980
3720
|
if (input.refs.length === 0) return { stats: [] };
|
|
3981
3721
|
const { supported } = await workspace({});
|
|
@@ -4037,128 +3777,130 @@ ${cursorOf(project)?.updatedBefore ?? ""}`;
|
|
|
4037
3777
|
);
|
|
4038
3778
|
return { stats: stats.flat() };
|
|
4039
3779
|
};
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
3780
|
+
return { listUncached, listStatsUncached, viewerOf };
|
|
3781
|
+
};
|
|
3782
|
+
|
|
3783
|
+
// src/service/workspace.ts
|
|
3784
|
+
var repositoryIdentityOf = (identity) => {
|
|
3785
|
+
if (identity.displayName) return identity.displayName;
|
|
3786
|
+
return identity.owner && identity.name ? `${identity.owner}/${identity.name}` : null;
|
|
3787
|
+
};
|
|
3788
|
+
var hostOf = (identity, kind) => {
|
|
3789
|
+
const host = identity.canonicalKey.split("/")[0]?.trim();
|
|
3790
|
+
return host === void 0 || host.length === 0 ? kind : host.toLowerCase();
|
|
3791
|
+
};
|
|
3792
|
+
var remoteHostOf = (remoteUrl) => {
|
|
3793
|
+
const normalized = remoteUrl.trim().replace(/\/+$/g, "").toLowerCase();
|
|
3794
|
+
if (/^(?:ssh|https?|git):\/\//i.test(normalized)) {
|
|
3795
|
+
try {
|
|
3796
|
+
return new URL(normalized).hostname;
|
|
3797
|
+
} catch {
|
|
3798
|
+
return "";
|
|
4050
3799
|
}
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
capacity: DETAIL_CACHE_CAPACITY,
|
|
4060
|
-
ttlMs: () => DETAIL_CACHE_TTL_MS,
|
|
4061
|
-
now
|
|
4062
|
-
});
|
|
4063
|
-
const activityCache = createCache({
|
|
4064
|
-
capacity: DETAIL_CACHE_CAPACITY,
|
|
4065
|
-
ttlMs: () => DETAIL_CACHE_TTL_MS,
|
|
4066
|
-
now
|
|
4067
|
-
});
|
|
4068
|
-
const diffCache = createCache({
|
|
4069
|
-
capacity: DIFF_CACHE_CAPACITY,
|
|
4070
|
-
// commit 的 diff 内容寻址,活得比整份 diff 久。
|
|
4071
|
-
ttlMs: (key) => JSON.parse(key)[5] === null ? DIFF_CACHE_TTL_MS : COMMIT_DIFF_CACHE_TTL_MS,
|
|
4072
|
-
now
|
|
4073
|
-
});
|
|
4074
|
-
const listStatsCache = createCache({
|
|
4075
|
-
capacity: LIST_STATS_CACHE_CAPACITY,
|
|
4076
|
-
ttlMs: () => LIST_STATS_CACHE_TTL_MS,
|
|
4077
|
-
now
|
|
4078
|
-
});
|
|
4079
|
-
const staleDiff = createStaleWindow({
|
|
4080
|
-
capacity: DIFF_CACHE_CAPACITY,
|
|
4081
|
-
staleMs: DIFF_STALE_WINDOW_MS,
|
|
4082
|
-
now,
|
|
4083
|
-
...options.onRefreshFailure === void 0 ? {} : { onRefreshFailure: options.onRefreshFailure }
|
|
4084
|
-
});
|
|
4085
|
-
const refKey = (ref) => JSON.stringify([refEpoch(ref), ref.projectId, ref.repository, ref.number]);
|
|
4086
|
-
const list = (input) => {
|
|
4087
|
-
const key = JSON.stringify([
|
|
4088
|
-
listingsEpoch,
|
|
4089
|
-
input.state,
|
|
4090
|
-
input.involvement ?? null,
|
|
4091
|
-
// 按位置写,所以同一组筛选无论记录怎么拼出来都是同一个键。
|
|
4092
|
-
input.filters === void 0 ? null : [
|
|
4093
|
-
input.filters.draft ?? null,
|
|
4094
|
-
input.filters.review ?? null,
|
|
4095
|
-
input.filters.checks ?? null,
|
|
4096
|
-
input.filters.author ?? null,
|
|
4097
|
-
input.filters.labels ?? null,
|
|
4098
|
-
input.filters.excludedLabels ?? null
|
|
4099
|
-
],
|
|
4100
|
-
input.projectId ?? null,
|
|
4101
|
-
input.projectIds === void 0 ? null : [...input.projectIds].sort(),
|
|
4102
|
-
input.host ?? null,
|
|
4103
|
-
input.limit ?? null,
|
|
4104
|
-
input.query ?? null,
|
|
4105
|
-
input.cursors === void 0 ? null : Object.entries(input.cursors).toSorted(([left], [right]) => left.localeCompare(right))
|
|
4106
|
-
]);
|
|
4107
|
-
return listCache.get(key, () => listUncached(input));
|
|
4108
|
-
};
|
|
4109
|
-
const listStats = (input) => {
|
|
4110
|
-
if (input.refs.length === 0) return Promise.resolve({ stats: [] });
|
|
4111
|
-
const key = JSON.stringify([
|
|
4112
|
-
listingsEpoch,
|
|
4113
|
-
input.refs.map((ref) => [ref.projectId, ref.repository, ref.number]).toSorted(
|
|
4114
|
-
(left, right) => `${left[0]} ${left[1]} ${left[2]}`.localeCompare(`${right[0]} ${right[1]} ${right[2]}`)
|
|
4115
|
-
)
|
|
4116
|
-
]);
|
|
4117
|
-
return listStatsCache.get(key, () => listStatsUncached(input));
|
|
4118
|
-
};
|
|
4119
|
-
const detail = (input) => detailCache.get(refKey(input), () => detailUncached(input));
|
|
4120
|
-
const activity = (input) => activityCache.get(refKey(input), () => activityUncached(input));
|
|
4121
|
-
const diff = (input) => {
|
|
4122
|
-
const key = JSON.stringify([
|
|
4123
|
-
refEpoch(input),
|
|
4124
|
-
input.projectId,
|
|
4125
|
-
input.repository,
|
|
4126
|
-
input.number,
|
|
4127
|
-
input.cursor ?? null,
|
|
4128
|
-
input.commit ?? null
|
|
4129
|
-
]);
|
|
4130
|
-
return staleDiff.get(key, () => diffCache.get(key, () => diffUncached(input)));
|
|
4131
|
-
};
|
|
4132
|
-
const invalidate = (input) => {
|
|
4133
|
-
if (input.reference === void 0) {
|
|
4134
|
-
listingsEpoch = ++epochCounter;
|
|
4135
|
-
viewersByHost.clear();
|
|
4136
|
-
return Promise.resolve(null);
|
|
3800
|
+
}
|
|
3801
|
+
return /^[a-z0-9._-]+@([^:/\s]+):/i.exec(normalized)?.[1] ?? "";
|
|
3802
|
+
};
|
|
3803
|
+
var claimTable = (apis) => {
|
|
3804
|
+
const claims = /* @__PURE__ */ new Map();
|
|
3805
|
+
for (const api of apis) {
|
|
3806
|
+
for (const host of api.hosts) {
|
|
3807
|
+
if (host.length > 0 && !claims.has(host)) claims.set(host, api.kind);
|
|
4137
3808
|
}
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
const
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
3809
|
+
}
|
|
3810
|
+
return claims;
|
|
3811
|
+
};
|
|
3812
|
+
var isArchivedWorkspaceProject = (project) => project.archivedAt != null;
|
|
3813
|
+
var listWorkspaceProjects = async (input) => {
|
|
3814
|
+
const projects = await input.listProjects();
|
|
3815
|
+
const claims = claimTable(input.apis);
|
|
3816
|
+
const byKind = new Map(input.apis.map((api) => [api.kind, api]));
|
|
3817
|
+
const supported = [];
|
|
3818
|
+
const unimplemented = /* @__PURE__ */ new Map();
|
|
3819
|
+
const viewerRoots = /* @__PURE__ */ new Map();
|
|
3820
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3821
|
+
for (const project of projects) {
|
|
3822
|
+
if (input.filter.projectId !== void 0 && project.projectId !== input.filter.projectId) {
|
|
3823
|
+
continue;
|
|
4153
3824
|
}
|
|
4154
|
-
|
|
4155
|
-
if (!
|
|
4156
|
-
|
|
4157
|
-
operation: "reviewerCandidates",
|
|
4158
|
-
detail: REVIEWER_REQUEST_REFUSAL
|
|
4159
|
-
});
|
|
3825
|
+
if (input.filter.projectId === void 0 && isArchivedWorkspaceProject(project)) continue;
|
|
3826
|
+
if (input.filter.projectIds !== void 0 && !input.filter.projectIds.includes(project.projectId)) {
|
|
3827
|
+
continue;
|
|
4160
3828
|
}
|
|
4161
|
-
|
|
3829
|
+
const identity = project.repositoryIdentity;
|
|
3830
|
+
if (identity === null) continue;
|
|
3831
|
+
const repository = repositoryIdentityOf(identity);
|
|
3832
|
+
if (repository === null) continue;
|
|
3833
|
+
let kind = identity.provider;
|
|
3834
|
+
if (kind === "unknown") {
|
|
3835
|
+
const claimed = claims.get(remoteHostOf(identity.remoteUrl));
|
|
3836
|
+
if (claimed !== void 0) kind = claimed;
|
|
3837
|
+
}
|
|
3838
|
+
const host = hostOf(identity, kind);
|
|
3839
|
+
if (input.filter.host !== void 0 && host !== input.filter.host.trim().toLowerCase()) {
|
|
3840
|
+
continue;
|
|
3841
|
+
}
|
|
3842
|
+
const api = byKind.get(kind) ?? null;
|
|
3843
|
+
if (api !== null) {
|
|
3844
|
+
const roots = viewerRoots.get(host);
|
|
3845
|
+
if (roots === void 0) viewerRoots.set(host, [project.workspaceRoot]);
|
|
3846
|
+
else if (!roots.includes(project.workspaceRoot)) roots.push(project.workspaceRoot);
|
|
3847
|
+
}
|
|
3848
|
+
const key = listCursorKey(host, repository);
|
|
3849
|
+
if (seen.has(key)) continue;
|
|
3850
|
+
seen.add(key);
|
|
3851
|
+
if (api === null) {
|
|
3852
|
+
const counted = unimplemented.get(host);
|
|
3853
|
+
if (counted === void 0) unimplemented.set(host, { kind, projectCount: 1 });
|
|
3854
|
+
else counted.projectCount += 1;
|
|
3855
|
+
continue;
|
|
3856
|
+
}
|
|
3857
|
+
supported.push({
|
|
3858
|
+
projectId: project.projectId,
|
|
3859
|
+
projectTitle: project.title,
|
|
3860
|
+
workspaceRoot: project.workspaceRoot,
|
|
3861
|
+
api: withRateLimitBackoff(api, host, input.backoff),
|
|
3862
|
+
repository,
|
|
3863
|
+
host
|
|
3864
|
+
});
|
|
3865
|
+
}
|
|
3866
|
+
return { supported, unimplemented, viewerRoots };
|
|
3867
|
+
};
|
|
3868
|
+
|
|
3869
|
+
// src/service/writes.ts
|
|
3870
|
+
var VERDICT_LABELS = {
|
|
3871
|
+
comment: "review",
|
|
3872
|
+
approve: "approve",
|
|
3873
|
+
"request-changes": "request changes on"
|
|
3874
|
+
};
|
|
3875
|
+
var ACTION_ACCESS_REFUSALS = {
|
|
3876
|
+
merge: "You need write access on this repository to merge.",
|
|
3877
|
+
ready: "You need write access on this repository, or to have opened this change request, to mark it ready for review.",
|
|
3878
|
+
draft: "You need write access on this repository, or to have opened this change request, to return it to a draft.",
|
|
3879
|
+
close: "You need write access on this repository, or to have opened this change request, to close it.",
|
|
3880
|
+
"update-branch": "You need write access on this repository, or to have opened this change request, to update its branch.",
|
|
3881
|
+
reopen: "You need write access on this repository, or to have opened this change request, to reopen it.",
|
|
3882
|
+
"enable-auto-merge": "You need write access on this repository to have it merged for you once it is ready.",
|
|
3883
|
+
"disable-auto-merge": "You need write access on this repository to stop it being merged for you once it is ready."
|
|
3884
|
+
};
|
|
3885
|
+
var REVIEWER_REQUEST_REFUSAL = "You need write access on this repository to ask for a review.";
|
|
3886
|
+
var makePullRequestWrites = (deps) => {
|
|
3887
|
+
const { requireProject, changeRequestRef, viewerPermissionsOf, afterMutation, options } = deps;
|
|
3888
|
+
const reviewerCandidates = async (input) => {
|
|
3889
|
+
const project = await requireProject(input);
|
|
3890
|
+
if (!project.api.capabilities.reviewers.listCandidates) {
|
|
3891
|
+
throw new PullRequestOperationFailed({
|
|
3892
|
+
operation: "reviewerCandidates",
|
|
3893
|
+
detail: "This host cannot say who may review a change request."
|
|
3894
|
+
});
|
|
3895
|
+
}
|
|
3896
|
+
const viewer = await viewerPermissionsOf(project, input, "reviewerCandidates");
|
|
3897
|
+
if (!viewer.requestReviewers) {
|
|
3898
|
+
throw new PullRequestOperationFailed({
|
|
3899
|
+
operation: "reviewerCandidates",
|
|
3900
|
+
detail: REVIEWER_REQUEST_REFUSAL
|
|
3901
|
+
});
|
|
3902
|
+
}
|
|
3903
|
+
return project.api.listReviewerCandidates(changeRequestRef(project, input)).catch((error) => {
|
|
4162
3904
|
throw toPullRequestError("reviewerCandidates", error, project.api.kind, project.host);
|
|
4163
3905
|
});
|
|
4164
3906
|
};
|
|
@@ -4312,8 +4054,8 @@ ${cursorOf(project)?.updatedBefore ?? ""}`;
|
|
|
4312
4054
|
const submitReview3 = async (input) => {
|
|
4313
4055
|
const project = await requireProject(input);
|
|
4314
4056
|
const review = project.api.capabilities.review;
|
|
4315
|
-
const refuse = (
|
|
4316
|
-
throw new PullRequestOperationFailed({ operation: "submitReview", detail
|
|
4057
|
+
const refuse = (detail) => {
|
|
4058
|
+
throw new PullRequestOperationFailed({ operation: "submitReview", detail });
|
|
4317
4059
|
};
|
|
4318
4060
|
if (!review.verdicts.includes(input.verdict)) {
|
|
4319
4061
|
refuse(`This host cannot ${VERDICT_LABELS[input.verdict]} a change request.`);
|
|
@@ -4441,6 +4183,290 @@ ${cursorOf(project)?.updatedBefore ?? ""}`;
|
|
|
4441
4183
|
return setReaction3(input);
|
|
4442
4184
|
}
|
|
4443
4185
|
};
|
|
4186
|
+
return { reviewerCandidates, act, converse };
|
|
4187
|
+
};
|
|
4188
|
+
|
|
4189
|
+
// src/service/pullRequestService.ts
|
|
4190
|
+
var LIST_CACHE_TTL_MS = 3e4;
|
|
4191
|
+
var DETAIL_CACHE_TTL_MS = 15e3;
|
|
4192
|
+
var DIFF_CACHE_TTL_MS = 6e4;
|
|
4193
|
+
var COMMIT_DIFF_CACHE_TTL_MS = 10 * 6e4;
|
|
4194
|
+
var LIST_STATS_CACHE_TTL_MS = 6e4;
|
|
4195
|
+
var DIFF_STALE_WINDOW_MS = 10 * 6e4;
|
|
4196
|
+
var LIST_CACHE_CAPACITY = 64;
|
|
4197
|
+
var LIST_STATS_CACHE_CAPACITY = 32;
|
|
4198
|
+
var DETAIL_CACHE_CAPACITY = 128;
|
|
4199
|
+
var DIFF_CACHE_CAPACITY = 128;
|
|
4200
|
+
var REF_EPOCH_CAPACITY = 2048;
|
|
4201
|
+
var createPullRequestService = (options) => {
|
|
4202
|
+
const now = options.now ?? Date.now;
|
|
4203
|
+
const backoff = createBackoff({ now });
|
|
4204
|
+
const workspace = (filter) => listWorkspaceProjects({
|
|
4205
|
+
listProjects: options.listProjects,
|
|
4206
|
+
apis: options.apis,
|
|
4207
|
+
backoff,
|
|
4208
|
+
filter
|
|
4209
|
+
}).catch(() => {
|
|
4210
|
+
throw new PullRequestOperationFailed({
|
|
4211
|
+
operation: "listProjects",
|
|
4212
|
+
detail: "The project list could not be read."
|
|
4213
|
+
});
|
|
4214
|
+
});
|
|
4215
|
+
const requireProject = async (ref) => {
|
|
4216
|
+
const { supported } = await workspace({ projectId: ref.projectId });
|
|
4217
|
+
const match = supported[0];
|
|
4218
|
+
if (!match) throw new PullRequestUnavailable({ reason: "provider-unsupported" });
|
|
4219
|
+
if (match.repository.toLowerCase() !== ref.repository.trim().toLowerCase()) {
|
|
4220
|
+
throw new PullRequestOperationFailed({
|
|
4221
|
+
operation: "resolveRepository",
|
|
4222
|
+
detail: "The change request does not belong to the selected project."
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
4225
|
+
return match;
|
|
4226
|
+
};
|
|
4227
|
+
const changeRequestRef = (project, ref) => ({
|
|
4228
|
+
cwd: project.workspaceRoot,
|
|
4229
|
+
repository: project.repository,
|
|
4230
|
+
host: project.host,
|
|
4231
|
+
number: ref.number
|
|
4232
|
+
});
|
|
4233
|
+
const viewerPermissionsOf = async (project, ref, operation) => {
|
|
4234
|
+
try {
|
|
4235
|
+
return await project.api.getViewerPermissions(changeRequestRef(project, ref));
|
|
4236
|
+
} catch (error) {
|
|
4237
|
+
throw toPullRequestError(operation, error, project.api.kind, project.host);
|
|
4238
|
+
}
|
|
4239
|
+
};
|
|
4240
|
+
const viewersByHost = /* @__PURE__ */ new Map();
|
|
4241
|
+
const { listUncached, listStatsUncached, viewerOf } = makePullRequestListReads({
|
|
4242
|
+
workspace,
|
|
4243
|
+
now,
|
|
4244
|
+
viewersByHost
|
|
4245
|
+
});
|
|
4246
|
+
const detailUncached = async (input) => {
|
|
4247
|
+
const project = await requireProject(input);
|
|
4248
|
+
const [changeRequest, viewer] = await Promise.all([
|
|
4249
|
+
project.api.getChangeRequest(changeRequestRef(project, input)).catch((error) => {
|
|
4250
|
+
throw toPullRequestError("detail", error, project.api.kind, project.host);
|
|
4251
|
+
}),
|
|
4252
|
+
viewerOf(project)
|
|
4253
|
+
]);
|
|
4254
|
+
return {
|
|
4255
|
+
provider: project.api.kind,
|
|
4256
|
+
capabilities: project.api.capabilities,
|
|
4257
|
+
projectId: project.projectId,
|
|
4258
|
+
projectTitle: project.projectTitle,
|
|
4259
|
+
workspaceRoot: project.workspaceRoot,
|
|
4260
|
+
repository: project.repository,
|
|
4261
|
+
number: changeRequest.number,
|
|
4262
|
+
title: changeRequest.title,
|
|
4263
|
+
body: changeRequest.body,
|
|
4264
|
+
url: changeRequest.url,
|
|
4265
|
+
author: changeRequest.author,
|
|
4266
|
+
state: changeRequest.state,
|
|
4267
|
+
isDraft: changeRequest.isDraft,
|
|
4268
|
+
mergeability: changeRequest.mergeability,
|
|
4269
|
+
additions: changeRequest.additions,
|
|
4270
|
+
deletions: changeRequest.deletions,
|
|
4271
|
+
changedFiles: changeRequest.changedFiles,
|
|
4272
|
+
headBranch: changeRequest.headBranch,
|
|
4273
|
+
baseBranch: changeRequest.baseBranch,
|
|
4274
|
+
createdAt: changeRequest.createdAt,
|
|
4275
|
+
updatedAt: changeRequest.updatedAt,
|
|
4276
|
+
mergedAt: changeRequest.mergedAt,
|
|
4277
|
+
closedAt: changeRequest.closedAt,
|
|
4278
|
+
reviewers: changeRequest.reviewers,
|
|
4279
|
+
labels: changeRequest.labels,
|
|
4280
|
+
checks: changeRequest.checks,
|
|
4281
|
+
mergeCapabilities: changeRequest.mergeCapabilities,
|
|
4282
|
+
viewerPermissions: changeRequest.viewerPermissions,
|
|
4283
|
+
...viewer === null || viewer.trim().length === 0 ? {} : { viewer },
|
|
4284
|
+
...changeRequest.baseComparison === void 0 ? {} : { baseComparison: changeRequest.baseComparison },
|
|
4285
|
+
...changeRequest.behindBy === void 0 ? {} : { behindBy: changeRequest.behindBy },
|
|
4286
|
+
...changeRequest.autoMergeEnabled === void 0 ? {} : { autoMergeEnabled: changeRequest.autoMergeEnabled }
|
|
4287
|
+
};
|
|
4288
|
+
};
|
|
4289
|
+
const activityUncached = async (input) => {
|
|
4290
|
+
const project = await requireProject(input);
|
|
4291
|
+
const activity2 = await project.api.getChangeRequestActivity(changeRequestRef(project, input)).catch((error) => {
|
|
4292
|
+
throw toPullRequestError("activity", error, project.api.kind, project.host);
|
|
4293
|
+
});
|
|
4294
|
+
return {
|
|
4295
|
+
...activity2.author === void 0 ? {} : { author: activity2.author },
|
|
4296
|
+
...activity2.reviewers === void 0 ? {} : { reviewers: activity2.reviewers },
|
|
4297
|
+
comments: activity2.comments,
|
|
4298
|
+
commentCount: activity2.commentCount,
|
|
4299
|
+
commentsTruncated: activity2.commentsTruncated,
|
|
4300
|
+
reviewThreads: activity2.reviewThreads,
|
|
4301
|
+
commits: activity2.commits,
|
|
4302
|
+
...activity2.reactions === void 0 ? {} : { reactions: activity2.reactions }
|
|
4303
|
+
};
|
|
4304
|
+
};
|
|
4305
|
+
const threadComments = async (input) => {
|
|
4306
|
+
const project = await requireProject(input);
|
|
4307
|
+
const read = project.api.getReviewThreadComments;
|
|
4308
|
+
if (read === void 0) {
|
|
4309
|
+
throw new PullRequestOperationFailed({
|
|
4310
|
+
operation: "threadComments",
|
|
4311
|
+
detail: "This host does not page review thread comments."
|
|
4312
|
+
});
|
|
4313
|
+
}
|
|
4314
|
+
return read({
|
|
4315
|
+
...changeRequestRef(project, input),
|
|
4316
|
+
threadId: input.threadId,
|
|
4317
|
+
cursor: input.cursor
|
|
4318
|
+
}).catch((error) => {
|
|
4319
|
+
throw toPullRequestError("threadComments", error, project.api.kind, project.host);
|
|
4320
|
+
});
|
|
4321
|
+
};
|
|
4322
|
+
const diffUncached = async (input) => {
|
|
4323
|
+
const project = await requireProject(input);
|
|
4324
|
+
if (!project.api.capabilities.diff) {
|
|
4325
|
+
throw new PullRequestOperationFailed({
|
|
4326
|
+
operation: "diff",
|
|
4327
|
+
detail: "This host cannot provide a diff for a change request."
|
|
4328
|
+
});
|
|
4329
|
+
}
|
|
4330
|
+
return project.api.getDiff({
|
|
4331
|
+
...changeRequestRef(project, input),
|
|
4332
|
+
...input.cursor === void 0 ? {} : { cursor: input.cursor },
|
|
4333
|
+
...input.commit === void 0 ? {} : { commit: input.commit }
|
|
4334
|
+
}).catch((error) => {
|
|
4335
|
+
throw toPullRequestError("diff", error, project.api.kind, project.host);
|
|
4336
|
+
});
|
|
4337
|
+
};
|
|
4338
|
+
const diffFileContents = async (input) => {
|
|
4339
|
+
const project = await requireProject(input);
|
|
4340
|
+
const read = project.api.getDiffFileContents;
|
|
4341
|
+
if (!project.api.capabilities.diff || read === void 0) {
|
|
4342
|
+
throw new PullRequestOperationFailed({
|
|
4343
|
+
operation: "diffFileContents",
|
|
4344
|
+
detail: "This host cannot expand unchanged pull request lines."
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
return read({
|
|
4348
|
+
...changeRequestRef(project, input),
|
|
4349
|
+
...input.commit === void 0 ? {} : { commit: input.commit },
|
|
4350
|
+
changeType: input.changeType,
|
|
4351
|
+
oldPath: input.oldPath,
|
|
4352
|
+
newPath: input.newPath
|
|
4353
|
+
}).catch((error) => {
|
|
4354
|
+
throw toPullRequestError("diffFileContents", error, project.api.kind, project.host);
|
|
4355
|
+
});
|
|
4356
|
+
};
|
|
4357
|
+
let epochCounter = 0;
|
|
4358
|
+
let listingsEpoch = 0;
|
|
4359
|
+
const refEpochs = /* @__PURE__ */ new Map();
|
|
4360
|
+
const refScope = (ref) => `${ref.projectId} ${ref.repository} ${ref.number}`;
|
|
4361
|
+
const refEpoch = (ref) => refEpochs.get(refScope(ref)) ?? 0;
|
|
4362
|
+
const bumpRefEpoch = (ref) => {
|
|
4363
|
+
const scope = refScope(ref);
|
|
4364
|
+
if (!refEpochs.has(scope) && refEpochs.size >= REF_EPOCH_CAPACITY) {
|
|
4365
|
+
const oldest = refEpochs.keys().next().value;
|
|
4366
|
+
if (oldest !== void 0) refEpochs.delete(oldest);
|
|
4367
|
+
}
|
|
4368
|
+
refEpochs.set(scope, ++epochCounter);
|
|
4369
|
+
};
|
|
4370
|
+
const listCache = createCache({
|
|
4371
|
+
capacity: LIST_CACHE_CAPACITY,
|
|
4372
|
+
ttlMs: () => LIST_CACHE_TTL_MS,
|
|
4373
|
+
now
|
|
4374
|
+
});
|
|
4375
|
+
const detailCache = createCache({
|
|
4376
|
+
capacity: DETAIL_CACHE_CAPACITY,
|
|
4377
|
+
ttlMs: () => DETAIL_CACHE_TTL_MS,
|
|
4378
|
+
now
|
|
4379
|
+
});
|
|
4380
|
+
const activityCache = createCache({
|
|
4381
|
+
capacity: DETAIL_CACHE_CAPACITY,
|
|
4382
|
+
ttlMs: () => DETAIL_CACHE_TTL_MS,
|
|
4383
|
+
now
|
|
4384
|
+
});
|
|
4385
|
+
const diffCache = createCache({
|
|
4386
|
+
capacity: DIFF_CACHE_CAPACITY,
|
|
4387
|
+
// commit 的 diff 内容寻址,活得比整份 diff 久。
|
|
4388
|
+
ttlMs: (key) => JSON.parse(key)[5] === null ? DIFF_CACHE_TTL_MS : COMMIT_DIFF_CACHE_TTL_MS,
|
|
4389
|
+
now
|
|
4390
|
+
});
|
|
4391
|
+
const listStatsCache = createCache({
|
|
4392
|
+
capacity: LIST_STATS_CACHE_CAPACITY,
|
|
4393
|
+
ttlMs: () => LIST_STATS_CACHE_TTL_MS,
|
|
4394
|
+
now
|
|
4395
|
+
});
|
|
4396
|
+
const staleDiff = createStaleWindow({
|
|
4397
|
+
capacity: DIFF_CACHE_CAPACITY,
|
|
4398
|
+
staleMs: DIFF_STALE_WINDOW_MS,
|
|
4399
|
+
now,
|
|
4400
|
+
...options.onRefreshFailure === void 0 ? {} : { onRefreshFailure: options.onRefreshFailure }
|
|
4401
|
+
});
|
|
4402
|
+
const refKey = (ref) => JSON.stringify([refEpoch(ref), ref.projectId, ref.repository, ref.number]);
|
|
4403
|
+
const list = (input) => {
|
|
4404
|
+
const key = JSON.stringify([
|
|
4405
|
+
listingsEpoch,
|
|
4406
|
+
input.state,
|
|
4407
|
+
input.involvement ?? null,
|
|
4408
|
+
// 按位置写,所以同一组筛选无论记录怎么拼出来都是同一个键。
|
|
4409
|
+
input.filters === void 0 ? null : [
|
|
4410
|
+
input.filters.draft ?? null,
|
|
4411
|
+
input.filters.review ?? null,
|
|
4412
|
+
input.filters.checks ?? null,
|
|
4413
|
+
input.filters.author ?? null,
|
|
4414
|
+
input.filters.labels ?? null,
|
|
4415
|
+
input.filters.excludedLabels ?? null
|
|
4416
|
+
],
|
|
4417
|
+
input.projectId ?? null,
|
|
4418
|
+
input.projectIds === void 0 ? null : [...input.projectIds].sort(),
|
|
4419
|
+
input.host ?? null,
|
|
4420
|
+
input.limit ?? null,
|
|
4421
|
+
input.query ?? null,
|
|
4422
|
+
input.cursors === void 0 ? null : Object.entries(input.cursors).toSorted(([left], [right]) => left.localeCompare(right))
|
|
4423
|
+
]);
|
|
4424
|
+
return listCache.get(key, () => listUncached(input));
|
|
4425
|
+
};
|
|
4426
|
+
const listStats = (input) => {
|
|
4427
|
+
if (input.refs.length === 0) return Promise.resolve({ stats: [] });
|
|
4428
|
+
const key = JSON.stringify([
|
|
4429
|
+
listingsEpoch,
|
|
4430
|
+
input.refs.map((ref) => [ref.projectId, ref.repository, ref.number]).toSorted(
|
|
4431
|
+
(left, right) => `${left[0]} ${left[1]} ${left[2]}`.localeCompare(`${right[0]} ${right[1]} ${right[2]}`)
|
|
4432
|
+
)
|
|
4433
|
+
]);
|
|
4434
|
+
return listStatsCache.get(key, () => listStatsUncached(input));
|
|
4435
|
+
};
|
|
4436
|
+
const detail = (input) => detailCache.get(refKey(input), () => detailUncached(input));
|
|
4437
|
+
const activity = (input) => activityCache.get(refKey(input), () => activityUncached(input));
|
|
4438
|
+
const diff = (input) => {
|
|
4439
|
+
const key = JSON.stringify([
|
|
4440
|
+
refEpoch(input),
|
|
4441
|
+
input.projectId,
|
|
4442
|
+
input.repository,
|
|
4443
|
+
input.number,
|
|
4444
|
+
input.cursor ?? null,
|
|
4445
|
+
input.commit ?? null
|
|
4446
|
+
]);
|
|
4447
|
+
return staleDiff.get(key, () => diffCache.get(key, () => diffUncached(input)));
|
|
4448
|
+
};
|
|
4449
|
+
const invalidate = (input) => {
|
|
4450
|
+
if (input.reference === void 0) {
|
|
4451
|
+
listingsEpoch = ++epochCounter;
|
|
4452
|
+
viewersByHost.clear();
|
|
4453
|
+
return Promise.resolve(null);
|
|
4454
|
+
}
|
|
4455
|
+
bumpRefEpoch(input.reference);
|
|
4456
|
+
return Promise.resolve(null);
|
|
4457
|
+
};
|
|
4458
|
+
const afterMutation = (ref) => {
|
|
4459
|
+
bumpRefEpoch(ref);
|
|
4460
|
+
listingsEpoch = ++epochCounter;
|
|
4461
|
+
return null;
|
|
4462
|
+
};
|
|
4463
|
+
const { reviewerCandidates, act, converse } = makePullRequestWrites({
|
|
4464
|
+
requireProject,
|
|
4465
|
+
changeRequestRef,
|
|
4466
|
+
viewerPermissionsOf,
|
|
4467
|
+
afterMutation,
|
|
4468
|
+
options
|
|
4469
|
+
});
|
|
4444
4470
|
return {
|
|
4445
4471
|
list,
|
|
4446
4472
|
listStats,
|