@gitkraken/core-gitlens 0.9.4 → 0.9.5

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 (154) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/git/models/issue.d.ts +25 -2
  3. package/dist/git/models/issue.js +3 -1
  4. package/dist/git/models/issue.js.map +1 -1
  5. package/dist/git/models/pullRequest.d.ts +55 -5
  6. package/dist/git/models/pullRequest.js +5 -1
  7. package/dist/git/models/pullRequest.js.map +1 -1
  8. package/dist/git/utils/issue.utils.d.ts +4 -0
  9. package/dist/git/utils/issue.utils.js +4 -0
  10. package/dist/git/utils/issue.utils.js.map +1 -1
  11. package/dist/git/utils/pullRequest.utils.d.ts +4 -0
  12. package/dist/git/utils/pullRequest.utils.js +8 -0
  13. package/dist/git/utils/pullRequest.utils.js.map +1 -1
  14. package/dist/plus/git-github/api/github.d.ts +64 -4
  15. package/dist/plus/git-github/api/github.js +232 -15
  16. package/dist/plus/git-github/api/github.js.map +1 -1
  17. package/dist/plus/git-github/models.d.ts +48 -6
  18. package/dist/plus/git-github/models.js +11 -10
  19. package/dist/plus/git-github/models.js.map +1 -1
  20. package/dist/plus/integrations/fieldPresence.d.ts +75 -0
  21. package/dist/plus/integrations/fieldPresence.js +381 -0
  22. package/dist/plus/integrations/fieldPresence.js.map +1 -0
  23. package/dist/plus/integrations/index.d.ts +6 -4
  24. package/dist/plus/integrations/index.js +2 -1
  25. package/dist/plus/integrations/index.js.map +1 -1
  26. package/dist/plus/integrations/integrationService.d.ts +7 -3
  27. package/dist/plus/integrations/integrationService.js.map +1 -1
  28. package/dist/plus/integrations/manager.d.ts +56 -6
  29. package/dist/plus/integrations/models/gitHostIntegration.d.ts +103 -9
  30. package/dist/plus/integrations/models/gitHostIntegration.js +116 -5
  31. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  32. package/dist/plus/integrations/models/integration.d.ts +91 -4
  33. package/dist/plus/integrations/models/integration.js +56 -7
  34. package/dist/plus/integrations/models/integration.js.map +1 -1
  35. package/dist/plus/integrations/models/issuesIntegration.d.ts +44 -3
  36. package/dist/plus/integrations/models/issuesIntegration.js +51 -1
  37. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  38. package/dist/plus/integrations/providers/azure/azure.js +3 -3
  39. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  40. package/dist/plus/integrations/providers/azure/models.d.ts +36 -4
  41. package/dist/plus/integrations/providers/azure/models.js +118 -8
  42. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  43. package/dist/plus/integrations/providers/azureDevOps.d.ts +102 -5
  44. package/dist/plus/integrations/providers/azureDevOps.js +264 -45
  45. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  46. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +30 -6
  47. package/dist/plus/integrations/providers/azureDevOpsServer.js +82 -21
  48. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  49. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +21 -1
  50. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +74 -10
  51. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  52. package/dist/plus/integrations/providers/bitbucket/models.d.ts +40 -3
  53. package/dist/plus/integrations/providers/bitbucket/models.js +30 -7
  54. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  55. package/dist/plus/integrations/providers/bitbucket-server/models.d.ts +2 -6
  56. package/dist/plus/integrations/providers/bitbucket-server/models.js +1 -3
  57. package/dist/plus/integrations/providers/bitbucket-server/models.js.map +1 -1
  58. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.d.ts +3 -1
  59. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +4 -1
  60. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
  61. package/dist/plus/integrations/providers/bitbucket-server.d.ts +2 -2
  62. package/dist/plus/integrations/providers/bitbucket-server.js +7 -5
  63. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  64. package/dist/plus/integrations/providers/bitbucket.d.ts +22 -2
  65. package/dist/plus/integrations/providers/bitbucket.js +29 -6
  66. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  67. package/dist/plus/integrations/providers/github.d.ts +40 -3
  68. package/dist/plus/integrations/providers/github.js +133 -11
  69. package/dist/plus/integrations/providers/github.js.map +1 -1
  70. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +34 -1
  71. package/dist/plus/integrations/providers/gitlab/gitlab.js +118 -4
  72. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  73. package/dist/plus/integrations/providers/gitlab/models.d.ts +70 -5
  74. package/dist/plus/integrations/providers/gitlab/models.js +106 -13
  75. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  76. package/dist/plus/integrations/providers/gitlab.d.ts +31 -2
  77. package/dist/plus/integrations/providers/gitlab.js +48 -8
  78. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  79. package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
  80. package/dist/plus/integrations/providers/jira-server.d.ts +1 -0
  81. package/dist/plus/integrations/providers/jira-server.js +9 -6
  82. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  83. package/dist/plus/integrations/providers/jira.d.ts +20 -1
  84. package/dist/plus/integrations/providers/jira.js +101 -6
  85. package/dist/plus/integrations/providers/jira.js.map +1 -1
  86. package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +79 -0
  87. package/dist/plus/integrations/providers/jiraIssueByKey.js +58 -7
  88. package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
  89. package/dist/plus/integrations/providers/linear.d.ts +19 -1
  90. package/dist/plus/integrations/providers/linear.js +97 -5
  91. package/dist/plus/integrations/providers/linear.js.map +1 -1
  92. package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +42 -0
  93. package/dist/plus/integrations/providers/linearIssuesEtag.js +94 -0
  94. package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -0
  95. package/dist/plus/integrations/providers/models.d.ts +51 -7
  96. package/dist/plus/integrations/providers/models.js +45 -16
  97. package/dist/plus/integrations/providers/models.js.map +1 -1
  98. package/dist/plus/integrations/providers/providerErrors.js +8 -1
  99. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  100. package/dist/plus/integrations/providers/providersApi.d.ts +62 -3
  101. package/dist/plus/integrations/providers/providersApi.js +145 -4
  102. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  103. package/dist/plus/integrations/providers/pullRequestReviews.d.ts +13 -0
  104. package/dist/plus/integrations/providers/pullRequestReviews.js +30 -6
  105. package/dist/plus/integrations/providers/pullRequestReviews.js.map +1 -1
  106. package/dist/plus/integrations/providers/trello.js +10 -7
  107. package/dist/plus/integrations/providers/trello.js.map +1 -1
  108. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +11 -0
  109. package/dist/plus/integrations/providers/utils/providerPaging.js +47 -1
  110. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  111. package/dist/plus/integrations/providers/utils.d.ts +1 -1
  112. package/dist/plus/integrations/reads/batchEtags.d.ts +69 -0
  113. package/dist/plus/integrations/reads/batchEtags.js +114 -0
  114. package/dist/plus/integrations/reads/batchEtags.js.map +1 -0
  115. package/dist/plus/integrations/reads/counts.js +2 -2
  116. package/dist/plus/integrations/reads/counts.js.map +1 -1
  117. package/dist/plus/integrations/reads/currentAccount.js +2 -2
  118. package/dist/plus/integrations/reads/currentAccount.js.map +1 -1
  119. package/dist/plus/integrations/reads/drains.d.ts +13 -3
  120. package/dist/plus/integrations/reads/drains.js +29 -5
  121. package/dist/plus/integrations/reads/drains.js.map +1 -1
  122. package/dist/plus/integrations/reads/etag.d.ts +26 -0
  123. package/dist/plus/integrations/reads/etag.js +108 -0
  124. package/dist/plus/integrations/reads/etag.js.map +1 -0
  125. package/dist/plus/integrations/reads/hierarchy.js +3 -3
  126. package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
  127. package/dist/plus/integrations/reads/issueBatch.d.ts +47 -16
  128. package/dist/plus/integrations/reads/issueBatch.js +55 -87
  129. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  130. package/dist/plus/integrations/reads/issueTracker.d.ts +1 -1
  131. package/dist/plus/integrations/reads/issueTracker.js +4 -4
  132. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  133. package/dist/plus/integrations/reads/issues.d.ts +1 -1
  134. package/dist/plus/integrations/reads/issues.js +4 -4
  135. package/dist/plus/integrations/reads/issues.js.map +1 -1
  136. package/dist/plus/integrations/reads/pullRequestBatch.d.ts +44 -6
  137. package/dist/plus/integrations/reads/pullRequestBatch.js +31 -34
  138. package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -1
  139. package/dist/plus/integrations/reads/pullRequestBranches.d.ts +5 -2
  140. package/dist/plus/integrations/reads/pullRequestBranches.js +4 -3
  141. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
  142. package/dist/plus/integrations/reads/pullRequests.js +8 -4
  143. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  144. package/dist/plus/integrations/reads/sweeps.js +8 -4
  145. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  146. package/dist/plus/integrations/reads/warnings.d.ts +23 -8
  147. package/dist/plus/integrations/reads/warnings.js +43 -19
  148. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  149. package/dist/plus/integrations/results.d.ts +12 -4
  150. package/dist/plus/integrations/results.js +2 -0
  151. package/dist/plus/integrations/results.js.map +1 -1
  152. package/docs/integrations.md +385 -23
  153. package/docs/kepler-read-api-parity.md +16 -2
  154. package/package.json +1 -1
@@ -1,9 +1,10 @@
1
1
  import { ResourceDescriptor } from "../../../../git/models/resourceDescriptor.js";
2
2
  import { Provider } from "../../../../git/models/remoteProvider.js";
3
3
  import { IssueOrPullRequestState } from "../../../../git/models/issueOrPullRequest.js";
4
- import { Issue } from "../../../../git/models/issue.js";
5
- import { PullRequest, PullRequestMember, PullRequestMergeableState, PullRequestReviewDecision, PullRequestReviewState, PullRequestReviewer } from "../../../../git/models/pullRequest.js";
4
+ import { Issue, IssueProjection } from "../../../../git/models/issue.js";
5
+ import { PullRequest, PullRequestMember, PullRequestMergeableState, PullRequestProjection, PullRequestReviewDecision, PullRequestReviewState, PullRequestReviewer } from "../../../../git/models/pullRequest.js";
6
6
  import { ProviderIssue } from "../models.js";
7
+ import { IssueEtagFields, PullRequestEtagFields, PullRequestEtagInclude } from "../../models/integration.js";
7
8
  //#region ../plus/integrations/src/providers/azure/models.d.ts
8
9
  export declare const vstsHostnameSuffix = ".visualstudio.com";
9
10
  export interface AzureRepositoryDescriptor extends ResourceDescriptor {
@@ -332,8 +333,8 @@ export interface AzureForkRepositoryUrls {
332
333
  * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from
333
334
  * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.
334
335
  */
335
- export declare function fromAzurePullRequest(pr: AzurePullRequest, provider: Provider, owner: string, baseUrl: string, forkRepositoryUrls: AzureForkRepositoryUrls | undefined): PullRequest;
336
- export declare function fromAzureWorkItem(workItem: WorkItem, provider: Provider, project: AzureProjectDescriptor, stateCategory?: AzureWorkItemStateCategory): Issue;
336
+ export declare function fromAzurePullRequest(pr: AzurePullRequest, provider: Provider, owner: string, baseUrl: string, forkRepositoryUrls: AzureForkRepositoryUrls | undefined, projection?: PullRequestProjection): PullRequest;
337
+ export declare function fromAzureWorkItem(workItem: WorkItem, provider: Provider, project: AzureProjectDescriptor, stateCategory?: AzureWorkItemStateCategory, projection?: IssueProjection): Issue;
337
338
  /**
338
339
  * A work item as `GET …/_apis/wit/workitems/{id}?$expand=Links` returns it. Loosely typed because
339
340
  * {@link fromAzureWorkItemToProviderIssue} checks every field it reads, as provider-apis does.
@@ -357,5 +358,36 @@ export interface AzureWorkItemResponse {
357
358
  * `state.name` is the raw `System.State`, as on every list read here, which passes the SDK no state-name map.
358
359
  */
359
360
  export declare function fromAzureWorkItemToProviderIssue(workItem: AzureWorkItemResponse, namespace: string, project: string): ProviderIssue | undefined;
361
+ /**
362
+ * The most ids one cheap work item read asks for: the most Azure DevOps' work item list (`GET …/_apis/wit/workitems`)
363
+ * takes.
364
+ */
365
+ export declare const azureWorkItemsEtagFieldsMaxIds = 200;
366
+ /**
367
+ * The fields the cheap work item read selects: only the ones a full row's etag reads, and the project the full read
368
+ * checks the work item against. Not `System.State`: {@link fromAzureWorkItemToProviderIssue} gives the state no
369
+ * category, so `toIssueShape` closes a work item by its closed date alone.
370
+ */
371
+ export declare const azureWorkItemEtagFieldNames: readonly string[];
372
+ /**
373
+ * A cheap read's work item, as the fields its full batch row's etag reads: `toIssueShape` closes the row the full
374
+ * read builds (see {@link fromAzureWorkItemToProviderIssue}) exactly when it has a closed date, parsed by the same rule.
375
+ * Throws where the full read fails the work item: on one in another project than `project` (compared as Azure
376
+ * compares names), and on a missing or unparseable change date, so the target falls through to the full read.
377
+ */
378
+ export declare function toAzureWorkItemEtagFields(workItem: AzureWorkItemResponse, project: string): IssueEtagFields;
379
+ /**
380
+ * A cheap read's pull request (the same `GET …/pullrequests/{id}` provider-apis sends), as the fields its full batch
381
+ * row's etag reads: provider-apis' mapping (see the tables above), then `fromProviderPullRequest`'s. Azure DevOps
382
+ * reports no update time, so provider-apis' `updatedDate` is the close time, else the creation time, which is why
383
+ * the fields Azure changes without one are fingerprinted into the `revision`, from the values the full row ends up
384
+ * with: provider-apis' title, description (`?? null`, which the row reads `?? undefined`) and target branch, and its
385
+ * reviews, split into requests and completed reviews by `fromProviderPullRequest`'s own helpers.
386
+ *
387
+ * Throws where provider-apis' mapping throws, on a field it reads without a guard, so a pull request the full read
388
+ * fails on falls through to it rather than matching an etag. A full row never carries a check rollup (provider-apis
389
+ * maps none for Azure DevOps), so the `checks` include always reads `undefined`, as there.
390
+ */
391
+ export declare function toAzurePullRequestEtagFields(pr: AzurePullRequest, etagIncludes: readonly PullRequestEtagInclude[]): PullRequestEtagFields;
360
392
  //#endregion
361
393
  //# sourceMappingURL=models.d.ts.map
@@ -1,5 +1,8 @@
1
1
  import { Issue } from "../../../../git/models/issue.js";
2
2
  import { PullRequest } from "../../../../git/models/pullRequest.js";
3
+ import { decideProviderReviewDecision, fromPullRequestReviewDecision, toCompletedReviews, toReviewRequests } from "../pullRequestReviews.js";
4
+ import { GitPullRequestMergeableState, GitPullRequestReviewState, GitPullRequestState, fromProviderPullRequestMergeableState, fromProviderPullRequestState } from "../models.js";
5
+ import { pullRequestRevision } from "../../models/integration.js";
3
6
  //#region ../plus/integrations/src/providers/azure/models.ts
4
7
  const vstsHostnameSuffix = ".visualstudio.com";
5
8
  function isClosedAzureWorkItemStateCategory(category) {
@@ -186,7 +189,7 @@ function fromAzureUserToMember(user, _type) {
186
189
  * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from
187
190
  * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.
188
191
  */
189
- function fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls) {
192
+ function fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls, projection) {
190
193
  const baseRepositoryUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);
191
194
  const baseCloneHttps = getAzureRepositoryCloneUrl(pr.repository, baseUrl, owner);
192
195
  const forkRepository = pr.forkSource?.repository;
@@ -195,8 +198,7 @@ function fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls)
195
198
  return new PullRequest(provider, fromAzureUserToMember(pr.createdBy, "pullRequest"), pr.pullRequestId.toString(), pr.pullRequestId.toString(), pr.title, getAzurePullRequestWebUrl(pr, baseUrl, owner), {
196
199
  owner,
197
200
  repo: pr.repository.name,
198
- id: pr.repository.id,
199
- accessLevel: 30
201
+ id: pr.repository.id
200
202
  }, azurePullRequestStatusToState(pr.status), new Date(pr.creationDate), new Date(pr.closedDate || pr.creationDate), pr.closedDate ? new Date(pr.closedDate) : void 0, pr.closedDate && pr.status === "completed" ? new Date(pr.closedDate) : void 0, fromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? "notSet"), void 0, {
201
203
  base: {
202
204
  branch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : "",
@@ -222,10 +224,10 @@ function fromAzurePullRequest(pr, provider, owner, baseUrl, forkRepositoryUrls)
222
224
  name: pr.repository.project.name,
223
225
  resourceId: "",
224
226
  resourceName: owner
225
- });
227
+ }, void 0, void 0, void 0, void 0, void 0, void 0, void 0, projection);
226
228
  }
227
- function fromAzureWorkItem(workItem, provider, project, stateCategory) {
228
- return new Issue(provider, workItem.id.toString(), workItem.id.toString(), workItem.fields["System.Title"], workItem._links.html.href, new Date(workItem.fields["System.CreatedDate"]), new Date(workItem.fields["System.ChangedDate"]), isClosedAzureWorkItemStateCategory(stateCategory), azureWorkItemsStateCategoryToState(stateCategory), fromAzureUserToMember(workItem.fields["System.CreatedBy"], "issue"), workItem.fields["System.AssignedTo"] != null ? [fromAzureUserToMember(workItem.fields["System.AssignedTo"], "issue")] : [], void 0, workItem.fields["Microsoft.VSTS.Common.ClosedDate"] ? new Date(workItem.fields["Microsoft.VSTS.Common.ClosedDate"]) : void 0, void 0, workItem.fields["System.CommentCount"], void 0, workItem.fields["System.Description"], project, void 0, void 0, void 0, void 0, toWorkItemIterations(workItem.fields["System.IterationPath"]));
229
+ function fromAzureWorkItem(workItem, provider, project, stateCategory, projection) {
230
+ return new Issue(provider, workItem.id.toString(), workItem.id.toString(), workItem.fields["System.Title"], workItem._links.html.href, new Date(workItem.fields["System.CreatedDate"]), new Date(workItem.fields["System.ChangedDate"]), isClosedAzureWorkItemStateCategory(stateCategory), azureWorkItemsStateCategoryToState(stateCategory), fromAzureUserToMember(workItem.fields["System.CreatedBy"], "issue"), workItem.fields["System.AssignedTo"] != null ? [fromAzureUserToMember(workItem.fields["System.AssignedTo"], "issue")] : [], void 0, workItem.fields["Microsoft.VSTS.Common.ClosedDate"] ? new Date(workItem.fields["Microsoft.VSTS.Common.ClosedDate"]) : void 0, void 0, workItem.fields["System.CommentCount"], void 0, workItem.fields["System.Description"], project, void 0, void 0, void 0, void 0, toWorkItemIterations(workItem.fields["System.IterationPath"]), projection);
229
231
  }
230
232
  /**
231
233
  * Azure reports the iteration as a backslash-delimited path rooted at the project. The project root is its default
@@ -294,7 +296,7 @@ function fromAzureWorkItemToProviderIssue(workItem, namespace, project) {
294
296
  key: null,
295
297
  id: null
296
298
  },
297
- upvoteCount: 0,
299
+ upvoteCount: null,
298
300
  labels: (typeof tags === "string" ? tags.split(";") : []).map((tag) => ({
299
301
  color: null,
300
302
  description: null,
@@ -349,7 +351,115 @@ function toAzureWorkItemIteration(path) {
349
351
  name: iteration.name
350
352
  } : void 0;
351
353
  }
354
+ /**
355
+ * The most ids one cheap work item read asks for: the most Azure DevOps' work item list (`GET …/_apis/wit/workitems`)
356
+ * takes.
357
+ */
358
+ const azureWorkItemsEtagFieldsMaxIds = 200;
359
+ /**
360
+ * The fields the cheap work item read selects: only the ones a full row's etag reads, and the project the full read
361
+ * checks the work item against. Not `System.State`: {@link fromAzureWorkItemToProviderIssue} gives the state no
362
+ * category, so `toIssueShape` closes a work item by its closed date alone.
363
+ */
364
+ const azureWorkItemEtagFieldNames = [
365
+ "System.TeamProject",
366
+ "System.ChangedDate",
367
+ "Microsoft.VSTS.Common.ClosedDate"
368
+ ];
369
+ /**
370
+ * A cheap read's work item, as the fields its full batch row's etag reads: `toIssueShape` closes the row the full
371
+ * read builds (see {@link fromAzureWorkItemToProviderIssue}) exactly when it has a closed date, parsed by the same rule.
372
+ * Throws where the full read fails the work item: on one in another project than `project` (compared as Azure
373
+ * compares names), and on a missing or unparseable change date, so the target falls through to the full read.
374
+ */
375
+ function toAzureWorkItemEtagFields(workItem, project) {
376
+ const fields = workItem.fields;
377
+ const teamProject = fields?.["System.TeamProject"];
378
+ if (typeof teamProject === "string" && teamProject.toLowerCase() !== project.toLowerCase()) throw new Error(`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`);
379
+ const updatedDate = parseAzureWorkItemDate(fields?.["System.ChangedDate"]);
380
+ if (updatedDate == null) throw new Error(`Azure DevOps returned work item ${String(workItem.id)} without a change date`);
381
+ return {
382
+ state: parseAzureWorkItemDate(fields?.["Microsoft.VSTS.Common.ClosedDate"]) != null ? "closed" : "opened",
383
+ updatedDate
384
+ };
385
+ }
386
+ /** provider-apis' pull request `status` map (`bi`). It has no `notSet`, which therefore maps to `undefined`. */
387
+ const azurePullRequestProviderStates = {
388
+ active: GitPullRequestState.Open,
389
+ completed: GitPullRequestState.Merged,
390
+ abandoned: GitPullRequestState.Closed
391
+ };
392
+ /** provider-apis' `mergeStatus` map (`Ti`); any other status, or none, is unknown. */
393
+ const azureMergeStatusProviderStates = {
394
+ conflicts: GitPullRequestMergeableState.Conflicts,
395
+ failure: GitPullRequestMergeableState.FailingChecks,
396
+ rejectedByPolicy: GitPullRequestMergeableState.Blocked,
397
+ succeeded: GitPullRequestMergeableState.Mergeable
398
+ };
399
+ /** provider-apis' reviewer `vote` map (`Di`); any other vote reads as review requested. */
400
+ const azureVoteProviderReviewStates = {
401
+ 10: GitPullRequestReviewState.Approved,
402
+ 5: GitPullRequestReviewState.Approved,
403
+ 0: GitPullRequestReviewState.ReviewRequested,
404
+ [-5]: GitPullRequestReviewState.ChangesRequested,
405
+ [-10]: GitPullRequestReviewState.ChangesRequested
406
+ };
407
+ /** provider-apis' reviewer mapping (`ve`), with the state its vote maps to: every reviewer, required or optional. */
408
+ function toAzureProviderReview(reviewer) {
409
+ return {
410
+ reviewer: {
411
+ id: reviewer.id,
412
+ name: reviewer.displayName ?? null,
413
+ username: (reviewer.uniqueName || reviewer.displayName) ?? null,
414
+ email: null,
415
+ avatarUrl: reviewer.imageUrl ?? null,
416
+ url: null
417
+ },
418
+ state: toAzureProviderReviewState(reviewer)
419
+ };
420
+ }
421
+ function toAzureProviderReviewState(reviewer) {
422
+ return azureVoteProviderReviewStates[reviewer.vote ?? 0] ?? GitPullRequestReviewState.ReviewRequested;
423
+ }
424
+ /**
425
+ * A cheap read's pull request (the same `GET …/pullrequests/{id}` provider-apis sends), as the fields its full batch
426
+ * row's etag reads: provider-apis' mapping (see the tables above), then `fromProviderPullRequest`'s. Azure DevOps
427
+ * reports no update time, so provider-apis' `updatedDate` is the close time, else the creation time, which is why
428
+ * the fields Azure changes without one are fingerprinted into the `revision`, from the values the full row ends up
429
+ * with: provider-apis' title, description (`?? null`, which the row reads `?? undefined`) and target branch, and its
430
+ * reviews, split into requests and completed reviews by `fromProviderPullRequest`'s own helpers.
431
+ *
432
+ * Throws where provider-apis' mapping throws, on a field it reads without a guard, so a pull request the full read
433
+ * fails on falls through to it rather than matching an etag. A full row never carries a check rollup (provider-apis
434
+ * maps none for Azure DevOps), so the `checks` include always reads `undefined`, as there.
435
+ */
436
+ function toAzurePullRequestEtagFields(pr, etagIncludes) {
437
+ if (pr.pullRequestId == null || pr.createdBy == null || pr.repository?.project == null || pr.lastMergeSourceCommit == null || pr.lastMergeTargetCommit == null || !Array.isArray(pr.reviewers) || pr.reviewers.some((r) => r == null) || typeof pr.url !== "string" || typeof pr.sourceRefName !== "string" || typeof pr.targetRefName !== "string") throw new Error(`Azure DevOps returned pull request ${String(pr.pullRequestId)} without a field it maps`);
438
+ const reviews = pr.reviewers.map(toAzureProviderReview);
439
+ const fields = {
440
+ state: fromProviderPullRequestState(azurePullRequestProviderStates[pr.status]),
441
+ isDraft: pr.isDraft,
442
+ updatedDate: new Date(pr.closedDate || pr.creationDate),
443
+ headSha: pr.lastMergeSourceCommit.commitId ?? "",
444
+ revision: pullRequestRevision({
445
+ title: pr.title,
446
+ body: pr.description ?? void 0,
447
+ refs: { base: { branch: normalizeAzureBranchName(pr.targetRefName) } },
448
+ reviewRequests: toReviewRequests(reviews),
449
+ latestReviews: toCompletedReviews(reviews)
450
+ })
451
+ };
452
+ if (etagIncludes.includes("mergeable")) {
453
+ const mergeable = (pr.mergeStatus != null ? azureMergeStatusProviderStates[pr.mergeStatus] : void 0) ?? GitPullRequestMergeableState.Unknown;
454
+ fields.mergeableState = fromProviderPullRequestMergeableState[mergeable];
455
+ }
456
+ if (etagIncludes.includes("reviewDecision")) {
457
+ const decision = decideProviderReviewDecision(pr.reviewers.filter((r) => r.isRequired).map(toAzureProviderReviewState));
458
+ fields.reviewDecision = decision ? fromPullRequestReviewDecision[decision] : void 0;
459
+ }
460
+ return fields;
461
+ }
352
462
  //#endregion
353
- export { azurePullRequestStatusToState, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzurePullRequestMergeStatusToMergeableState, fromAzurePullRequestVoteToReviewState, fromAzureReviewerToPullRequestMember, fromAzureUserWithVoteToReviewer, fromAzureWorkItem, fromAzureWorkItemToProviderIssue, getAzureDevOpsOwner, getAzurePullRequestReviewDecision, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, isVsts, normalizeAzureBranchName, sanitizeAzureRepositoryUrl, vstsHostnameSuffix };
463
+ export { azurePullRequestStatusToState, azureWorkItemEtagFieldNames, azureWorkItemsEtagFieldsMaxIds, azureWorkItemsStateCategoryToState, encodeAzurePathSegment, fromAzurePullRequest, fromAzurePullRequestMergeStatusToMergeableState, fromAzurePullRequestVoteToReviewState, fromAzureReviewerToPullRequestMember, fromAzureUserWithVoteToReviewer, fromAzureWorkItem, fromAzureWorkItemToProviderIssue, getAzureDevOpsOwner, getAzurePullRequestReviewDecision, getAzurePullRequestWebUrl, isClosedAzurePullRequestStatus, isClosedAzureWorkItemStateCategory, isVsts, normalizeAzureBranchName, sanitizeAzureRepositoryUrl, toAzurePullRequestEtagFields, toAzureWorkItemEtagFields, vstsHostnameSuffix };
354
464
 
355
465
  //# sourceMappingURL=models.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { IssueIteration, IssueMember } from '@gitlens/git/models/issue.js';\nimport { Issue, RepositoryAccessLevel } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\n\nexport const vstsHostnameSuffix = '.visualstudio.com';\n\nexport interface AzureRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n\tproject?: string;\n\tvirtualDirectory?: string;\n}\n\nexport interface AzureOrganizationDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n}\n\nexport interface AzureProjectDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface AzureRemoteRepositoryDescriptor extends ResourceDescriptor {\n\tid: string;\n\tnodeId?: string;\n\tresourceName: string;\n\tname: string;\n\tprojectName?: string;\n\turl?: string;\n\tcloneUrlHttps?: string;\n\tcloneUrlSsh?: string;\n}\n\nexport interface AzureProjectInputDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport type AzureWorkItemStateCategory = 'Proposed' | 'InProgress' | 'Resolved' | 'Completed' | 'Removed';\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn category === 'Completed' || category === 'Resolved' || category === 'Removed';\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueOrPullRequestState {\n\tswitch (category) {\n\t\tcase 'Resolved':\n\t\tcase 'Completed':\n\t\tcase 'Removed':\n\t\t\treturn 'closed';\n\t\tcase 'Proposed':\n\t\tcase 'InProgress':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\n/**\n * Encodes one route segment of an Azure DevOps request (#5878): `encodeURIComponent` leaves a dot segment as is, and\n * the URL would resolve it away, re-scoping the request instead of failing it.\n */\nexport function encodeAzurePathSegment(value: string): string {\n\tif (value === '.' || value === '..') throw new Error(`Invalid Azure path segment '${value}'.`);\n\n\treturn encodeURIComponent(value);\n}\n\n/**\n * `baseUrl` and `owner` are the authoritative prefix used for the API request. The payload URL cannot supply that\n * prefix safely because it may address the repository by id or name an untrusted host.\n */\nfunction getAzureRepositoryWebUrl(baseUrl: string, owner: string, projectName: string, repoName: string): string {\n\tconst repoPath = `${encodeURIComponent(owner)}/${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${repoPath}`;\n}\n\nexport function getAzurePullRequestWebUrl(pr: AzurePullRequest, baseUrl: string, owner: string): string {\n\tconst repoUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\treturn `${repoUrl}/pullrequest/${pr.pullRequestId}`;\n}\n\n/**\n * Whether `url` and `expected` are the two cloud spellings of one organization: a cloud organization answers on both\n * `dev.azure.com/{owner}` and the legacy `{owner}.visualstudio.com`, so those name the same host. Anything else is\n * somewhere we didn't ask.\n */\nfunction isSameAzureCloudOrganization(url: URL, expected: URL, expectedOwner: string): boolean {\n\tif (url.protocol !== expected.protocol) return false;\n\n\tconst owner = expectedOwner.toLowerCase();\n\tif (expected.hostname !== 'dev.azure.com' && !isVsts(expected.hostname)) return false;\n\n\tif (url.hostname === 'dev.azure.com') return getAzureDevOpsOwner(url).toLowerCase() === owner;\n\n\treturn url.hostname === `${owner}${vstsHostnameSuffix}`;\n}\n\n/**\n * Whether `url` sits under the collection the integration is configured to talk to — `{expectedUrl}/{expectedOwner}`.\n * A matching origin says nothing on its own: Azure organizations and self-hosted collections are path segments.\n */\nfunction isUnderAzureCollection(url: URL, expected: URL, expectedOwner: string): boolean {\n\tconst base = expected.pathname.replace(/\\/+$/, '');\n\tconst prefix = `${base}/${encodeURIComponent(expectedOwner)}/`.toLowerCase();\n\treturn `${url.pathname}/`.toLowerCase().startsWith(prefix);\n}\n\n/**\n * Restricts a provider-supplied repository URL to the integration's collection and removes credentials, query, and\n * fragment before a consumer can use it as a git remote. Takes the value as the payloads carry it — every one of\n * these URLs is optional — so a caller never has to spell the absent case itself.\n */\nexport function sanitizeAzureRepositoryUrl(\n\tvalue: string | undefined,\n\texpectedUrl: string,\n\texpectedOwner: string,\n): string | undefined {\n\tif (value == null) return undefined;\n\n\tlet expected: URL;\n\tlet url: URL;\n\ttry {\n\t\texpected = new URL(expectedUrl);\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (expected.protocol !== 'https:' && expected.protocol !== 'http:') return undefined;\n\tif (!expectedOwner) return undefined;\n\n\tif (url.origin === expected.origin) {\n\t\tif (!isUnderAzureCollection(url, expected, expectedOwner)) return undefined;\n\t} else if (!isSameAzureCloudOrganization(url, expected, expectedOwner)) {\n\t\treturn undefined;\n\t}\n\n\turl.username = '';\n\turl.password = '';\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\n/**\n * The clone URL the payload carries for a repository, restricted to the integration's collection and then\n * cross-checked against the repository that payload names.\n *\n * The collection check alone is not enough for a URL that is READ rather than built. Every other repository URL the\n * model reports is built from the authoritative prefix, so the payload cannot move it; this one cannot be built,\n * because Azure spells a repository for git the same way it spells it for the web only by convention. A URL under\n * the right collection may still name a DIFFERENT repository in it, which would leave `cloneHttps` describing one\n * repository while `url` describes another.\n *\n * Azure ends a clone URL with `_git/{repo}` on every host style — including the short `{owner}/_git/{repo}` form it\n * uses when a repository carries its project's name, and the legacy `{owner}.visualstudio.com` spelling that has no\n * organization segment at all — so the repository name is the one part comparable across all of them. A same-named\n * repository in another project of the same organization is the single substitution this cannot catch.\n */\nfunction getAzureRepositoryCloneUrl(\n\trepository: AzureRepositoryReference,\n\tbaseUrl: string,\n\towner: string,\n): string | undefined {\n\tconst sanitized = sanitizeAzureRepositoryUrl(repository.remoteUrl, baseUrl, owner);\n\tif (sanitized == null) return undefined;\n\n\t// Safe to parse: `sanitizeAzureRepositoryUrl` returns what it already parsed.\n\tconst segments = new URL(sanitized).pathname.split('/').filter(s => s.length > 0);\n\tconst name = segments.pop();\n\tif (name == null || segments.pop() !== '_git') return undefined;\n\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(name);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Azure resolves project and repository names case-insensitively, so comparing them any other way would refuse a\n\t// URL the provider considers the same one.\n\treturn decoded.toLowerCase() === repository.name.toLowerCase() ? sanitized : undefined;\n}\n\nexport function fromAzurePullRequestMergeStatusToMergeableState(\n\tmergeStatus: AzurePullRequestAsyncStatus,\n): PullRequestMergeableState {\n\tswitch (mergeStatus) {\n\t\tcase 'conflicts':\n\t\t\treturn PullRequestMergeableState.Conflicting;\n\t\tcase 'failure':\n\t\t\treturn PullRequestMergeableState.FailingChecks;\n\t\tcase 'rejectedByPolicy':\n\t\t\treturn PullRequestMergeableState.BlockedByPolicy;\n\t\tcase 'succeeded':\n\t\t\treturn PullRequestMergeableState.Mergeable;\n\t\tcase 'notSet':\n\t\tcase 'queued':\n\t\tdefault:\n\t\t\treturn PullRequestMergeableState.Unknown;\n\t}\n}\n\nexport function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {\n\tswitch (vote) {\n\t\tcase 10:\n\t\tcase 5:\n\t\t\treturn PullRequestReviewState.Approved;\n\t\tcase 0:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t\tcase -5:\n\t\tcase -10:\n\t\t\treturn PullRequestReviewState.ChangesRequested;\n\t\tdefault:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t}\n}\n\nexport function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {\n\treturn {\n\t\tisCodeOwner: undefined,\n\t\treviewer: {\n\t\t\tavatarUrl: reviewer.imageUrl,\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName,\n\t\t\tusername: reviewer.uniqueName,\n\t\t\turl: reviewer.url,\n\t\t},\n\t\tstate: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),\n\t};\n}\n\nexport function getAzurePullRequestReviewDecision(\n\tvotes: AzurePullRequestVote[],\n): PullRequestReviewDecision | undefined {\n\tconst reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));\n\tif (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {\n\t\treturn PullRequestReviewDecision.ChangesRequested;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.Approved)) {\n\t\treturn PullRequestReviewDecision.Approved;\n\t}\n\n\treturn undefined;\n}\n\nexport function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: reviewer.imageUrl,\n\t\tid: reviewer.id,\n\t\tname: reviewer.displayName,\n\t\tusername: reviewer.uniqueName,\n\t\turl: reviewer.url,\n\t};\n}\n\nexport function normalizeAzureBranchName(branchName: string): string {\n\treturn branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;\n}\n\nfunction fromAzureUserToMember(user: AzureUser, type: 'issue'): IssueMember;\nfunction fromAzureUserToMember(user: AzureUser, type: 'pullRequest'): PullRequestMember;\nfunction fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'): PullRequestMember | IssueMember {\n\treturn {\n\t\tavatarUrl: user.imageUrl,\n\t\tid: user.id,\n\t\tname: user.displayName,\n\t\tusername: user.uniqueName,\n\t\turl: user.url,\n\t};\n}\n\n/**\n * The URLs of a cross-repository pull request's fork, resolved by a lookup because the pull request payload embeds\n * only an abbreviated reference to it: `url` is the URL the head ref reports — the fork's web URL, or its clone URL\n * when the response carries no web URL — and `cloneHttps` its HTTPS clone URL.\n *\n * A fork that resolves neither is reported as no fork at all rather than as an object with nothing in it, so `url`\n * is always present here; `cloneHttps` alone is best-effort, and is absent when the response omits it or its URL\n * sits outside the integration's collection.\n */\nexport interface AzureForkRepositoryUrls {\n\turl: string;\n\tcloneHttps: string | undefined;\n}\n\n/**\n * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from\n * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.\n */\nexport function fromAzurePullRequest(\n\tpr: AzurePullRequest,\n\tprovider: Provider,\n\towner: string,\n\tbaseUrl: string,\n\tforkRepositoryUrls: AzureForkRepositoryUrls | undefined,\n): PullRequest {\n\tconst baseRepositoryUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\tconst baseCloneHttps = getAzureRepositoryCloneUrl(pr.repository, baseUrl, owner);\n\n\tconst forkRepository = pr.forkSource?.repository;\n\tconst headRepositoryUrl = forkRepository == null ? baseRepositoryUrl : forkRepositoryUrls?.url;\n\tconst headCloneHttps = forkRepository == null ? baseCloneHttps : forkRepositoryUrls?.cloneHttps;\n\n\treturn new PullRequest(\n\t\tprovider,\n\t\tfromAzureUserToMember(pr.createdBy, 'pullRequest'),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.title,\n\t\tgetAzurePullRequestWebUrl(pr, baseUrl, owner),\n\t\t{\n\t\t\towner: owner,\n\t\t\trepo: pr.repository.name,\n\t\t\tid: pr.repository.id,\n\t\t\t// TODO: Remove this assumption once actual access level is available\n\t\t\taccessLevel: RepositoryAccessLevel.Write,\n\t\t},\n\t\tazurePullRequestStatusToState(pr.status),\n\t\tnew Date(pr.creationDate),\n\t\tnew Date(pr.closedDate || pr.creationDate),\n\t\tpr.closedDate ? new Date(pr.closedDate) : undefined,\n\t\tpr.closedDate && pr.status === 'completed' ? new Date(pr.closedDate) : undefined,\n\t\tfromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? 'notSet'),\n\t\tundefined,\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : '',\n\t\t\t\tsha: pr.lastMergeTargetCommit?.commitId ?? '',\n\t\t\t\trepo: pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.targetRefName != null,\n\t\t\t\turl: baseRepositoryUrl,\n\t\t\t\tcloneHttps: baseCloneHttps,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.sourceRefName ? normalizeAzureBranchName(pr.sourceRefName) : '',\n\t\t\t\tsha: pr.lastMergeSourceCommit?.commitId ?? '',\n\t\t\t\trepo: forkRepository?.name ?? pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.sourceRefName != null,\n\t\t\t\turl: headRepositoryUrl,\n\t\t\t\tcloneHttps: headCloneHttps,\n\t\t\t},\n\t\t\tisCrossRepository: pr.forkSource != null,\n\t\t},\n\t\tpr.isDraft,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tgetAzurePullRequestReviewDecision(pr.reviewers?.filter(r => r.isRequired).map(r => r.vote ?? 0) ?? []),\n\t\tpr.reviewers.filter(r => r.vote == null || r.vote === 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.filter(r => r.vote != null && r.vote !== 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.map(r => fromAzureReviewerToPullRequestMember(r)),\n\t\tundefined,\n\t\t{\n\t\t\tid: pr.repository?.project?.id,\n\t\t\tname: pr.repository.project.name,\n\t\t\tresourceId: '', // TODO: This is a workaround until we can get the org id here.\n\t\t\tresourceName: owner,\n\t\t},\n\t);\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t);\n}\n\n/**\n * Azure reports the iteration as a backslash-delimited path rooted at the project. The project root is its default\n * for a work item with no sprint, so only a nested path names one; the path is the identity because Azure supplies\n * no iteration id here.\n *\n * Mirrors `normalizeIteration` in provider-apis so the same work item yields the same identity whether it is read\n * here or through the SDK. That contract keeps the path **verbatim** — it is what the iteration is matched back by,\n * so it is deliberately not trimmed — and trims only the display name.\n */\nfunction toWorkItemIterations(path: string | undefined): IssueIteration[] | undefined {\n\tif (!path) return undefined;\n\n\tconst segments = path.split('\\\\');\n\tif (segments.length < 2) return undefined;\n\n\tconst name = segments.at(-1)?.trim();\n\treturn name ? [{ id: path, name: name }] : undefined;\n}\n\n/**\n * A work item as `GET …/_apis/wit/workitems/{id}?$expand=Links` returns it. Loosely typed because\n * {@link fromAzureWorkItemToProviderIssue} checks every field it reads, as provider-apis does.\n */\nexport interface AzureWorkItemResponse {\n\tid?: unknown;\n\tfields?: Record<string, unknown>;\n\t_links?: { html?: { href?: unknown } };\n}\n\n/**\n * Converts a work item exactly as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work\n * item read by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither\n * that converter nor a single work item read, so this mirrors it field for field; the batch issue tests compare\n * the two, so a provider-apis change that this misses fails there.\n *\n * `undefined` for a work item the SDK would skip: no positive integer id, no title, or no valid created date.\n * `state.name` is the raw `System.State`, as on every list read here, which passes the SDK no state-name map.\n */\nexport function fromAzureWorkItemToProviderIssue(\n\tworkItem: AzureWorkItemResponse,\n\tnamespace: string,\n\tproject: string,\n): ProviderIssue | undefined {\n\tconst fields = workItem.fields;\n\tconst id = workItem.id;\n\tconst title = fields?.['System.Title'];\n\tconst createdDate = parseAzureWorkItemDate(fields?.['System.CreatedDate']);\n\tif (\n\t\tfields == null ||\n\t\ttypeof id !== 'number' ||\n\t\t!Number.isSafeInteger(id) ||\n\t\tid <= 0 ||\n\t\ttypeof title !== 'string' ||\n\t\t!title.trim() ||\n\t\tcreatedDate == null\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst assignee = fromAzureWorkItemIdentity(fields['System.AssignedTo']);\n\tconst commentCount = fields['System.CommentCount'];\n\tconst url = workItem._links?.html?.href;\n\tconst tags = fields['System.Tags'];\n\tconst state = fields['System.State'];\n\tconst type = fields['System.WorkItemType'];\n\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\tupvoteCount: 0,\n\t\tlabels: (typeof tags === 'string' ? tags.split(';') : []).map(tag => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: tag.trim(),\n\t\t})),\n\t};\n}\n\n/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"models.js","names":[],"sources":["../../../../../../plus/integrations/src/providers/azure/models.ts"],"sourcesContent":["import type { IssueIteration, IssueMember, IssueProjection } from '@gitlens/git/models/issue.js';\nimport { Issue } from '@gitlens/git/models/issue.js';\nimport type { IssueOrPullRequestState } from '@gitlens/git/models/issueOrPullRequest.js';\nimport type { PullRequestMember, PullRequestProjection, PullRequestReviewer } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tPullRequest,\n\tPullRequestMergeableState,\n\tPullRequestReviewDecision,\n\tPullRequestReviewState,\n} from '@gitlens/git/models/pullRequest.js';\nimport type { Provider } from '@gitlens/git/models/remoteProvider.js';\nimport type { ResourceDescriptor } from '@gitlens/git/models/resourceDescriptor.js';\nimport type { IssueEtagFields, PullRequestEtagFields, PullRequestEtagInclude } from '../../models/integration.js';\nimport { pullRequestRevision } from '../../models/integration.js';\nimport type { ProviderAccount, ProviderIssue } from '../models.js';\nimport {\n\tfromProviderPullRequestMergeableState,\n\tfromProviderPullRequestState,\n\tGitPullRequestMergeableState,\n\tGitPullRequestReviewState,\n\tGitPullRequestState,\n} from '../models.js';\nimport type { ProviderPullRequestReview } from '../pullRequestReviews.js';\nimport {\n\tdecideProviderReviewDecision,\n\tfromPullRequestReviewDecision,\n\ttoCompletedReviews,\n\ttoReviewRequests,\n} from '../pullRequestReviews.js';\n\nexport const vstsHostnameSuffix = '.visualstudio.com';\n\nexport interface AzureRepositoryDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n\tproject?: string;\n\tvirtualDirectory?: string;\n}\n\nexport interface AzureOrganizationDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n}\n\nexport interface AzureProjectDescriptor extends ResourceDescriptor {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface AzureRemoteRepositoryDescriptor extends ResourceDescriptor {\n\tid: string;\n\tnodeId?: string;\n\tresourceName: string;\n\tname: string;\n\tprojectName?: string;\n\turl?: string;\n\tcloneUrlHttps?: string;\n\tcloneUrlSsh?: string;\n}\n\nexport interface AzureProjectInputDescriptor extends ResourceDescriptor {\n\towner: string;\n\tname: string;\n}\n\nexport type AzureWorkItemStateCategory = 'Proposed' | 'InProgress' | 'Resolved' | 'Completed' | 'Removed';\n\nexport function isClosedAzureWorkItemStateCategory(category: AzureWorkItemStateCategory | undefined): boolean {\n\treturn category === 'Completed' || category === 'Resolved' || category === 'Removed';\n}\n\nexport function azureWorkItemsStateCategoryToState(\n\tcategory: AzureWorkItemStateCategory | undefined,\n): IssueOrPullRequestState {\n\tswitch (category) {\n\t\tcase 'Resolved':\n\t\tcase 'Completed':\n\t\tcase 'Removed':\n\t\t\treturn 'closed';\n\t\tcase 'Proposed':\n\t\tcase 'InProgress':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\n\nexport interface AzureLink {\n\thref: string;\n}\n\nexport interface AzureUser {\n\tdisplayName: string;\n\turl: string;\n\t_links: {\n\t\tavatar: AzureLink;\n\t};\n\tid: string;\n\tuniqueName: string;\n\timageUrl: string;\n\tdescriptor?: string;\n}\n\nexport interface AzureUserWithVote extends AzureUser {\n\tisFlagged?: boolean;\n\thasDeclined?: boolean;\n\tisReapprove?: boolean;\n\tisRequired?: boolean;\n\tvote?: AzurePullRequestVote;\n}\n\nexport type AzurePullRequestVote =\n\t| 10 // approved\n\t| 5 // approved with suggestions\n\t| 0 // no vote\n\t| -5 // waiting for author\n\t| -10; // rejected\n\nexport interface AzureWorkItemCommentVersionRef {\n\tcommentId: number;\n\tcreatedInRevision: number;\n\tisDeleted: boolean;\n\ttext: string;\n\turl: string;\n\tversion: number;\n}\n\nexport interface AzureWorkItemRelation {\n\tattributes: {\n\t\t[key: string]: string;\n\t};\n\trelation: string;\n\turl: string;\n}\n\nexport interface WorkItem {\n\t_links: {\n\t\tfields: AzureLink;\n\t\thtml: AzureLink;\n\t\tself: AzureLink;\n\t\tworkItemComments: AzureLink;\n\t\tworkItemRevisions: AzureLink;\n\t\tworkItemType: AzureLink;\n\t\tworkItemUpdates: AzureLink;\n\t};\n\tfields: {\n\t\t//'System.AreaPath': string;\n\t\t'System.TeamProject': string;\n\t\t'System.IterationPath'?: string;\n\t\t'System.WorkItemType': string;\n\t\t'System.State': string;\n\t\t// 'System.Reason': string;\n\t\t'System.AssignedTo': AzureUser;\n\t\t'System.CreatedDate': string;\n\t\t'System.CreatedBy': AzureUser;\n\t\t'System.ChangedDate': string;\n\t\t'System.ChangedBy': AzureUser;\n\t\t'System.CommentCount': number;\n\t\t'System.Description': string;\n\t\t'System.Title': string;\n\t\t'Microsoft.VSTS.Common.ClosedDate': string;\n\t\t// 'Microsoft.VSTS.Common.StateChangeDate': string;\n\t\t// 'Microsoft.VSTS.Common.Priority': number;\n\t\t// 'Microsoft.VSTS.Common.Severity': string;\n\t\t// 'Microsoft.VSTS.Common.ValueArea': string;\n\t};\n\tid: number;\n\trev: number;\n\turl: string;\n\tcommentVersionRef?: AzureWorkItemCommentVersionRef;\n\trelations?: AzureWorkItemRelation[];\n}\n\nexport interface AzureWorkItemState {\n\tname: string;\n\tcolor: string;\n\tcategory: AzureWorkItemStateCategory;\n}\n\nexport type AzurePullRequestStatus = 'abandoned' | 'active' | 'completed' | 'notSet';\nexport function azurePullRequestStatusToState(status: AzurePullRequestStatus): IssueOrPullRequestState {\n\tswitch (status) {\n\t\tcase 'abandoned':\n\t\t\treturn 'closed';\n\t\tcase 'completed':\n\t\t\treturn 'merged';\n\t\tcase 'active':\n\t\tcase 'notSet':\n\t\tdefault:\n\t\t\treturn 'opened';\n\t}\n}\nexport function isClosedAzurePullRequestStatus(status: AzurePullRequestStatus): boolean {\n\treturn azurePullRequestStatusToState(status) !== 'opened';\n}\n\nexport type AzureProjectState = 'createPending' | 'deleted' | 'deleting' | 'new' | 'unchanged' | 'wellFormed';\nexport type AzureProjectVisibility = 'private' | 'public';\n\nexport interface AzureProject {\n\tid: string;\n\tname: string;\n\turl: string;\n\tstate: AzureProjectState;\n\trevision: number;\n\tvisibility: AzureProjectVisibility;\n\tlastUpdateTime: string;\n}\n\nexport interface AzureRepositoryReference {\n\tid: string;\n\tname: string;\n\turl: string;\n\tremoteUrl?: string;\n}\n\nexport interface AzureRepository extends AzureRepositoryReference {\n\tproject: AzureProject;\n\tsize: number;\n\tremoteUrl: string;\n\tsshUrl: string;\n\twebUrl: string;\n\tisDisabled: boolean;\n\tisInMaintenance: boolean;\n}\n\nexport interface AzurePullRequestRepository extends AzureRepositoryReference {\n\tproject: Pick<AzureProject, 'id' | 'name'>;\n}\n\n/** The URL fields of a repository response — all the fork lookup reads, and all an older `api-version` promises. */\nexport type AzureRepositoryUrls = Partial<Pick<AzureRepository, 'webUrl' | 'remoteUrl'>>;\n\n/** The `GET .../_apis/git/repositories/{repositoryId or name}` response, adding fork/default-branch fields to {@link AzureRepository}. */\nexport interface AzureRepositoryWithMetadata extends AzureRepository {\n\t/** Fully-qualified ref, e.g. `refs/heads/main`. */\n\tdefaultBranch?: string;\n\tisFork?: boolean;\n\tparentRepository?: {\n\t\tid: string;\n\t\tname?: string;\n\t\tproject?: { id: string; name: string };\n\t};\n}\n\nexport interface AzureGitUser {\n\tdate?: string;\n\temail?: string;\n\timageUrl?: string;\n\tname: string;\n}\n\nexport interface AzureGitCommitRef {\n\tcommitId: string;\n\turl: string;\n}\n\nexport interface AzureGitCommit {\n\t_links: {\n\t\tchanges: AzureLink;\n\t\trepository: AzureLink;\n\t\tself: AzureLink;\n\t\tweb: AzureLink;\n\t};\n\tauthor: AzureGitUser;\n\tcomment: string;\n\tcommentTruncated?: boolean;\n\tcommitId: string;\n\tcommitTooManyChanges?: boolean;\n\tcommitter: AzureGitUser;\n\tparents: string[];\n\tpush: {\n\t\tdate: string;\n\t\tpushedBy: AzureUser;\n\t\tpushId: number;\n\t};\n\tremoteUrl: string;\n\tstatuses?: AzureGitStatus[];\n\ttreeId: string;\n\turl: string;\n\tworkItems?: AzureResourceRef[];\n}\n\nexport interface AzureResourceRef {\n\tid: string;\n\turl: string;\n}\n\nexport interface AzurePullRequestCompletionOptions {\n\tautoCompleteIgnoreConflicts: number[];\n\tbypassPolicy: boolean;\n\tbypassReason: string;\n\tdeleteSourceBranch: boolean;\n\tmergeCommitMessage: string;\n\tmergeStrategy: 'noFastForward' | 'rebase' | 'rebaseMerge' | 'squash';\n\tsquashMerge: boolean;\n\ttransitionWorkItems: boolean;\n\ttriggeredByAutoComplete: boolean;\n}\n\nexport interface AzureGitStatus {\n\tcontext: {\n\t\tname: string;\n\t\tgenre: string;\n\t};\n\tcreatedBy: AzureUser;\n\tcreateDate: string;\n\tdescription: string;\n\tstate: 'error' | 'failed' | 'notApplicable' | 'notSet' | 'pending' | 'succeeded';\n\ttargetUrl: string;\n\tupdateDate: string;\n}\n\nexport interface AzureGitForkRef {\n\tcreator: AzureUser;\n\tisLocked: boolean;\n\tisLockedBy: AzureUser;\n\tname: string;\n\tobjectId: string;\n\tpeeledObjectId: string;\n\trepository: AzureRepositoryReference;\n\tstatuses: AzureGitStatus[];\n\turl: string;\n}\n\nexport interface AzureWebApiTagDefinition {\n\tactive: boolean;\n\tid: string;\n\tname: string;\n\turl: string;\n}\n\nexport interface AzureGitPullRequestMergeOptions {\n\tconflictAuthorshipCommits: boolean;\n\tdetectRenameFalsePositives: boolean;\n\tdisableRenames: boolean;\n}\n\nexport type AzurePullRequestAsyncStatus =\n\t| 'conflicts'\n\t| 'failure'\n\t| 'notSet'\n\t| 'queued'\n\t| 'rejectedByPolicy'\n\t| 'succeeded';\n\nexport interface AzurePullRequest {\n\trepository: AzurePullRequestRepository;\n\tpullRequestId: number;\n\tcodeReviewId: number;\n\tstatus: AzurePullRequestStatus;\n\tcreatedBy: AzureUser;\n\tcreationDate: string;\n\tclosedDate?: string;\n\tclosedBy?: AzureUser; // Can be missed even if closedDate is presented.\n\ttitle: string;\n\tdescription: string;\n\tforkSource?: AzureGitForkRef;\n\tsourceRefName: string;\n\ttargetRefName: string;\n\tisDraft: boolean;\n\tmergeId: string;\n\tmergeStatus?: AzurePullRequestAsyncStatus;\n\tlastMergeCommit?: AzureGitCommitRef;\n\tlastMergeSourceCommit: AzureGitCommitRef;\n\tlastMergeTargetCommit: AzureGitCommitRef;\n\treviewers: AzureUserWithVote[];\n\turl: string;\n\tsupportsIterations: boolean;\n}\n\nexport interface AzurePullRequestWithLinks extends AzurePullRequest {\n\t_links: {\n\t\tself: AzureLink;\n\t\trepository: AzureLink;\n\t\tworkItems: AzureLink;\n\t\tsourceBranch: AzureLink;\n\t\ttargetBranch: AzureLink;\n\t\tstatuses: AzureLink;\n\t\tsourceCommit: AzureLink;\n\t\ttargetCommit: AzureLink;\n\t\tcreatedBy: AzureLink;\n\t\titerations: AzureLink;\n\t};\n\tartifactId: string;\n\tautoCompleteSetBy?: AzureUser;\n\tcommits?: AzureGitCommitRef[];\n\tcompletionOptions?: AzurePullRequestCompletionOptions;\n\tcompletionQueueTime?: string;\n\thasMultipleMergeBases?: boolean;\n\tlabels?: AzureWebApiTagDefinition[];\n\tmergeFailureMessage?: string;\n\tmergeFailureType?: 'caseSensitive' | 'none' | 'objectTooLarge' | 'unknown';\n\tmergeOptions?: AzureGitPullRequestMergeOptions;\n\tremoteUrl?: string;\n\tworkItemRefs?: AzureResourceRef[];\n}\n\nexport function getAzureDevOpsOwner(url: URL): string {\n\treturn url.pathname.split('/')[1];\n}\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\n/**\n * Encodes one route segment of an Azure DevOps request (#5878): `encodeURIComponent` leaves a dot segment as is, and\n * the URL would resolve it away, re-scoping the request instead of failing it.\n */\nexport function encodeAzurePathSegment(value: string): string {\n\tif (value === '.' || value === '..') throw new Error(`Invalid Azure path segment '${value}'.`);\n\n\treturn encodeURIComponent(value);\n}\n\n/**\n * `baseUrl` and `owner` are the authoritative prefix used for the API request. The payload URL cannot supply that\n * prefix safely because it may address the repository by id or name an untrusted host.\n */\nfunction getAzureRepositoryWebUrl(baseUrl: string, owner: string, projectName: string, repoName: string): string {\n\tconst repoPath = `${encodeURIComponent(owner)}/${encodeURIComponent(projectName)}/_git/${encodeURIComponent(repoName)}`;\n\treturn `${baseUrl.replace(/\\/+$/, '')}/${repoPath}`;\n}\n\nexport function getAzurePullRequestWebUrl(pr: AzurePullRequest, baseUrl: string, owner: string): string {\n\tconst repoUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\treturn `${repoUrl}/pullrequest/${pr.pullRequestId}`;\n}\n\n/**\n * Whether `url` and `expected` are the two cloud spellings of one organization: a cloud organization answers on both\n * `dev.azure.com/{owner}` and the legacy `{owner}.visualstudio.com`, so those name the same host. Anything else is\n * somewhere we didn't ask.\n */\nfunction isSameAzureCloudOrganization(url: URL, expected: URL, expectedOwner: string): boolean {\n\tif (url.protocol !== expected.protocol) return false;\n\n\tconst owner = expectedOwner.toLowerCase();\n\tif (expected.hostname !== 'dev.azure.com' && !isVsts(expected.hostname)) return false;\n\n\tif (url.hostname === 'dev.azure.com') return getAzureDevOpsOwner(url).toLowerCase() === owner;\n\n\treturn url.hostname === `${owner}${vstsHostnameSuffix}`;\n}\n\n/**\n * Whether `url` sits under the collection the integration is configured to talk to — `{expectedUrl}/{expectedOwner}`.\n * A matching origin says nothing on its own: Azure organizations and self-hosted collections are path segments.\n */\nfunction isUnderAzureCollection(url: URL, expected: URL, expectedOwner: string): boolean {\n\tconst base = expected.pathname.replace(/\\/+$/, '');\n\tconst prefix = `${base}/${encodeURIComponent(expectedOwner)}/`.toLowerCase();\n\treturn `${url.pathname}/`.toLowerCase().startsWith(prefix);\n}\n\n/**\n * Restricts a provider-supplied repository URL to the integration's collection and removes credentials, query, and\n * fragment before a consumer can use it as a git remote. Takes the value as the payloads carry it — every one of\n * these URLs is optional — so a caller never has to spell the absent case itself.\n */\nexport function sanitizeAzureRepositoryUrl(\n\tvalue: string | undefined,\n\texpectedUrl: string,\n\texpectedOwner: string,\n): string | undefined {\n\tif (value == null) return undefined;\n\n\tlet expected: URL;\n\tlet url: URL;\n\ttry {\n\t\texpected = new URL(expectedUrl);\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tif (expected.protocol !== 'https:' && expected.protocol !== 'http:') return undefined;\n\tif (!expectedOwner) return undefined;\n\n\tif (url.origin === expected.origin) {\n\t\tif (!isUnderAzureCollection(url, expected, expectedOwner)) return undefined;\n\t} else if (!isSameAzureCloudOrganization(url, expected, expectedOwner)) {\n\t\treturn undefined;\n\t}\n\n\turl.username = '';\n\turl.password = '';\n\turl.search = '';\n\turl.hash = '';\n\treturn url.toString();\n}\n\n/**\n * The clone URL the payload carries for a repository, restricted to the integration's collection and then\n * cross-checked against the repository that payload names.\n *\n * The collection check alone is not enough for a URL that is READ rather than built. Every other repository URL the\n * model reports is built from the authoritative prefix, so the payload cannot move it; this one cannot be built,\n * because Azure spells a repository for git the same way it spells it for the web only by convention. A URL under\n * the right collection may still name a DIFFERENT repository in it, which would leave `cloneHttps` describing one\n * repository while `url` describes another.\n *\n * Azure ends a clone URL with `_git/{repo}` on every host style — including the short `{owner}/_git/{repo}` form it\n * uses when a repository carries its project's name, and the legacy `{owner}.visualstudio.com` spelling that has no\n * organization segment at all — so the repository name is the one part comparable across all of them. A same-named\n * repository in another project of the same organization is the single substitution this cannot catch.\n */\nfunction getAzureRepositoryCloneUrl(\n\trepository: AzureRepositoryReference,\n\tbaseUrl: string,\n\towner: string,\n): string | undefined {\n\tconst sanitized = sanitizeAzureRepositoryUrl(repository.remoteUrl, baseUrl, owner);\n\tif (sanitized == null) return undefined;\n\n\t// Safe to parse: `sanitizeAzureRepositoryUrl` returns what it already parsed.\n\tconst segments = new URL(sanitized).pathname.split('/').filter(s => s.length > 0);\n\tconst name = segments.pop();\n\tif (name == null || segments.pop() !== '_git') return undefined;\n\n\tlet decoded: string;\n\ttry {\n\t\tdecoded = decodeURIComponent(name);\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\t// Azure resolves project and repository names case-insensitively, so comparing them any other way would refuse a\n\t// URL the provider considers the same one.\n\treturn decoded.toLowerCase() === repository.name.toLowerCase() ? sanitized : undefined;\n}\n\nexport function fromAzurePullRequestMergeStatusToMergeableState(\n\tmergeStatus: AzurePullRequestAsyncStatus,\n): PullRequestMergeableState {\n\tswitch (mergeStatus) {\n\t\tcase 'conflicts':\n\t\t\treturn PullRequestMergeableState.Conflicting;\n\t\tcase 'failure':\n\t\t\treturn PullRequestMergeableState.FailingChecks;\n\t\tcase 'rejectedByPolicy':\n\t\t\treturn PullRequestMergeableState.BlockedByPolicy;\n\t\tcase 'succeeded':\n\t\t\treturn PullRequestMergeableState.Mergeable;\n\t\tcase 'notSet':\n\t\tcase 'queued':\n\t\tdefault:\n\t\t\treturn PullRequestMergeableState.Unknown;\n\t}\n}\n\nexport function fromAzurePullRequestVoteToReviewState(vote: AzurePullRequestVote): PullRequestReviewState {\n\tswitch (vote) {\n\t\tcase 10:\n\t\tcase 5:\n\t\t\treturn PullRequestReviewState.Approved;\n\t\tcase 0:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t\tcase -5:\n\t\tcase -10:\n\t\t\treturn PullRequestReviewState.ChangesRequested;\n\t\tdefault:\n\t\t\treturn PullRequestReviewState.ReviewRequested;\n\t}\n}\n\nexport function fromAzureUserWithVoteToReviewer(reviewer: AzureUserWithVote): PullRequestReviewer {\n\treturn {\n\t\tisCodeOwner: undefined,\n\t\treviewer: {\n\t\t\tavatarUrl: reviewer.imageUrl,\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName,\n\t\t\tusername: reviewer.uniqueName,\n\t\t\turl: reviewer.url,\n\t\t},\n\t\tstate: fromAzurePullRequestVoteToReviewState(reviewer.vote ?? 0),\n\t};\n}\n\nexport function getAzurePullRequestReviewDecision(\n\tvotes: AzurePullRequestVote[],\n): PullRequestReviewDecision | undefined {\n\tconst reviewStates = votes.map(vote => fromAzurePullRequestVoteToReviewState(vote));\n\tif (reviewStates.includes(PullRequestReviewState.ChangesRequested)) {\n\t\treturn PullRequestReviewDecision.ChangesRequested;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.ReviewRequested)) {\n\t\treturn PullRequestReviewDecision.ReviewRequired;\n\t}\n\n\tif (reviewStates.includes(PullRequestReviewState.Approved)) {\n\t\treturn PullRequestReviewDecision.Approved;\n\t}\n\n\treturn undefined;\n}\n\nexport function fromAzureReviewerToPullRequestMember(reviewer: AzureUser): PullRequestMember {\n\treturn {\n\t\tavatarUrl: reviewer.imageUrl,\n\t\tid: reviewer.id,\n\t\tname: reviewer.displayName,\n\t\tusername: reviewer.uniqueName,\n\t\turl: reviewer.url,\n\t};\n}\n\nexport function normalizeAzureBranchName(branchName: string): string {\n\treturn branchName.startsWith('refs/heads/') ? branchName.replace('refs/heads/', '') : branchName;\n}\n\nfunction fromAzureUserToMember(user: AzureUser, type: 'issue'): IssueMember;\nfunction fromAzureUserToMember(user: AzureUser, type: 'pullRequest'): PullRequestMember;\nfunction fromAzureUserToMember(user: AzureUser, _type: 'issue' | 'pullRequest'): PullRequestMember | IssueMember {\n\treturn {\n\t\tavatarUrl: user.imageUrl,\n\t\tid: user.id,\n\t\tname: user.displayName,\n\t\tusername: user.uniqueName,\n\t\turl: user.url,\n\t};\n}\n\n/**\n * The URLs of a cross-repository pull request's fork, resolved by a lookup because the pull request payload embeds\n * only an abbreviated reference to it: `url` is the URL the head ref reports — the fork's web URL, or its clone URL\n * when the response carries no web URL — and `cloneHttps` its HTTPS clone URL.\n *\n * A fork that resolves neither is reported as no fork at all rather than as an object with nothing in it, so `url`\n * is always present here; `cloneHttps` alone is best-effort, and is absent when the response omits it or its URL\n * sits outside the integration's collection.\n */\nexport interface AzureForkRepositoryUrls {\n\turl: string;\n\tcloneHttps: string | undefined;\n}\n\n/**\n * `baseUrl` and `owner` are the prefix the pull request was read through; every URL the model reports is built from\n * them. The required `forkRepositoryUrls` argument makes each call site resolve the best-effort fork URLs explicitly.\n */\nexport function fromAzurePullRequest(\n\tpr: AzurePullRequest,\n\tprovider: Provider,\n\towner: string,\n\tbaseUrl: string,\n\tforkRepositoryUrls: AzureForkRepositoryUrls | undefined,\n\tprojection?: PullRequestProjection,\n): PullRequest {\n\tconst baseRepositoryUrl = getAzureRepositoryWebUrl(baseUrl, owner, pr.repository.project.name, pr.repository.name);\n\tconst baseCloneHttps = getAzureRepositoryCloneUrl(pr.repository, baseUrl, owner);\n\n\tconst forkRepository = pr.forkSource?.repository;\n\tconst headRepositoryUrl = forkRepository == null ? baseRepositoryUrl : forkRepositoryUrls?.url;\n\tconst headCloneHttps = forkRepository == null ? baseCloneHttps : forkRepositoryUrls?.cloneHttps;\n\n\treturn new PullRequest(\n\t\tprovider,\n\t\tfromAzureUserToMember(pr.createdBy, 'pullRequest'),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.pullRequestId.toString(),\n\t\tpr.title,\n\t\tgetAzurePullRequestWebUrl(pr, baseUrl, owner),\n\t\t{\n\t\t\towner: owner,\n\t\t\trepo: pr.repository.name,\n\t\t\tid: pr.repository.id,\n\t\t},\n\t\tazurePullRequestStatusToState(pr.status),\n\t\tnew Date(pr.creationDate),\n\t\tnew Date(pr.closedDate || pr.creationDate),\n\t\tpr.closedDate ? new Date(pr.closedDate) : undefined,\n\t\tpr.closedDate && pr.status === 'completed' ? new Date(pr.closedDate) : undefined,\n\t\t// The read selects the merge status, so an absent one is Azure's own \"not set\", as provider-apis maps it.\n\t\tfromAzurePullRequestMergeStatusToMergeableState(pr.mergeStatus ?? 'notSet'),\n\t\tundefined,\n\t\t{\n\t\t\tbase: {\n\t\t\t\tbranch: pr.targetRefName ? normalizeAzureBranchName(pr.targetRefName) : '',\n\t\t\t\tsha: pr.lastMergeTargetCommit?.commitId ?? '',\n\t\t\t\trepo: pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.targetRefName != null,\n\t\t\t\turl: baseRepositoryUrl,\n\t\t\t\tcloneHttps: baseCloneHttps,\n\t\t\t},\n\t\t\thead: {\n\t\t\t\tbranch: pr.sourceRefName ? normalizeAzureBranchName(pr.sourceRefName) : '',\n\t\t\t\tsha: pr.lastMergeSourceCommit?.commitId ?? '',\n\t\t\t\trepo: forkRepository?.name ?? pr.repository.name,\n\t\t\t\towner: owner,\n\t\t\t\texists: pr.sourceRefName != null,\n\t\t\t\turl: headRepositoryUrl,\n\t\t\t\tcloneHttps: headCloneHttps,\n\t\t\t},\n\t\t\tisCrossRepository: pr.forkSource != null,\n\t\t},\n\t\tpr.isDraft,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tgetAzurePullRequestReviewDecision(pr.reviewers?.filter(r => r.isRequired).map(r => r.vote ?? 0) ?? []),\n\t\tpr.reviewers.filter(r => r.vote == null || r.vote === 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.filter(r => r.vote != null && r.vote !== 0).map(r => fromAzureUserWithVoteToReviewer(r)),\n\t\tpr.reviewers.map(r => fromAzureReviewerToPullRequestMember(r)),\n\t\tundefined,\n\t\t{\n\t\t\tid: pr.repository?.project?.id,\n\t\t\tname: pr.repository.project.name,\n\t\t\tresourceId: '', // TODO: This is a workaround until we can get the org id here.\n\t\t\tresourceName: owner,\n\t\t},\n\t\tundefined, // version\n\t\tundefined, // commitCount\n\t\tundefined, // stack\n\t\tundefined, // filesChanged\n\t\tundefined, // body\n\t\tundefined, // number\n\t\tundefined, // authoredByMe\n\t\tprojection,\n\t);\n}\n\nexport function fromAzureWorkItem(\n\tworkItem: WorkItem,\n\tprovider: Provider,\n\tproject: AzureProjectDescriptor,\n\tstateCategory?: AzureWorkItemStateCategory,\n\tprojection?: IssueProjection,\n): Issue {\n\treturn new Issue(\n\t\tprovider,\n\t\tworkItem.id.toString(),\n\t\tworkItem.id.toString(),\n\t\tworkItem.fields['System.Title'],\n\t\tworkItem._links.html.href,\n\t\tnew Date(workItem.fields['System.CreatedDate']),\n\t\tnew Date(workItem.fields['System.ChangedDate']),\n\t\tisClosedAzureWorkItemStateCategory(stateCategory),\n\t\tazureWorkItemsStateCategoryToState(stateCategory),\n\t\tfromAzureUserToMember(workItem.fields['System.CreatedBy'], 'issue'),\n\t\tworkItem.fields['System.AssignedTo'] != null\n\t\t\t? [fromAzureUserToMember(workItem.fields['System.AssignedTo'], 'issue')]\n\t\t\t: [],\n\t\tundefined,\n\t\tworkItem.fields['Microsoft.VSTS.Common.ClosedDate']\n\t\t\t? new Date(workItem.fields['Microsoft.VSTS.Common.ClosedDate'])\n\t\t\t: undefined,\n\t\tundefined,\n\t\tworkItem.fields['System.CommentCount'],\n\t\tundefined,\n\t\tworkItem.fields['System.Description'],\n\t\tproject,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\tundefined,\n\t\ttoWorkItemIterations(workItem.fields['System.IterationPath']),\n\t\tprojection,\n\t);\n}\n\n/**\n * Azure reports the iteration as a backslash-delimited path rooted at the project. The project root is its default\n * for a work item with no sprint, so only a nested path names one; the path is the identity because Azure supplies\n * no iteration id here.\n *\n * Mirrors `normalizeIteration` in provider-apis so the same work item yields the same identity whether it is read\n * here or through the SDK. That contract keeps the path **verbatim** — it is what the iteration is matched back by,\n * so it is deliberately not trimmed — and trims only the display name.\n */\nfunction toWorkItemIterations(path: string | undefined): IssueIteration[] | undefined {\n\tif (!path) return undefined;\n\n\tconst segments = path.split('\\\\');\n\tif (segments.length < 2) return undefined;\n\n\tconst name = segments.at(-1)?.trim();\n\treturn name ? [{ id: path, name: name }] : undefined;\n}\n\n/**\n * A work item as `GET …/_apis/wit/workitems/{id}?$expand=Links` returns it. Loosely typed because\n * {@link fromAzureWorkItemToProviderIssue} checks every field it reads, as provider-apis does.\n */\nexport interface AzureWorkItemResponse {\n\tid?: unknown;\n\tfields?: Record<string, unknown>;\n\t_links?: { html?: { href?: unknown } };\n}\n\n/**\n * Converts a work item exactly as provider-apis' `getIssuesForAzureProject` converts each row it reads, so a work\n * item read by id reaches `toIssueShape` identical to the list read's row for it. provider-apis exports neither\n * that converter nor a single work item read, so this mirrors it field for field; the batch issue tests compare\n * the two, so a provider-apis change that this misses fails there.\n *\n * `undefined` for a work item the SDK would skip: no positive integer id, no title, or no valid created date.\n * `state.name` is the raw `System.State`, as on every list read here, which passes the SDK no state-name map.\n */\nexport function fromAzureWorkItemToProviderIssue(\n\tworkItem: AzureWorkItemResponse,\n\tnamespace: string,\n\tproject: string,\n): ProviderIssue | undefined {\n\tconst fields = workItem.fields;\n\tconst id = workItem.id;\n\tconst title = fields?.['System.Title'];\n\tconst createdDate = parseAzureWorkItemDate(fields?.['System.CreatedDate']);\n\tif (\n\t\tfields == null ||\n\t\ttypeof id !== 'number' ||\n\t\t!Number.isSafeInteger(id) ||\n\t\tid <= 0 ||\n\t\ttypeof title !== 'string' ||\n\t\t!title.trim() ||\n\t\tcreatedDate == null\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst assignee = fromAzureWorkItemIdentity(fields['System.AssignedTo']);\n\tconst commentCount = fields['System.CommentCount'];\n\tconst url = workItem._links?.html?.href;\n\tconst tags = fields['System.Tags'];\n\tconst state = fields['System.State'];\n\tconst type = fields['System.WorkItemType'];\n\tconst description = fields['System.Description'];\n\treturn {\n\t\tid: id.toString(),\n\t\tnumber: id.toString(),\n\t\ttitle: title,\n\t\tcommentCount: typeof commentCount === 'number' && Number.isFinite(commentCount) ? commentCount : null,\n\t\tauthor: fromAzureWorkItemIdentity(fields['System.CreatedBy']),\n\t\tclosedDate: parseAzureWorkItemDate(fields['Microsoft.VSTS.Common.ClosedDate']),\n\t\tcreatedDate: createdDate,\n\t\tupdatedDate: parseAzureWorkItemDate(fields['System.ChangedDate']),\n\t\turl: typeof url === 'string' ? url : null,\n\t\tassignees: assignee != null ? [assignee] : [],\n\t\tdescription: typeof description === 'string' ? description : null,\n\t\tstate: typeof state === 'string' && state ? { name: state, color: null } : null,\n\t\ttype: typeof type === 'string' ? type : null,\n\t\titeration: toAzureWorkItemIteration(fields['System.IterationPath']),\n\t\trepository: null,\n\t\tproject: { namespace: namespace, name: project, resourceId: null, key: null, id: null },\n\t\t// provider-apis fills 0, but work items have no reactions.\n\t\tupvoteCount: null,\n\t\tlabels: (typeof tags === 'string' ? tags.split(';') : []).map(tag => ({\n\t\t\tcolor: null,\n\t\t\tdescription: null,\n\t\t\tid: null,\n\t\t\tname: tag.trim(),\n\t\t})),\n\t};\n}\n\n/** An identity field, as provider-apis maps it: `name` from `uniqueName`, `username` from `displayName`. */\nfunction fromAzureWorkItemIdentity(value: unknown): ProviderAccount | null {\n\tconst identity = value as\n\t\t| { id?: unknown; uniqueName?: unknown; displayName?: unknown; _links?: { avatar?: { href?: unknown } } }\n\t\t| null\n\t\t| undefined;\n\tif (typeof identity?.id !== 'string' || !identity.id.trim()) return null;\n\n\tconst avatarUrl = identity._links?.avatar?.href;\n\treturn {\n\t\tavatarUrl: typeof avatarUrl === 'string' ? avatarUrl : null,\n\t\temail: null,\n\t\tid: identity.id,\n\t\tname: typeof identity.uniqueName === 'string' ? identity.uniqueName : null,\n\t\tusername: typeof identity.displayName === 'string' ? identity.displayName : null,\n\t\turl: null,\n\t};\n}\n\n/** Only a UTC ISO timestamp naming a real calendar date and time, as provider-apis accepts; anything else is `null`. */\nfunction parseAzureWorkItemDate(value: unknown): Date | null {\n\tif (typeof value !== 'string') return null;\n\n\tconst match = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d{1,7})?Z$/.exec(value);\n\tif (match == null) return null;\n\n\tconst [year, month, day, hour, minute, second] = match.slice(1).map(Number);\n\tconst isLeapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n\tconst daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n\tif (month < 1 || month > 12 || day < 1 || day > daysInMonth[month - 1] || hour > 23 || minute > 59 || second > 59) {\n\t\treturn null;\n\t}\n\n\tconst date = new Date(value);\n\treturn Number.isNaN(date.getTime()) ? null : date;\n}\n\n/** The SDK's `iteration`, by the same path rule as {@link toWorkItemIterations}. */\nfunction toAzureWorkItemIteration(path: unknown): ProviderIssue['iteration'] {\n\tconst [iteration] = toWorkItemIterations(typeof path === 'string' ? path : undefined) ?? [];\n\treturn iteration != null ? { path: iteration.id, name: iteration.name } : undefined;\n}\n\n/**\n * The most ids one cheap work item read asks for: the most Azure DevOps' work item list (`GET …/_apis/wit/workitems`)\n * takes.\n */\nexport const azureWorkItemsEtagFieldsMaxIds = 200;\n\n/**\n * The fields the cheap work item read selects: only the ones a full row's etag reads, and the project the full read\n * checks the work item against. Not `System.State`: {@link fromAzureWorkItemToProviderIssue} gives the state no\n * category, so `toIssueShape` closes a work item by its closed date alone.\n */\nexport const azureWorkItemEtagFieldNames: readonly string[] = [\n\t'System.TeamProject',\n\t'System.ChangedDate',\n\t'Microsoft.VSTS.Common.ClosedDate',\n];\n\n/**\n * A cheap read's work item, as the fields its full batch row's etag reads: `toIssueShape` closes the row the full\n * read builds (see {@link fromAzureWorkItemToProviderIssue}) exactly when it has a closed date, parsed by the same rule.\n * Throws where the full read fails the work item: on one in another project than `project` (compared as Azure\n * compares names), and on a missing or unparseable change date, so the target falls through to the full read.\n */\nexport function toAzureWorkItemEtagFields(workItem: AzureWorkItemResponse, project: string): IssueEtagFields {\n\tconst fields = workItem.fields;\n\tconst teamProject = fields?.['System.TeamProject'];\n\tif (typeof teamProject === 'string' && teamProject.toLowerCase() !== project.toLowerCase()) {\n\t\tthrow new Error(\n\t\t\t`Azure DevOps work item ${String(workItem.id)} is in project '${teamProject}', not '${project}'`,\n\t\t);\n\t}\n\n\tconst updatedDate = parseAzureWorkItemDate(fields?.['System.ChangedDate']);\n\tif (updatedDate == null) {\n\t\tthrow new Error(`Azure DevOps returned work item ${String(workItem.id)} without a change date`);\n\t}\n\n\treturn {\n\t\tstate: parseAzureWorkItemDate(fields?.['Microsoft.VSTS.Common.ClosedDate']) != null ? 'closed' : 'opened',\n\t\tupdatedDate: updatedDate,\n\t};\n}\n\ntype ProviderPullRequestState = Parameters<typeof fromProviderPullRequestState>[0];\ntype ProviderMergeableState = keyof typeof fromProviderPullRequestMergeableState;\ntype ProviderReviewState = keyof typeof fromPullRequestReviewDecision;\n\n// The tables below mirror provider-apis' (0.61.0) Azure DevOps pull request mapping behind `getPullRequestForRepo` (`Xe`\n// and its helpers in the bundle), which it doesn't export. A full batch row takes these values on through\n// `fromProviderPullRequest`, so the cheap check composes the same steps to compute the same etag.\n\n/** provider-apis' pull request `status` map (`bi`). It has no `notSet`, which therefore maps to `undefined`. */\nconst azurePullRequestProviderStates: Partial<Record<string, ProviderPullRequestState>> = {\n\tactive: GitPullRequestState.Open,\n\tcompleted: GitPullRequestState.Merged,\n\tabandoned: GitPullRequestState.Closed,\n};\n\n/** provider-apis' `mergeStatus` map (`Ti`); any other status, or none, is unknown. */\nconst azureMergeStatusProviderStates: Partial<Record<string, ProviderMergeableState>> = {\n\tconflicts: GitPullRequestMergeableState.Conflicts,\n\tfailure: GitPullRequestMergeableState.FailingChecks,\n\trejectedByPolicy: GitPullRequestMergeableState.Blocked,\n\tsucceeded: GitPullRequestMergeableState.Mergeable,\n};\n\n/** provider-apis' reviewer `vote` map (`Di`); any other vote reads as review requested. */\nconst azureVoteProviderReviewStates: Partial<Record<number, ProviderReviewState>> = {\n\t10: GitPullRequestReviewState.Approved,\n\t5: GitPullRequestReviewState.Approved,\n\t0: GitPullRequestReviewState.ReviewRequested,\n\t[-5]: GitPullRequestReviewState.ChangesRequested,\n\t[-10]: GitPullRequestReviewState.ChangesRequested,\n};\n\n/** provider-apis' reviewer mapping (`ve`), with the state its vote maps to: every reviewer, required or optional. */\nfunction toAzureProviderReview(reviewer: AzureUserWithVote): ProviderPullRequestReview {\n\treturn {\n\t\treviewer: {\n\t\t\tid: reviewer.id,\n\t\t\tname: reviewer.displayName ?? null,\n\t\t\tusername: (reviewer.uniqueName || reviewer.displayName) ?? null,\n\t\t\temail: null,\n\t\t\tavatarUrl: reviewer.imageUrl ?? null,\n\t\t\turl: null,\n\t\t},\n\t\tstate: toAzureProviderReviewState(reviewer),\n\t};\n}\n\nfunction toAzureProviderReviewState(reviewer: AzureUserWithVote): ProviderReviewState {\n\treturn azureVoteProviderReviewStates[reviewer.vote ?? 0] ?? GitPullRequestReviewState.ReviewRequested;\n}\n\n/**\n * A cheap read's pull request (the same `GET …/pullrequests/{id}` provider-apis sends), as the fields its full batch\n * row's etag reads: provider-apis' mapping (see the tables above), then `fromProviderPullRequest`'s. Azure DevOps\n * reports no update time, so provider-apis' `updatedDate` is the close time, else the creation time, which is why\n * the fields Azure changes without one are fingerprinted into the `revision`, from the values the full row ends up\n * with: provider-apis' title, description (`?? null`, which the row reads `?? undefined`) and target branch, and its\n * reviews, split into requests and completed reviews by `fromProviderPullRequest`'s own helpers.\n *\n * Throws where provider-apis' mapping throws, on a field it reads without a guard, so a pull request the full read\n * fails on falls through to it rather than matching an etag. A full row never carries a check rollup (provider-apis\n * maps none for Azure DevOps), so the `checks` include always reads `undefined`, as there.\n */\nexport function toAzurePullRequestEtagFields(\n\tpr: AzurePullRequest,\n\tetagIncludes: readonly PullRequestEtagInclude[],\n): PullRequestEtagFields {\n\tif (\n\t\tpr.pullRequestId == null ||\n\t\tpr.createdBy == null ||\n\t\tpr.repository?.project == null ||\n\t\tpr.lastMergeSourceCommit == null ||\n\t\tpr.lastMergeTargetCommit == null ||\n\t\t!Array.isArray(pr.reviewers) ||\n\t\tpr.reviewers.some(r => r == null) ||\n\t\ttypeof pr.url !== 'string' ||\n\t\ttypeof pr.sourceRefName !== 'string' ||\n\t\ttypeof pr.targetRefName !== 'string'\n\t) {\n\t\tthrow new Error(`Azure DevOps returned pull request ${String(pr.pullRequestId)} without a field it maps`);\n\t}\n\n\tconst reviews = pr.reviewers.map(toAzureProviderReview);\n\tconst fields: PullRequestEtagFields = {\n\t\t// `notSet` passes `undefined` here, as its full row does.\n\t\tstate: fromProviderPullRequestState(azurePullRequestProviderStates[pr.status] as ProviderPullRequestState),\n\t\tisDraft: pr.isDraft,\n\t\tupdatedDate: new Date(pr.closedDate || pr.creationDate),\n\t\theadSha: pr.lastMergeSourceCommit.commitId ?? '',\n\t\trevision: pullRequestRevision({\n\t\t\ttitle: pr.title,\n\t\t\tbody: pr.description ?? undefined,\n\t\t\trefs: { base: { branch: normalizeAzureBranchName(pr.targetRefName) } },\n\t\t\treviewRequests: toReviewRequests(reviews),\n\t\t\tlatestReviews: toCompletedReviews(reviews),\n\t\t}),\n\t};\n\n\tif (etagIncludes.includes('mergeable')) {\n\t\tconst mergeable =\n\t\t\t(pr.mergeStatus != null ? azureMergeStatusProviderStates[pr.mergeStatus] : undefined) ??\n\t\t\tGitPullRequestMergeableState.Unknown;\n\t\tfields.mergeableState = fromProviderPullRequestMergeableState[mergeable];\n\t}\n\n\tif (etagIncludes.includes('reviewDecision')) {\n\t\t// Only the required reviewers decide.\n\t\tconst decision = decideProviderReviewDecision(\n\t\t\tpr.reviewers.filter(r => r.isRequired).map(toAzureProviderReviewState),\n\t\t);\n\t\tfields.reviewDecision = decision ? fromPullRequestReviewDecision[decision] : undefined;\n\t}\n\n\treturn fields;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { ResourceDescriptor } from "../../../git/models/resourceDescriptor.js";
2
2
  import { IssueOrPullRequest, IssueOrPullRequestState, IssueOrPullRequestType } from "../../../git/models/issueOrPullRequest.js";
3
3
  import { Issue as Issue$1, IssueShape } from "../../../git/models/issue.js";
4
- import { PullRequest, PullRequestFilter, PullRequestMergeMethod, PullRequestShape, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
4
+ import { PullRequest, PullRequestFilter, PullRequestMergeMethod, PullRequestProjection, PullRequestShape, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
5
5
  import { Account as Account$1, UnidentifiedAuthor } from "../../../git/models/author.js";
6
6
  import { DefaultBranch } from "../../../git/models/defaultBranch.js";
7
7
  import { RepositoryMetadata } from "../../../git/models/repositoryMetadata.js";
@@ -10,13 +10,13 @@ import { AuthenticationSessionLike, ProviderAuthenticationSession, TokenWithInfo
10
10
  import { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from "../constants.js";
11
11
  import { ProviderOrganization, ProviderWarningCause, ProviderWarningScope } from "../results.js";
12
12
  import { ProviderRefusal } from "../collectionMetadata.js";
13
+ import { ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureResource, ProviderHierarchyResult, ProviderPullRequest, ProviderRepository } from "./models.js";
13
14
  import { AzureOrganizationDescriptor, AzureProjectDescriptor, AzureProjectInputDescriptor, AzureRemoteRepositoryDescriptor, AzureRepositoryDescriptor } from "./azure/models.js";
14
15
  import { AccountWideIssuesResult, SearchMyIssuesOptions } from "../models/issueReads.js";
15
16
  import { GitHostIntegration, SearchMyPullRequestsOptions, SearchPullRequestsOptions } from "../models/gitHostIntegration.js";
16
- import "../models/integration.js";
17
- import { ProviderApiCollectionResult, ProviderApiPagedResult, ProviderAzureResource, ProviderHierarchyResult, ProviderPullRequest, ProviderRepository } from "./models.js";
17
+ import { IssueEtagFields, IssueEtagInclude, PullRequestEtagFields, PullRequestEtagInclude } from "../models/integration.js";
18
18
  import { IntegrationAuthenticationProviderDescriptor } from "../authentication/integrationAuthenticationProvider.js";
19
- import { CollectionScopeFailure } from "@gitkraken/provider-apis";
19
+ import { CollectionScopeFailure, GitPullRequestState } from "@gitkraken/provider-apis";
20
20
  //#region ../plus/integrations/src/providers/azureDevOps.d.ts
21
21
  export declare function getAzureRepositoryIdentity(repo: Pick<AzureRepositoryDescriptor, 'owner' | 'name' | 'project'>): {
22
22
  resourceName: string;
@@ -31,6 +31,29 @@ export declare function getAzureRepositoryApiBaseUrl(baseUrl: string, repo: Pick
31
31
  export declare function sameAzureName(a: string, b: string): boolean;
32
32
  /** The distinct names in `names`, compared as Azure compares them, keeping the first spelling of each. */
33
33
  export declare function uniqueAzureNames(names: readonly string[]): string[];
34
+ /**
35
+ * Flags the reviews of `pr` that name one of the current user's groups (see `PullRequestReviewer.isMyGroup`), which a
36
+ * reviewer read finds through that group rather than through the user.
37
+ */
38
+ export declare function markMyGroupReviews(pr: ProviderPullRequest, groupIds: ReadonlySet<string>): ProviderPullRequest;
39
+ /** One read a reviewer relationship takes in a project or repository, and the rows of it the relationship keeps. */
40
+ export interface AzureReviewerRead {
41
+ filter: {
42
+ reviewerId?: string;
43
+ states?: GitPullRequestState[];
44
+ };
45
+ keep?: (pr: ProviderPullRequest) => boolean;
46
+ }
47
+ /**
48
+ * The reads a reviewer relationship over `states` (omitted: open) takes for `userId`, whose groups are `groupIds`:
49
+ *
50
+ * - Open pull requests: all of them, kept when one names the user, or names one of the user's groups and the user
51
+ * didn't write it. Azure's reviewer filter only matches the identity it names, never the groups that identity is in,
52
+ * and a group asked to review the user's own pull request isn't asking the user.
53
+ * - Closed and merged ones: Azure's own filter for the user. They are every pull request a project ever had, too many
54
+ * to read whole on every read, so a group's request is only followed while the pull request is open.
55
+ */
56
+ export declare function toAzureReviewerReads(userId: string, groupIds: ReadonlySet<string>, states: readonly GitPullRequestState[] | undefined): AzureReviewerRead[];
34
57
  export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends GitCloudHostIntegrationId.AzureDevOps | GitSelfManagedHostIntegrationId.AzureDevOpsServer, TRepositoryDescriptor extends AzureRepositoryDescriptor = AzureRepositoryDescriptor> extends GitHostIntegration<TIntegrationId, TRepositoryDescriptor> {
35
58
  protected abstract apiBaseUrlFor(session: ProviderAuthenticationSession): string;
36
59
  protected getApiOptions(session: ProviderAuthenticationSession, doNotConvertToPat?: boolean): {
@@ -61,6 +84,31 @@ export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends
61
84
  * since Azure DevOps Server gives one person a different id in each collection than at the server level.
62
85
  */
63
86
  protected getFilterUserId(session: ProviderAuthenticationSession, _collection: string): Promise<string | undefined>;
87
+ /**
88
+ * The current user's groups per credential, collection and user, kept for {@link discoveryCacheTtl} like the other
89
+ * discovered sets: membership changes rarely, and a sweep that reads every few minutes would otherwise pay for it
90
+ * every time. A failed read isn't kept, and a re-sync drops them all (see {@link invalidateDiscoveryCaches}).
91
+ */
92
+ private readonly _reviewerGroupIds;
93
+ /**
94
+ * Every group the current user (`userId`, as {@link getFilterUserId} resolved it) is a member of in `collection`:
95
+ * teams and security groups, in any project, directly or through other groups. Two requests per collection,
96
+ * however many groups, and none while the last read is kept.
97
+ */
98
+ protected getReviewerGroupIds(session: ProviderAuthenticationSession, collection: string, userId: string): Promise<Set<string>>;
99
+ invalidateDiscoveryCaches(): void;
100
+ /**
101
+ * For one read, the current account as each organization's pull requests identify it, given the account itself
102
+ * (`undefined` when it couldn't be read). Azure DevOps Services gives a person one id, so that is `account`; the
103
+ * server override resolves each collection's own, once per read.
104
+ */
105
+ protected getOrganizationViewers(_session: ProviderAuthenticationSession, account: {
106
+ id: string;
107
+ username?: string;
108
+ } | undefined): (org: string) => Promise<{
109
+ id: string;
110
+ username?: string;
111
+ } | undefined>;
64
112
  /**
65
113
  * What the discovery caches (account, organizations, projects, and their stored copies) are keyed by. Azure DevOps
66
114
  * Services has a single address, so the credential alone identifies what it discovers; the server override adds
@@ -161,6 +209,48 @@ export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends
161
209
  username?: string;
162
210
  };
163
211
  } | undefined, _cancellation?: AbortSignal): Promise<PromiseSettledResult<PullRequestShape | undefined>[] | undefined>;
212
+ /**
213
+ * The cheap check behind the batch issue read's etags: one request per organization, project and
214
+ * {@link azureWorkItemsEtagFieldsMaxIds} distinct ids, where {@link getProviderIssuesBatch} sends one per target,
215
+ * asking only for each work item's change state. A request that throws rejects only its own targets' slots, with
216
+ * its error classified as the full read's would be.
217
+ *
218
+ * Never proves an absence. Azure silently leaves out every id it can't return, whether missing, hidden or failing,
219
+ * so such a target's slot is rejected and the full read, whose not-found does prove an absence, decides. So is a
220
+ * work item in another project, which the full read fails. When no target was answered and nothing failed, the
221
+ * check declines instead, so a batch of only such ids costs a full read, not a failure.
222
+ *
223
+ * A work item has no reactions, and its full row no count, so `'reactions'` reads nothing and costs nothing.
224
+ */
225
+ protected getProviderIssuesEtagFields(session: ProviderAuthenticationSession, coordinates: readonly {
226
+ owner: string;
227
+ repo: string;
228
+ number: number;
229
+ project?: string;
230
+ }[], _options: {
231
+ etagIncludes?: readonly IssueEtagInclude[];
232
+ }, _cancellation?: AbortSignal): Promise<PromiseSettledResult<IssueEtagFields | undefined>[] | undefined>;
233
+ /**
234
+ * The cheap check behind the batch pull request read's etags: still one request per target, as Azure DevOps has
235
+ * no read of several pull requests by id, but only the one {@link getProviderPullRequestsBatch} sends first —
236
+ * not the repository read provider-apis adds to it for clone URLs, once per pull request. Settled per target,
237
+ * with absence proven by the same not-found rule as the full read.
238
+ *
239
+ * Azure DevOps reports no update time, so a full row's `updatedDate` is its close time, else its creation time,
240
+ * and moves only when it closes. Its etag therefore adds a `revision` of the fields that change without it —
241
+ * title, description, target branch, and every reviewer and their vote — read off the same response at no extra
242
+ * cost. It sees a pull request's state, draft flag, head commit and those fields, and, when included, its
243
+ * mergeability and its required reviewers' review decision; never a comment or a label, which its full row
244
+ * doesn't carry either. A full row carries no check rollup, so the `checks` include adds nothing and costs nothing.
245
+ */
246
+ protected getProviderPullRequestsEtagFields(session: ProviderAuthenticationSession, coordinates: readonly {
247
+ owner: string;
248
+ repo: string;
249
+ number: number;
250
+ project?: string;
251
+ }[], options: {
252
+ etagIncludes?: readonly PullRequestEtagInclude[];
253
+ }, _cancellation?: AbortSignal): Promise<PromiseSettledResult<PullRequestEtagFields | undefined>[] | undefined>;
164
254
  /**
165
255
  * Two steps, because provider-apis has no source-branch filter: a direct Azure read finds each branch's matching
166
256
  * pull request ids, one request per target, then {@link getProviderPullRequestsBatch} resolves them — so a row is
@@ -201,6 +291,13 @@ export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends
201
291
  cursor?: string;
202
292
  filters?: PullRequestFilter[];
203
293
  }): Promise<ProviderApiPagedResult<ProviderPullRequest> | undefined>;
294
+ /**
295
+ * Per organization (keyed by its lowercased name), every group the user is in (see {@link getReviewerGroupIds}),
296
+ * and their union. An organization whose groups can't be read gets none and is recorded in `failures`, with the
297
+ * read reported `incomplete`: what only its groups review is missing, while what names the user is still read.
298
+ * Organizations whose user could not be resolved are left out; the read already reports them.
299
+ */
300
+ private getReviewerGroupsByOrganization;
204
301
  protected searchProviderPullRequests(session: ProviderAuthenticationSession, searchQuery: string, repos?: AzureRepositoryDescriptor[], cancellation?: AbortSignal, options?: SearchPullRequestsOptions): Promise<PullRequest[] | undefined>;
205
302
  protected searchProviderMyIssues(session: ProviderAuthenticationSession, repos?: AzureRepositoryDescriptor[]): Promise<IssueShape[] | undefined>;
206
303
  /**
@@ -219,7 +316,7 @@ export declare abstract class AzureDevOpsIntegrationBase<TIntegrationId extends
219
316
  protected searchProviderMyIssuesWithTruncation(session: ProviderAuthenticationSession, _resources?: ResourceDescriptor[], _cancellation?: AbortSignal, searchOptions?: SearchMyIssuesOptions): Promise<AccountWideIssuesResult | undefined>;
220
317
  protected providerOnConnect(): Promise<void>;
221
318
  protected providerOnDisconnect(): void;
222
- protected fromAzureProviderPullRequest(azurePullRequest: ProviderPullRequest, repoDescriptors: AzureRemoteRepositoryDescriptor[], projectDescriptors: AzureProjectDescriptor[]): PullRequest;
319
+ protected fromAzureProviderPullRequest(azurePullRequest: ProviderPullRequest, repoDescriptors: AzureRemoteRepositoryDescriptor[], projectDescriptors: AzureProjectDescriptor[], projection: PullRequestProjection): PullRequest;
223
320
  }
224
321
  export declare class AzureDevOpsIntegration extends AzureDevOpsIntegrationBase<GitCloudHostIntegrationId.AzureDevOps> {
225
322
  readonly authProvider: IntegrationAuthenticationProviderDescriptor;